WebStack

Annotated tools/JavaServlet/classes/uk/org/boddie/webstack/util/PyServlet.java

528:2e1f060e873b
2005-11-26 paulb [project @ 2005-11-26 01:15:51 by paulb] Fixed get_processed_virtual_path_info.
paulb@215 1
package uk.org.boddie.webstack.util;
paulb@215 2
paulb@215 3
import java.io.*;
paulb@215 4
import java.util.*;
paulb@215 5
import javax.servlet.*;
paulb@215 6
import javax.servlet.http.*;
paulb@215 7
import org.python.core.*;
paulb@215 8
import org.python.util.PythonInterpreter;
paulb@215 9
paulb@215 10
/**
paulb@215 11
 * This servlet is used to re-serve JPython servlets.  It stores
paulb@215 12
 * bytecode for JPython servlets and re-uses it if the underlying .py
paulb@215 13
 * file has not changed.
paulb@215 14
 * <p>
paulb@215 15
 * Many people have been involved with this class:
paulb@215 16
 * <ul>
paulb@215 17
 * <li>Chris Gokey
paulb@215 18
 * <li>David Syer
paulb@215 19
 * <li>Finn Bock
paulb@215 20
 * </ul>
paulb@215 21
 * If somebody is missing from this list, let us know.
paulb@215 22
 * <p>
paulb@215 23
 *
paulb@215 24
 * e.g. http://localhost:8080/test/hello.py
paulb@215 25
 * <pre>
paulb@215 26
 *
paulb@215 27
 * from javax.servlet.http import HttpServlet
paulb@215 28
 * class hello(HttpServlet):
paulb@215 29
 *     def doGet(self, req, res):
paulb@215 30
 *         res.setContentType("text/html");
paulb@215 31
 *         out = res.getOutputStream()
paulb@215 32
 *         print >>out, "<html>"
paulb@215 33
 *         print >>out, "<head><title>Hello World, How are we?</title></head>"
paulb@215 34
 *         print >>out, "<body>Hello World, how are we?"
paulb@215 35
 *         print >>out, "</body>"
paulb@215 36
 *         print >>out, "</html>"
paulb@215 37
 *         out.close()
paulb@215 38
 *         return
paulb@215 39
 * </pre>
paulb@215 40
 *
paulb@215 41
 * in web.xml for the PyServlet context:
paulb@215 42
 * <pre>
paulb@215 43
 * &lt;web-app>
paulb@215 44
 *     &lt;servlet>
paulb@215 45
 *         &lt;servlet-name>PyServlet&lt;/servlet-name>
paulb@215 46
 *         &lt;servlet-class>uk.org.boddie.webstack.util.PyServlet&lt;/servlet-class>
paulb@215 47
 *         &lt;init-param>
paulb@215 48
 *             &lt;param-name>python.home&lt;/param-name>
paulb@215 49
 *             &lt;param-value>/usr/home/jython-2.1&lt;/param-value>
paulb@215 50
 *         &lt;/init-param>
paulb@215 51
 *         &lt;init-param>
paulb@215 52
 *             &lt;param-name>servlet.file&lt;/param-name>
paulb@215 53
 *             &lt;param-value>hello.py&lt;/param-value>
paulb@215 54
 *         &lt;/init-param>
paulb@215 55
 *     &lt;/servlet>
paulb@215 56
 *     &lt;servlet-mapping>
paulb@215 57
 *         &lt;servlet-name>PyServlet&lt;/servlet-name>
paulb@215 58
 *         &lt;url-pattern>/*&lt;/url-pattern>
paulb@215 59
 *     &lt;/servlet-mapping>
paulb@215 60
 * &lt;/web-app>
paulb@215 61
 *
paulb@215 62
 * </pre>
paulb@215 63
 */
