Intention
I am using the java.lang.instrument package to create some tools for Java programs. The idea is that I use bytecode manipulation through this system to add method calls at the beginning and end of each method. Generally speaking, a modified Java method will look like this:
public void whateverMethod(){ MyFancyProfiler.methodEntered("whateverMethod");
MyFancyProfiler is the entry point to a relatively complex system that is initialized during the premain method (which is part of java.lang.instrument ).
edit - MyFancyProfiler contains a static API that will receive a link to the rest of the system through a mechanism similar to that described in the solution to this question . The link is obtained as Object and the corresponding calls are made using reflection, so even if the current ClassLoader does not know about the base classes, itβs all will work equally.
Difficulties
For a simple Java program, the approach works just fine. For "real" applications (for example, windowed applications and especially RCP / OSGi applications) I had problems with ClassLoaders . Some ClassLoaders will not know how to find the MyFancyProfiler class, so it will throw exceptions when it tries to call static methods in MyFancyProfiler .
My solution to this (and where my real problem happens) is currently " MyFancyProfiler " MyFancyProfiler in every ClassLoader encounters by ClassLoader call to defineClass . Its essence is as follows:
public byte[] transform(ClassLoader loader, String className, /* etc... */) { if(/* this is the first time I've seen loader */){ //try to look up `MyFancyProfiler` in `loader`. if(/* loader can't find my class */){ // get the bytes for the MyFancyProfiler class // reflective call to // loader.defineClass( // "com.foo.bar.MyFancyProfiler", classBytes, 0, classBytes.length); } } // actually do class transformation via ASM bytecode manipulation }
edit for more information. The reason for this injection is that each class, no matter which ClassLoader loader loads, can directly call MyFancyProfiler.methodEntered . Once he makes this call, MyFancyProfiler will need to use reflection to interact with the rest of the system, otherwise I will get an InvocationTargetException or NoClassDef exception when he tries to call directly. Currently, I work in such a way that the only βdirectβ dependencies of MyFancyProfiler are the JRE system classes, so this seems like normal.
Problem
It even works! Most part of time! But for at least two separate ClassLoaders that I encounter when trying to trace Eclipse (starting the IDE from the command line), I get a NullPointerException coming from the ClassLoader.defineClass method:
java.lang.NullPointerException at java.lang.ClassLoader.checkPackageAccess(ClassLoader.java:500) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:791) at java.lang.ClassLoader.defineClass(ClassLoader.java:634) 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:601)
The 500 line of the ClassLoader.java class is a call to domains.add(pd) , where domains represents Set, which is initialized during the constructor, and pd is ProtectionDomain , which (as far as I can tell) should be the "default" ProtectionDomain . Therefore, I do not see the obvious path for this line to raise a NullPointerException . I'm currently at an impasse, and I hope someone can talk about it.
What could cause defineClass to crash this way? And if there is no obvious solution, can you suggest a potential alternative approach to my common problem?