src.com.xhaus.modjy.ModjyJServlet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython Show documentation
Show all versions of jython Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
/*###
#
# Copyright Alan Kennedy.
#
# You may contact the copyright holder at this uri:
#
# http://www.xhaus.com/contact/modjy
#
# The licence under which this code is released is the Apache License v2.0.
#
# The terms and conditions of this license are listed in a file contained
# in the distribution that also contained this file, under the name
# LICENSE.txt.
#
# You may also read a copy of the license at the following web address.
#
# http://modjy.xhaus.com/LICENSE.txt
#
###*/
package com.xhaus.modjy;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Enumeration;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.python.core.Options;
import org.python.core.Py;
import org.python.core.PyException;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PySystemState;
import org.python.core.PyType;
import org.python.util.PythonInterpreter;
public class ModjyJServlet extends HttpServlet {
protected final static String MODJY_PYTHON_CLASSNAME = "modjy_servlet";
protected final static String LIB_PYTHON = "/WEB-INF/lib-python";
protected final static String PTH_FILE_EXTENSION = ".pth";
protected final static String LOAD_SITE_PACKAGES_PARAM = "load_site_packages";
protected final static String PYTHON_HOME_PARAM = "python.home";
protected PythonInterpreter interp;
protected HttpServlet modjyServlet;
/**
* Read configuration
* 1. Both context and servlet parameters are included in the set, so that
* the definition of some parameters (e.g python.*) can be shared between multiple WSGI
* servlets.
* 2. servlet params take precedence over context parameters
*/
protected Properties readConfiguration() {
Properties props = new Properties();
// Context parameters
ServletContext context = getServletContext();
Enumeration> e = context.getInitParameterNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
props.put(name, context.getInitParameter(name));
}
// Servlet parameters override context parameters
e = getInitParameterNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
props.put(name, getInitParameter(name));
}
// Check if python home is relative, in which case find it in the servlet context
String pythonHomeString = props.getProperty(PYTHON_HOME_PARAM);
if (pythonHomeString != null) {
File pythonHome = new File(pythonHomeString);
if (!pythonHome.isAbsolute())
pythonHomeString = context.getRealPath(pythonHomeString);
props.setProperty(PYTHON_HOME_PARAM, pythonHomeString);
}
return props;
}
/**
* Initialise the modjy servlet.
* 1. Read the configuration
* 2. Initialise the jython runtime
* 3. Setup, in relation to the J2EE servlet environment
* 4. Create the jython-implemented servlet
* 5. Initialise the jython-implemented servlet
*/
@Override
public void init() throws ServletException {
try {
Properties props = readConfiguration();
// We check for site packages settings before initialising the runtime
// https://hg.python.org/jython/rev/51b28cc2c43d
checkSitePackages(props);
PythonInterpreter.initialize(System.getProperties(), props, new String[0]);
PySystemState systemState = new PySystemState();
interp = new PythonInterpreter(null, systemState);
setupEnvironment(interp, props, systemState);
try {
interp.exec("from modjy.modjy import " + MODJY_PYTHON_CLASSNAME);
} catch (PyException ix) {
throw new ServletException("Unable to import '" + MODJY_PYTHON_CLASSNAME
+ "': maybe you need to set the '" + PYTHON_HOME_PARAM + "' parameter?", ix);
}
PyObject pyServlet = ((PyType)interp.get(MODJY_PYTHON_CLASSNAME)).__call__();
Object temp = pyServlet.__tojava__(HttpServlet.class);
if (temp == Py.NoConversion)
throw new ServletException("Corrupted modjy file: cannot find definition of '"
+ MODJY_PYTHON_CLASSNAME + "' class");
modjyServlet = (HttpServlet)temp;
modjyServlet.init(this);
} catch (PyException pyx) {
throw new ServletException("Exception creating modjy servlet: " + pyx.toString(), pyx);
}
}
/**
* Actually service the incoming request. Simply delegate to the jython servlet.
*
* @param req
* - The incoming HttpServletRequest
* @param resp
* - The outgoing HttpServletResponse
*/
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
modjyServlet.service(req, resp);
}
/**
* Close down the modjy servlet.
*
*/
@Override
public void destroy( ) {
interp.cleanup();
}
/**
* Setup the modjy environment, i.e.
* 1. Find the location of the modjy.jar file and add it to sys.path
* 2. Process the WEB-INF/lib-python directory, if it exists
*
* @param interp
* - The PythonInterpreter used to service requests
* @param props
* - The properties from which config options are found
* @param systemState
* - The PySystemState corresponding to the interpreter servicing requests
*/
protected void setupEnvironment(PythonInterpreter interp,
Properties props,
PySystemState systemState) throws PyException {
processPythonLib(interp, systemState);
}
/**
* Check if the user has requested to initialise the jython installation "site-packages".
* The value defaults to true, i.e. load site packages
*
* @param props
* - The properties from which config options are found
*/
protected void checkSitePackages(Properties props) throws PyException {
boolean loadSitePackages = true;
String loadSitePackagesParam = props.getProperty(LOAD_SITE_PACKAGES_PARAM);
if (loadSitePackagesParam != null && loadSitePackagesParam.trim().compareTo("0") == 0) {
loadSitePackages = false;
}
Options.importSite = loadSitePackages;
}
/**
* Do all processing in relation to the lib-python subdirectory of WEB-INF
*
* @param interp
* - The PythonInterpreter used to service requests
* @param systemState
* - The PySystemState whose path should be updated
*/
protected void processPythonLib(PythonInterpreter interp, PySystemState systemState) {
// Add the lib-python directory to sys.path
String pythonLibPath = getServletContext().getRealPath(LIB_PYTHON);
if (pythonLibPath == null) {
return;
}
File pythonLib = new File(pythonLibPath);
if (!pythonLib.exists()) {
return;
}
systemState.path.append(new PyString(pythonLibPath));
// Now check for .pth files in lib-python and process each one
String[] libPythonContents = pythonLib.list();
for (String libPythonContent : libPythonContents) {
if (libPythonContent.endsWith(PTH_FILE_EXTENSION)) {
processPthFile(interp, systemState, pythonLibPath, libPythonContent);
}
}
}
/**
* Process an individual file .pth file in the lib-python directory
*
* @param interp
* - The PythonInterpreter which will execute imports
* @param systemState
* - The PySystemState whose path should be updated
* @param pythonLibPath
* - The actual path to the lib-python directory
* @param pthFilename
* - The PySystemState whose path should be updated
*/
protected void processPthFile(PythonInterpreter interp,
PySystemState systemState,
String pythonLibPath,
String pthFilename) {
try {
LineNumberReader lineReader = new LineNumberReader(new FileReader(new File(pythonLibPath,
pthFilename)));
String line;
while ((line = lineReader.readLine()) != null) {
line = line.trim();
if (line.length() == 0) {
continue;
}
if (line.startsWith("#")) {
continue;
}
if (line.startsWith("import")) {
interp.exec(line);
continue;
}
File archiveFile = new File(pythonLibPath, line);
String archiveRealpath = archiveFile.getAbsolutePath();
systemState.path.append(new PyString(archiveRealpath));
}
} catch (IOException iox) {
System.err.println("IOException: " + iox.toString());
}
}
}