I've been trying to set up a custom classloader that intercepts classes to print out which classes are being loaded into the application. The classloader looks like this
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("Loading: " + name);
return super.loadClass(name);
}
}
It just spits out the name of all the classes it loads. However, when i try to run some code,
import org.python.util.PythonInterpreter;
public class Scripts {
public String main(){
PythonInterpreter p = new PythonInterpreter();
p.exec("print 'Python ' + open('.gitignore').read()");
return "Success! Nothing broke";
}
}
via
MyClassLoader bcl = new MyClassLoader();
Class c = bcl.loadClass("Scripts");
Method m = c.getMethod("main");
String result = (String) m.invoke(c.getConstructor().newInstance());
it prints out
Loading: Scripts
Loading: java.lang.Object
Loading: java.lang.String
Loading: org.python.util.PythonInterpreter
Python build/
.idea/*
*.iml
RESULT: Success! Nothing broke
Which seems rather odd. org.python.util.PythonInterpreter
is not a simple class, and it depends on a whole bunch of other classes in the org.python.util
package. Those classes are clearly being loaded, for the exec
'd python code is able to do stuff and read my file. For some reason, though, those classes are not being loaded by the classloader which loaded PythonInterpreter
.
Why is that? I was under the impression that the classloader used to load a class C
would be used to load all the other classes needed by C
, but that's clearly not happening here. Is that assumption mistaken? If it is, how do i set it up such that all the transitive dependencies of C
are loaded by my classloader?
EDIT:
Some experiments with using URLClassLoader
, which was suggested. I modified the delegation in loadClass()
:
try{
byte[] output = IOUtils.toByteArray(this.getResourceAsStream(name));
return instrument(defineClass(name, output, 0, output.length));
}catch(Exception e){
return instrument(super.loadClass(name));
}
as well as made MyClassLoader subclass URLClassLoader rather than plain ClassLoader, grabbing URLs via:
super(((URLClassLoader)ClassLoader.getSystemClassLoader()).getURLs());
But it doesn't seem to be the right thing. In particular, getResourceAsStream()
is throwing nulls back at me for all the classes I'm requesting, even non-system classes like that Jython lib.