Jynx 0.3 – how to fix custom class loaders for use with Jython

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.

This entry was posted in Jynx, Jython. Bookmark the permalink.