Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service loader does not locate service provider class, even though class is in same JAR file as META-INF/services

I am successfully running a C++ application that loads a JVM with a JAR file as a classpath argument. The application then successfully uses JNI calls to execute various functions defined in .class files within this JAR file.

Included in the .jar file's directory structure is a 3rd-party set of .class files - those merged from jai_imageio.jar (these .class files, with their full directory structure, were merged into this single .jar file using Intellij IDEA). Also included in the merged .jar file are the lines from the original jai_imageio.jar's manifest.mf - in particular implementation-title and related lines. Also, the meta-inf/services folder is present, also copied from jai_imageio.jar. The various services listed within the services directory look correct.

In particular, javax.imageio.spi.ImageOutputStreamSpi within the meta-inf/services folder in the .jar file contains the single line com.sun.media.imageioimpl.stream.ChannelImageOutputStreamSpi, and there is a class corresponding to this within the .jar file at exactly the directory indicatted by that line: com/sun/media/imageioimpl/stream/ChannelImageOutputStreamSpi.class.

However, when the Java code executes the following line:

ImageIO.write(image, "tiff", file); // Assume 'image' is a BufferedImage and 'file' is a File

... it throws an exception:

java.util.ServiceConfigurationError: javax.imageio.spi.ImageOutputStreamSpi:
Provider com.sun.media.imageioimpl.stream.ChannelImageOutputStreamSpi not found

... even though this class is present within the same .jar file, as noted above.

Can somebody please explain why this error is happening, and what I should do to resolve it.

like image 290
Dan Nissenbaum Avatar asked May 09 '12 10:05

Dan Nissenbaum


1 Answers

From this documentation http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html

"When a thread is attached to the VM, the context class loader is the bootstrap loader."

Any native thread attached to the JVM via AttachCurrentThread() gets only the bootstrap class loader, not even the system class loader. Classes referenced by ServiceLoader will not be available unless you explicitly fix up the new thread's context class loader.

This can be done like:

java.lang.Thread.currentThread().setContextClassLoader(
    java.lang.ClassLoader.getSystemClassLoader()
);
like image 67
Wheezil Avatar answered Oct 29 '22 17:10

Wheezil