paulb@215 64
public class PyServlet extends HttpServlet {
paulb@215 65
    private PythonInterpreter interp;
paulb@215 66
    private Hashtable cache = new Hashtable();
paulb@215 67
    private String rootPath;
paulb@215 68
    private String servletFile = null;
paulb@215 69
paulb@215 70
    public void init() {
paulb@215 71
        rootPath = getServletContext().getRealPath("/");
paulb@215 72
        if (!rootPath.endsWith(File.separator))
paulb@215 73
            rootPath += File.separator;
paulb@215 74
paulb@215 75
        Properties props = new Properties();
paulb@215 76
paulb@215 77
        // Context parameters
paulb@215 78
        ServletContext context = getServletContext();
paulb@215 79
        Enumeration e = context.getInitParameterNames();
paulb@215 80
        while (e.hasMoreElements()) {
paulb@215 81
            String name = (String) e.nextElement();
paulb@215 82
            props.put(name, context.getInitParameter(name));
paulb@215 83
        }
paulb@215 84
paulb@215 85
        // Config parameters
paulb@215 86
        e = getInitParameterNames();
paulb@215 87
        while (e.hasMoreElements()) {
paulb@215 88
            String name = (String) e.nextElement();
paulb@215 89
            props.put(name, getInitParameter(name));
paulb@215 90
        }
paulb@215 91
paulb@215 92
        if (props.getProperty("python.home") == null &&
paulb@215 93
                                System.getProperty("python.home") == null) {
paulb@215 94
            props.put("python.home", rootPath + "WEB-INF" +
paulb@215 95
                                             File.separator + "lib");
paulb@215 96
        }
paulb@215 97
paulb@215 98
        // Define the servlet file.
paulb@215 99
        servletFile = props.getProperty("servlet.file");
paulb@215 100
paulb@215 101
        PythonInterpreter.initialize(System.getProperties(), props,
paulb@215 102
                                     new String[0]);
paulb@215 103
        reset();
paulb@215 104
paulb@215 105
        PySystemState sys = Py.getSystemState();
paulb@215 106
        sys.add_package("javax.servlet");
paulb@215 107
        sys.add_package("javax.servlet.http");
paulb@215 108
        sys.add_package("javax.servlet.jsp");
paulb@215 109
        sys.add_package("javax.servlet.jsp.tagext");
paulb@215 110
paulb@215 111
        sys.add_classdir(rootPath + "WEB-INF" +
paulb@215 112
                          File.separator + "classes");
paulb@215 113
paulb@215 114
        sys.add_extdir(rootPath + "WEB-INF" + File.separator + "lib", true);
paulb@215 115
    }
paulb@215 116
paulb@215 117
    /**
paulb@215 118
     * Implementation of the HttpServlet main method.
paulb@215 119
     * @param req the request parameter.
paulb@215 120
     * @param res the response parameter.
paulb@215 121
     * @exception ServletException
paulb@215 122
     * @exception IOException
paulb@215 123
     */
paulb@215 124
    public void service(ServletRequest req, ServletResponse res)
paulb@215 125
        throws ServletException, IOException
paulb@215 126
    {
paulb@215 127
        req.setAttribute("pyservlet", this);
paulb@215 128
paulb@215 129
        String spath = (String)req.getAttribute(
paulb@215 130
                                    "javax.servlet.include.servlet_path");
paulb@215 131
        if (spath == null) {
paulb@215 132
            spath = ((HttpServletRequest) req).getServletPath();
paulb@215 133
            if (spath == null || spath.length() == 0) {
paulb@215 134
                // Servlet 2.1 puts the path of an extension-matched
paulb@215 135
                // servlet in PathInfo.
paulb@215 136
                spath = ((HttpServletRequest) req).getPathInfo();
paulb@215 137
            }
paulb@215 138
        }
paulb@215 139
        String rpath = getServletContext().getRealPath(spath);
paulb@215 140
paulb@215 141
        interp.set("__file__", rpath);
paulb@215 142
paulb@215 143
        // Instead of using the URL to find the Jython servlet, use a supplied
paulb@215 144
        // property instead.
paulb@215 145
paulb@215 146
        if (servletFile == null)
paulb@215 147
            throw new ServletException("No servlet.file specified in configuration.");
paulb@215 148
paulb@215 149
        HttpServlet servlet = getServlet(rootPath + servletFile);
paulb@215 150
        if (servlet !=  null)
paulb@215 151
            servlet.service(req, res);
paulb@215 152
        else
paulb@215 153
            throw new ServletException("No python servlet found for: " + rpath);
paulb@215 154
    }
paulb@215 155
paulb@215 156
    public void reset() {
paulb@215 157
        destroyCache();
paulb@215 158
        interp = new PythonInterpreter(null, new PySystemState());
paulb@215 159
        cache.clear();
paulb@215 160
        PySystemState sys = Py.getSystemState();
paulb@215 161
        sys.path.append(new PyString(rootPath));
paulb@215 162
paulb@215 163
        String modulesDir = rootPath + "WEB-INF" +
paulb@215 164
                            File.separator + "jython";
paulb@215 165
        sys.path.append(new PyString(modulesDir));
paulb@215 166
    }
paulb@215 167
paulb@215 168
    private synchronized HttpServlet getServlet(String path)
paulb@215 169
        throws ServletException, IOException
paulb@215 170
    {
paulb@215 171
        CacheEntry entry = (CacheEntry) cache.get(path);
paulb@215 172
        if (entry == null)
paulb@215 173
            return loadServlet(path);
paulb@215 174
        File file = new File(path);
paulb@215 175
        if (file.lastModified() > entry.date)
paulb@215 176
            return loadServlet(path);
paulb@215 177
        return entry.servlet;
paulb@215 178
    }
paulb@215 179
paulb@215 180
    private HttpServlet loadServlet(String path)
paulb@215 181
        throws ServletException, IOException
paulb@215 182
    {
paulb@215 183
        HttpServlet servlet = null;
paulb@215 184
        File file = new File(path);
paulb@215 185
paulb@215 186
        // Extract servlet name from path (strip ".../" and ".py")
paulb@215 187
        int start = path.lastIndexOf(File.separator);
paulb@215 188
        if (start < 0)
paulb@215 189
            start = 0;
paulb@215 190
        else
paulb@215 191
            start++;
paulb@215 192
        int end = path.lastIndexOf('.');
paulb@215 193
        if ((end < 0) || (end <= start))
paulb@215 194
            end = path.length();
paulb@215 195
        String name = path.substring(start, end);
paulb@215 196
paulb@215 197
        try {
paulb@215 198
            interp.execfile(path);
paulb@215 199
            PyObject cls = interp.get(name);
paulb@215 200
            if (cls == null)
paulb@215 201
                throw new ServletException("No callable (class or function) "+
paulb@215 202
                                       "named " + name + " in " + path);
paulb@215 203
paulb@215 204
            PyObject pyServlet = cls.__call__();
paulb@215 205
            Object o = pyServlet.__tojava__(HttpServlet.class);
paulb@215 206
            if (o == Py.NoConversion)
paulb@215 207
                throw new ServletException("The value from " + name +
paulb@215 208
                                       "must extend HttpServlet");
paulb@215 209
            servlet = (HttpServlet)o;
paulb@215 210
            servlet.init(getServletConfig());
paulb@215 211
paulb@215 212
        } catch (PyException e) {
paulb@215 213
            throw new ServletException("Could not create "+
paulb@215 214
                                       "Jython servlet" + e.toString());
paulb@215 215
        }
paulb@215 216
        CacheEntry entry = new CacheEntry(servlet, file.lastModified());
paulb@215 217
        cache.put(path, entry);
paulb@215 218
        return servlet;
paulb@215 219
    }
paulb@215 220
paulb@215 221
    public void destroy() {
paulb@215 222
        destroyCache();
paulb@215 223
    }
paulb@215 224
paulb@215 225
    private void destroyCache() {
paulb@215 226
        for (Enumeration e = cache.elements(); e.hasMoreElements(); ) {
paulb@215 227
            CacheEntry entry = (CacheEntry) e.nextElement();
paulb@215 228
            entry.servlet.destroy();
paulb@215 229
        }
paulb@215 230
    }
paulb@215 231
paulb@215 232
}
paulb@215 233
paulb@215 234
class CacheEntry {
paulb@215 235
    public long date;
paulb@215 236
    public HttpServlet servlet;
paulb@215 237
paulb@215 238
    CacheEntry(HttpServlet servlet, long date) {
paulb@215 239
        this.servlet = servlet;
paulb@215 240
        this.date = date;
paulb@215 241
    }
paulb@215 242
}