Freitag, 12. April 2013

JNI causes XML parsing problems when using a custom ClassLoader

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.





Montag, 8. April 2013

OpenNI 2 with Fedora 18

The current Fedora 18 still ships with an older version of OpenNI. Therefore, I just tried to compile OpenNI2 from source [1] on my Fedora 18 system. There were some differences with regard to the required packages as they are noted for the usual Linux build instructions.

libUSB was installed by
sudo yum install libusbx-devel

freeglut3 does not exist, but
sudo yum install freeglut-devel 
seemed to be sufficient

libGL can be installed by either
sudo yum install mesa-libGLU-devel
or by installing the whole group via
sudo yum groupinstall "X Software Development"

In addition I added the following to line 73 of OpenNI2/ThirdParty/PSCommon/BuildSystem/CommonCppMakefile

LDFLAGS += -lpthread -lrt $(LIB_DIRS_OPTION) $(USED_LIBS_OPTION)
was: LDFLAGS += $(LIB_DIRS_OPTION) $(USED_LIBS_OPTION)

It is important that pthread and rt are linked prior to the rest. Afterwards calling make ran without failures.

[1] https://github.com/OpenNI/OpenNI2