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