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.
Keine Kommentare:
Kommentar veröffentlichen