alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Jetty example source code file (CGI.java)

This example Jetty source code file (CGI.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Jetty tags/keywords

cgi, cgi, enumeration, envlist, envlist, file, file, http, inputstream, io, ioexception, path, request, response, servlet, servletexception, string, string, systemroot, util

The Jetty CGI.java source code

//========================================================================
//Copyright 2006 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at 
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================

package org.mortbay.servlet;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mortbay.log.Log;
import org.mortbay.util.IO;
import org.mortbay.util.StringUtil;

//-----------------------------------------------------------------------------
/**
 * CGI Servlet.
 * 
 * The cgi bin directory can be set with the "cgibinResourceBase" init parameter
 * or it will default to the resource base of the context.
 * 
 * The "commandPrefix" init parameter may be used to set a prefix to all
 * commands passed to exec. This can be used on systems that need assistance to
 * execute a particular file type. For example on windows this can be set to
 * "perl" so that perl scripts are executed.
 * 
 * The "Path" init param is passed to the exec environment as PATH. Note: Must
 * be run unpacked somewhere in the filesystem.
 * 
 * Any initParameter that starts with ENV_ is used to set an environment
 * variable with the name stripped of the leading ENV_ and using the init
 * parameter value.
 * 
 * @author Julian Gosnell
 * @author Thanassis Papathanasiou - Some minor modifications for Jetty6 port
 */
public class CGI extends HttpServlet
{
    private boolean _ok;
    private File _docRoot;
    private String _path;
    private String _cmdPrefix;
    private EnvList _env;
    private boolean _ignoreExitState;

    /* ------------------------------------------------------------ */
    public void init() throws ServletException
    {
        _env=new EnvList();
        _cmdPrefix=getInitParameter("commandPrefix");

        String tmp=getInitParameter("cgibinResourceBase");
        if (tmp==null)
        {
            tmp=getInitParameter("resourceBase");
            if (tmp==null)
                tmp=getServletContext().getRealPath("/");
        }

        if (tmp==null)
        {
            Log.warn("CGI: no CGI bin !");
            return;
        }

        File dir=new File(tmp);
        if (!dir.exists())
        {
            Log.warn("CGI: CGI bin does not exist - "+dir);
            return;
        }

        if (!dir.canRead())
        {
            Log.warn("CGI: CGI bin is not readable - "+dir);
            return;
        }

        if (!dir.isDirectory())
        {
            Log.warn("CGI: CGI bin is not a directory - "+dir);
            return;
        }

        try
        {
            _docRoot=dir.getCanonicalFile();
        }
        catch (IOException e)
        {
            Log.warn("CGI: CGI bin failed - "+dir,e);
            return;
        }

        _path=getInitParameter("Path");
        if (_path!=null)
            _env.set("PATH",_path);

        _ignoreExitState="true".equalsIgnoreCase(getInitParameter("ignoreExitState"));
        Enumeration e=getInitParameterNames();
        while (e.hasMoreElements())
        {
            String n=(String)e.nextElement();
            if (n!=null&&n.startsWith("ENV_"))
                _env.set(n.substring(4),getInitParameter(n));
        }
        if(!_env.envMap.containsKey("SystemRoot"))
        {
      	    String os = System.getProperty("os.name");
            if (os!=null && os.toLowerCase().indexOf("windows")!=-1)
            {
        	String windir = System.getProperty("windir");
        	_env.set("SystemRoot", windir!=null ? windir : "C:\\WINDOWS"); 
            }
        }   
      
        _ok=true;
    }

    /* ------------------------------------------------------------ */
    public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
    {
        if (!_ok)
        {
            res.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
            return;
        }
        
        String pathInContext=StringUtil.nonNull(req.getServletPath())+StringUtil.nonNull(req.getPathInfo());

        if (Log.isDebugEnabled())
        {
            Log.debug("CGI: ContextPath : "+req.getContextPath());
            Log.debug("CGI: ServletPath : "+req.getServletPath());
            Log.debug("CGI: PathInfo    : "+req.getPathInfo());
            Log.debug("CGI: _docRoot    : "+_docRoot);
            Log.debug("CGI: _path       : "+_path);
            Log.debug("CGI: _ignoreExitState: "+_ignoreExitState);
        }

        // pathInContext may actually comprises scriptName/pathInfo...We will
        // walk backwards up it until we find the script - the rest must
        // be the pathInfo;

        String both=pathInContext;
        String first=both;
        String last="";

        File exe=new File(_docRoot,first);

        while ((first.endsWith("/")||!exe.exists())&&first.length()>=0)
        {
            int index=first.lastIndexOf('/');

            first=first.substring(0,index);
            last=both.substring(index,both.length());
            exe=new File(_docRoot,first);
        }

        if (first.length()==0||!exe.exists()||exe.isDirectory()||!exe.getCanonicalPath().equals(exe.getAbsolutePath()))
        {
            res.sendError(404);
        }
        else
        {
            if (Log.isDebugEnabled())
            {
                Log.debug("CGI: script is "+exe);
                Log.debug("CGI: pathInfo is "+last);
            }
            exec(exe,last,req,res);
        }
    }

    /* ------------------------------------------------------------ */
    /*
     * @param root @param path @param req @param res @exception IOException
     */
    private void exec(File command, String pathInfo, HttpServletRequest req, HttpServletResponse res) throws IOException
    {
        String path=command.getAbsolutePath();
        File dir=command.getParentFile();
        String scriptName=req.getRequestURI().substring(0,req.getRequestURI().length()-pathInfo.length());
        String scriptPath=getServletContext().getRealPath(scriptName);
        String pathTranslated=req.getPathTranslated();

        int len=req.getContentLength();
        if (len<0)
            len=0;
        if ((pathTranslated==null)||(pathTranslated.length()==0))
            pathTranslated=path;

        EnvList env=new EnvList(_env);
        // these ones are from "The WWW Common Gateway Interface Version 1.1"
        // look at :
        // http://Web.Golux.Com/coar/cgi/draft-coar-cgi-v11-03-clean.html#6.1.1
        env.set("AUTH_TYPE",req.getAuthType());
        env.set("CONTENT_LENGTH",Integer.toString(len));
        env.set("CONTENT_TYPE",req.getContentType());
        env.set("GATEWAY_INTERFACE","CGI/1.1");
        if ((pathInfo!=null)&&(pathInfo.length()>0))
        {
            env.set("PATH_INFO",pathInfo);
        }
        env.set("PATH_TRANSLATED",pathTranslated);
        env.set("QUERY_STRING",req.getQueryString());
        env.set("REMOTE_ADDR",req.getRemoteAddr());
        env.set("REMOTE_HOST",req.getRemoteHost());
        // The identity information reported about the connection by a
        // RFC 1413 [11] request to the remote agent, if
        // available. Servers MAY choose not to support this feature, or
        // not to request the data for efficiency reasons.
        // "REMOTE_IDENT" => "NYI"
        env.set("REMOTE_USER",req.getRemoteUser());
        env.set("REQUEST_METHOD",req.getMethod());
        env.set("SCRIPT_NAME",scriptName);
        env.set("SCRIPT_FILENAME",scriptPath);
        env.set("SERVER_NAME",req.getServerName());
        env.set("SERVER_PORT",Integer.toString(req.getServerPort()));
        env.set("SERVER_PROTOCOL",req.getProtocol());
        env.set("SERVER_SOFTWARE",getServletContext().getServerInfo());

        Enumeration enm=req.getHeaderNames();
        while (enm.hasMoreElements())
        {
            String name=(String)enm.nextElement();
            String value=req.getHeader(name);
            env.set("HTTP_"+name.toUpperCase().replace('-','_'),value);
        }

        // these extra ones were from printenv on www.dev.nomura.co.uk
        env.set("HTTPS",(req.isSecure()?"ON":"OFF"));
        // "DOCUMENT_ROOT" => root + "/docs",
        // "SERVER_URL" => "NYI - http://us0245",
        // "TZ" => System.getProperty("user.timezone"),

        // are we meant to decode args here ? or does the script get them
        // via PATH_INFO ? if we are, they should be decoded and passed
        // into exec here...
        String execCmd=path;
        if ((execCmd.charAt(0)!='"')&&(execCmd.indexOf(" ")>=0))
            execCmd="\""+execCmd+"\"";
        if (_cmdPrefix!=null)
            execCmd=_cmdPrefix+" "+execCmd;

        Process p=(dir==null)?Runtime.getRuntime().exec(execCmd,env.getEnvArray()):Runtime.getRuntime().exec(execCmd,env.getEnvArray(),dir);

        // hook processes input to browser's output (async)
        final InputStream inFromReq=req.getInputStream();
        final OutputStream outToCgi=p.getOutputStream();
        final int inLength=len;

        IO.copyThread(p.getErrorStream(),System.err);
        
        new Thread(new Runnable()
        {
            public void run()
            {
                try
                {
                    if (inLength>0)
                        IO.copy(inFromReq,outToCgi,inLength);
                    outToCgi.close();
                }
                catch (IOException e)
                {
                    Log.ignore(e);
                }
            }
        }).start();

        // hook processes output to browser's input (sync)
        // if browser closes stream, we should detect it and kill process...
        OutputStream os = null;
        try
        {
            // read any headers off the top of our input stream
            // NOTE: Multiline header items not supported!
            String line=null;
            InputStream inFromCgi=p.getInputStream();

            //br=new BufferedReader(new InputStreamReader(inFromCgi));
            //while ((line=br.readLine())!=null)
            while( (line = getTextLineFromStream( inFromCgi )).length() > 0 )
            {
                if (!line.startsWith("HTTP"))
                {
                    int k=line.indexOf(':');
                    if (k>0)
                    {
                        String key=line.substring(0,k).trim();
                        String value = line.substring(k+1).trim();
                        if ("Location".equals(key))
                        {
                            res.sendRedirect(value);
                        }
                        else if ("Status".equals(key))
                        {
                        	String[] token = value.split( " " );
                            int status=Integer.parseInt(token[0]);
                            res.setStatus(status);
                        }
                        else
                        {
                            // add remaining header items to our response header
                            res.addHeader(key,value);
                        }
                    }
                }
            }
            // copy cgi content to response stream...
            os = res.getOutputStream();
            IO.copy(inFromCgi, os);
            p.waitFor();

            if (!_ignoreExitState)
            {
                int exitValue=p.exitValue();
                if (0!=exitValue)
                {
                    Log.warn("Non-zero exit status ("+exitValue+") from CGI program: "+path);
                    if (!res.isCommitted())
                        res.sendError(500,"Failed to exec CGI");
                }
            }
        }
        catch (IOException e)
        {
            // browser has probably closed its input stream - we
            // terminate and clean up...
            Log.debug("CGI: Client closed connection!");
        }
        catch (InterruptedException ie)
        {
            Log.debug("CGI: interrupted!");
        }
        finally
        {
            if( os != null )
            	os.close();
            os = null;
            p.destroy();
            // Log.debug("CGI: terminated!");
        }
    }

    /**
     * Utility method to get a line of text from the input stream.
     * @param is the input stream
     * @return the line of text
     * @throws IOException
     */
    private String getTextLineFromStream( InputStream is ) throws IOException {
        StringBuffer buffer = new StringBuffer();
        int b;

       	while( (b = is.read()) != -1 && b != (int) '\n' ) {
       		buffer.append( (char) b );
       	}
       	return buffer.toString().trim();
    }
    /* ------------------------------------------------------------ */
    /**
     * private utility class that manages the Environment passed to exec.
     */
    private static class EnvList
    {
        private Map envMap;

        EnvList()
        {
            envMap=new HashMap();
        }

        EnvList(EnvList l)
        {
            envMap=new HashMap(l.envMap);
        }

        /**
         * Set a name/value pair, null values will be treated as an empty String
         */
        public void set(String name, String value)
        {
            envMap.put(name,name+"="+StringUtil.nonNull(value));
        }

        /** Get representation suitable for passing to exec. */
        public String[] getEnvArray()
        {
            return (String[])envMap.values().toArray(new String[envMap.size()]);
        }

        public String toString()
        {
            return envMap.toString();
        }
    }
}

Other Jetty examples (source code examples)

Here is a short list of links related to this Jetty CGI.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.