Recently, I discovered some strange problems, when I was trying to parse XML files when being called via
JNI. The special thing here was that this method was called from a class that I loaded with a custom Java
ClassLoader. Here, I got a
NullPointerException when I was trying to obtain an
XPathFactory via
XPathFactory xpathFactory = XPathFactory.newInstance();
Usually, this works out of the box, but I got an exception similar to this one:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6245257
The strange thing is: The same code works if it was not called from a JNI method but from my own Java method.
Searching via Google reveiled a preliminary solution at
https://forums.oracle.com/forums/thread.jspa?threadID=1626887
They suggest to use
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[0], cl));
...
Thread.currentThread().setContextClassLoader(cl);
This code pretty much resets the
ClassLoader to some more reasonable value. However, my problems did not vanish completely. The next error popped up, when I utilized some
spring beans parser that threw strange exceptions about some XSD files claiming they were not readable. Again, the same code worked when being called from plain Java. So I started to dig into the problem. JNI guarantees that the
ClassLoaders are the same when a Java method is called from JNI that were used to load the JNI library (see section 11.2 at
http://192.9.162.55/docs/books/jni/html/design.html). This was true. A quick check with
System.out.println(getClass().getClassLoader());
showed the same
ClassLoader for the time the library was loaded and for the time when the method from JNI was called. However, this was not true for the context
ClassLoader. When the library was loaded, the method returned the usual
URLClassLoader, but when being called from JNI, it simply returend
null, which is the system
ClassLoader. This was also true if the code above was used. This means that you will be able to access all class files that reside in rt.jar plus those that you added in your own class loader. Other classes that are subject to the endorsed capabilities will not be accessible anymore (see
http://docs.oracle.com/javase/6/docs/technotes/guides/standards/). This includes the XML parsing libraries.
My solution for now was to adapt the context ClassLoader with
ClassLoader loader = getClass().getClassLoader();
Thread.currentThread().setContextClassLoader(loader);
This sets the context ClassLoader to the same value as it was when the JNI library was loaded.