I spent several sleepless nights trying to figure out why Jython 2.7beta3 suddenly refused to work in Google App Engine while Jython 2.7 beta2 worked nicely.
It crashes while initializing site.py standard package because Google App Engine blocks any attempt to use ProcessBuilder.
com.jythonui.client.service.JythonService.runAction(com.jythonui.shared.RequestContext,com.jythonui.shared.DialogVariables,java.lang.String,java.lang.String)' threw an unexpected exception: Traceback (most recent call last): File "/base/data/home/apps/s~testjavahotel/5.380117830403911812/WEB-INF/lib/jython-standalone-2.7-b3.jar/Lib/site.py", line 571, inThis exception (raised in Development and Production mode) can be resolved easy by setting Options.no_user_site = true; before starting the Jython interpreter.File "/base/data/home/apps/s~testjavahotel/5.380117830403911812/WEB-INF/lib/jython-standalone-2.7-b3.jar/Lib/site.py", line 552, in main File "/base/data/home/apps/s~testjavahotel/5.380117830403911812/WEB-INF/lib/jython-standalone-2.7-b3.jar/Lib/site.py", line 231, in check_enableusersite at jnr.posix.JavaPOSIX.geteuid(JavaPOSIX.java:102) at jnr.posix.LazyPOSIX.geteuid(LazyPOSIX.java:115) at org.python.modules.posix.PosixModule.geteuid(PosixModule.java:343) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:45) java.lang.NoClassDefFoundError: java.lang.NoClassDefFoundError: Could not initialize class jnr.posix.JavaPOSIX$LoginInfo at com.google.gwt.user.server.rpc.RPC.encodeResponseForFailure(RPC.java:389) at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:579) at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:265) at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:305) at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62) at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
But then came up the second one which unveils only in Production mode (works in Development mode).
javax.servlet.ServletContext log: Exception while dispatching incoming RPC call com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract com.jythonui.shared.DialogVariables com.jythonui.client.service.JythonService.runAction(com.jythonui.shared.RequestContext,com.jythonui.shared.DialogVariables,java.lang.String,java.lang.String)' threw an unexpected exception: Traceback (most recent call last): File "__pyclasspath__/site$py.class", line 571, inAttribute sys.prefix is None for some mysterious reason. Because debugging in Production mode is impossible the only way was to compile Jython from sources (which is not trivial for the first time), add logging messages and trying to encircle the bug. Finally I realized that in Google App Engine Production mode system property "java.class.path" is null. So the codeFile "__pyclasspath__/site$py.class", line 553, in main File "__pyclasspath__/site$py.class", line 286, in addusersitepackages File "__pyclasspath__/site$py.class", line 261, in getusersitepackages File "__pyclasspath__/site$py.class", line 250, in getuserbase File "__pyclasspath__/sysconfig$py.class", line 112, in File "__pyclasspath__/posixpath$py.class", line 391, in normpath AttributeError: 'NoneType' object has no attribute 'startswith' at com.google.gwt.user.server.rpc.RPC.encodeResponseForFailure(RPC.java:389) at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:579) at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteService
if (root == null) { String classpath = preProperties.getProperty("java.class.path"); ll.warning("classpath=" + classpath + "!"); if (classpath != null) { String lowerCaseClasspath = classpath.toLowerCase(); int jarIndex = lowerCaseClasspath.indexOf(JYTHON_JAR); if (jarIndex < 0) { jarIndex = lowerCaseClasspath.indexOf(JYTHON_DEV_JAR); } if (jarIndex >= 0) { int start = classpath.lastIndexOf(File.pathSeparator, jarIndex) + 1; root = classpath.substring(start, jarIndex); } else if (jarFileName != null) { // in case JYTHON_JAR is referenced from a MANIFEST inside another jar on the // classpath root = new File(jarFileName).getParent(); } } } if (root == null) { return null; }does not find the root path for Jython libraries. There is a bug in this code because clause:
} else if (jarFileName != null) { // in case JYTHON_JAR is referenced from a MANIFEST inside another jar on the // classpath root = new File(jarFileName).getParent(); } }should be placed outside if classpath != null clause (not inside) and root directory cannot be extracted from Jython jar file path as well.
Solution
But finally I found the solution and it was simple as usual.
protected PythonInterpreter(PyObject dict, PySystemState systemState, boolean useThreadLocalState) { if (dict == null) { dict = Py.newStringMap(); } globals = dict; if (systemState == null) systemState = Py.getSystemState(); this.systemState = systemState; setSystemState(); this.useThreadLocalState = useThreadLocalState; if (!useThreadLocalState) { PyModule module = new PyModule("__main__", dict); systemState.modules.__setitem__("__main__", module); } if (Options.importSite) { // Ensure site-packages are available imp.load("site"); }So it was enough to set Options.importSite = false; before launching PythonInterpreter and the whole stuff related to site.py (useless in Google App Engine restricted environment) is disabled.
Brak komentarzy:
Prześlij komentarz