Package org.geoserver.python.app

Source Code of org.geoserver.python.app.PythonAppResource$WSGIResponse

package org.geoserver.python.app;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.logging.Logger;

import org.geoserver.python.Python;
import org.geoserver.rest.RestletException;
import org.geotools.util.logging.Logging;
import org.python.core.Py;
import org.python.core.PyDictionary;
import org.python.core.PyFunction;
import org.python.core.PyIterator;
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyStringMap;
import org.python.core.PyTuple;
import org.python.util.PythonInterpreter;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.resource.OutputRepresentation;
import org.restlet.resource.Resource;

/**
* Python resource that provides a WSGI environments for scripts to run in.
*
* @author Justin Deoliveira, OpenGeo
*
*/
public class PythonAppResource extends Resource {

    static Logger LOGGER = Logging.getLogger("org.geoserver.python");
   
    Python python;
    File appFile;
   
    static ThreadLocal<WSGIResponse> response = new ThreadLocal<WSGIResponse>();

    public PythonAppResource(Python python, File appFile, Request request, Response response) {
        super(null, request, response);
        this.python = python;
        this.appFile = appFile;
    }

    /**
     * Creates the environment object.
     * <pre>
    * REQUEST_METHOD
    *       The HTTP request method, such as "GET" or "POST". This cannot ever be
    *       an empty string, and so is always required.
    * SCRIPT_NAME
    *       The initial portion of the request URL's "path" that corresponds to the
    *       application object, so that the application knows its virtual "location"
    *       . This may be an empty string, if the application corresponds to the
    *       "root" of the server.
    * PATH_INFO
    *       The remainder of the request URL's "path", designating the virtual
    *       "location" of the request's target within the application. This may be
    *        an empty string, if the request URL targets the application root and
    *       does not have a trailing slash.
    * QUERY_STRING
    *       The portion of the request URL that follows the "?", if any. May be
    *       empty or absent.
    * CONTENT_TYPE
    *       The contents of any Content-Type fields in the HTTP request. May be
    *       empty or absent.
    * CONTENT_LENGTH
    *       The contents of any Content-Length fields in the HTTP request. May be
    *       empty or absent.
    * SERVER_NAME, SERVER_PORT
    *       When combined with SCRIPT_NAME and PATH_INFO, these variables can be
    *       used to complete the URL. Note, however, that HTTP_HOST, if present,
    *       should be used in preference to SERVER_NAME for reconstructing the
    *       request URL. See the URL Reconstruction section below for more detail.
    *       SERVER_NAME and SERVER_PORT can never be empty strings, and so are
    *       always required.
    * SERVER_PROTOCOL
    *       The version of the protocol the client used to send the request.
    *       Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may
    *       be used by the application to determine how to treat any HTTP request
    *       headers. (This variable should probably be called REQUEST_PROTOCOL,
    *       since it denotes the protocol used in the request, and is not
    *       necessarily the protocol that will be used in the server's response.
    *       However, for compatibility with CGI we have to keep the existing name.)
    * HTTP_ Variables
    *       Variables corresponding to the client-supplied HTTP request headers
    *       (i.e., variables whose names begin with "HTTP_"). The presence or
    *       absence of these variables should correspond with the presence or
    *       absence of the appropriate HTTP header in the request.
    *
     * </pre>
     * @param request
     * @return
     */
    PyObject createEnviron(Request request) {
       
        PyDictionary environ = new PyDictionary();
       
        environ.put("REQUEST_METHOD", request.getMethod().toString());
       
        Reference ref = request.getResourceRef();
        environ.put("SCRIPT_NAME", ref.getLastSegment());
       
        Reference pref = ref.getParentRef();
        environ.put("PATH_INFO", pref.toString().substring(
            request.getRootRef().toString().length(), pref.toString().length()-1 ));
       
        environ.put("QUERY_STRING", request.getResourceRef().getQuery());

        //TODO: fill in rest of parameters
        return environ;
    }
   
    public static Object start_response(PyObject[] objs, String[] values) {
        PyString status = (PyString) objs[0];
        int space = status.toString().indexOf(' ');
       
        WSGIResponse r = response.get();
        if (space != -1) {
            r.statusCode = status.toString().substring(0, space);
            r.statusMessage = status.toString().substring(space+1);
        }
        else {
            r.statusCode = status.toString();
        }
       
        if (objs.length > 1) {
            PyList headers = (PyList) objs[1];
            for (Iterator i = headers.iterator(); i.hasNext();) {
                PyTuple tup = (PyTuple) i.next();
                r.headers.put(tup.get(0).toString(), tup.get(1).toString());
            }
        }
        return null;
    }
   
    PyFunction createStartResponse() {
        return new PyFunction(new PyStringMap(), new PyObject[]{}, Py.newJavaCode(getClass(), "start_response"));
    }
   
    @Override
    public void handleGet() {
        PythonInterpreter pi = python.interpreter();
        pi.execfile(appFile.getAbsolutePath());
       
        PyObject app = pi.get("app");
        if (app == null) {
            throw new RestletException("'app' function not found", Status.SERVER_ERROR_INTERNAL);
        }
        if (!(app instanceof PyFunction)) {
            throw new RestletException("'app' must be a function", Status.SERVER_ERROR_INTERNAL);
        }
       
        PyFunction appf = (PyFunction) app;
        PyFunction start_response = createStartResponse();
       
        WSGIResponse wr = new WSGIResponse();
        response.set(wr);
       
        PyObject ret = appf.__call__(new PyObject[]{createEnviron(getRequest()), start_response });
        if (ret != null) {
           
            String contentType = wr.headers.get("Content-type");
            if (contentType == null) {
                contentType = "text/plain";
            }
           
            MediaType mediaType = new MediaType(contentType);
           
            if (ret instanceof PyString) {
                getResponse().setEntity(ret.toString(), mediaType);
            }
            else if (ret instanceof PyList) {
                final PyList list = (PyList) ret;
                getResponse().setEntity(new OutputRepresentation(mediaType) {
                   
                    @Override
                    public void write(OutputStream outputStream) throws IOException {
                        for (Iterator i = list.iterator(); i.hasNext();) {
                            outputStream.write(i.next().toString().getBytes());
                            outputStream.write('\n');
                        }
                    }
                });
            }
            else if (ret instanceof PyIterator) {
                final PyIterator iter = (PyIterator) ret;
                getResponse().setEntity(new OutputRepresentation(mediaType) {
                   
                    @Override
                    public void write(OutputStream outputStream) throws IOException {
                        for (Iterator i = iter.iterator(); i.hasNext();) {
                            outputStream.write(i.next().toString().getBytes());
                            outputStream.write('\n');
                        }
                    }
                });
            }
            else {
                LOGGER.warning( "Unsure how to handle " + ret + ". Resorting to outputing string " +
                    "representation.");
                getResponse().setEntity(ret.toString(), mediaType);
            }
        }
       
        response.remove();
    }
   
    static class WSGIResponse {
       
        String statusCode;
        String statusMessage;
        TreeMap<String,String> headers = new TreeMap<String, String>();
    }
}
TOP

Related Classes of org.geoserver.python.app.PythonAppResource$WSGIResponse

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.