Broken class loaders
Jynx 0.2 contained an ugly workaround for a bug I couldn’t fix for quite a while. The bug can be described as follows: suppose you defined code of a Java class `A` and compiled it dynamically:
A = JavaCompiler().createClass("A", A_source) |
When you attempt to build a subclass
class B(A): pass
a `NoClassDefFoundError` exception was raised:
Traceback (most recent call last): File "C:\lang\Jython\jcompile.py", line 185, in <module> class B(A):pass at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:621) at java.lang.ClassLoader.defineClass(ClassLoader.java:466) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) java.lang.NoClassDefFoundError: org/python/core/PyProxy (wrong name: A) |
In that case the Jython runtime failed to create a proxy class for `B` while locating `PyProxy` which is a Jython core interface. From the traceback it wasn’t clear how to locate the error and I started to debug into Jython from Netbeans.
This is what happened: Jynx defines a `ByteClassLoader` class which is custom class loader for dynamic compilation of `A`. When `A` is loaded with `loadClass` a `findClass` method is called to locate `A` and this method had to be overwritten. The `ByteClassLoader` was bound to `A` automatically and used by Jython to locate interfaces such as `org.python.core.PyProxy`. This didn’t work and explains the failure. A possible fix is to respond to classes which cannot be dealt with from `ByteClassLoader` and delegate a `findClass` call to the parent class loader.
Curiously Jython stopped using `ByteClassLoader` after I changed the inheritance hierarchy from
class ByteClassLoader(ClassLoader): def __init__(self, code): super(ByteClassLoader, self).__init__(ClassLoader.getClassLoader()) ... |
to
class ByteClassLoader(URLClassLoader): def __init__(self, code): super(ByteClassLoader, self).__init__([], ClassLoader.getSystemClassLoader()) ... |
The `URLClassLoader` provides the opportunity to add `URLs` at runtime and therefore modifying the `CLASSPATH` dynamically.
No disk dumps in Jynx 0.3
Prior to Jynx 0.3 a workaround has been dumping `A` to disk and load the class from there. We discussed the subtle nuances of selecting the right class loader and loading `A` from disk moved the machinery into a correct state. This wasn’t only cumbersome but a hurdle when a programmer intended to work within a Java sandbox. With Jynx 0.3 I feel prepared to explore Java integration with Jynx on GAE-J.