javacompressionjavax.imageiotwelvemonkeys

ImageIO Unsupported Image Type - TwelveMonkeys Plugin with fix not working?


I encountered the Unsupported Image Type error due to an incompatible colour profile using com.sun.imageio.plugins.jpeg.JPEGImageReader. I later found the TwelveMonkeys plugins that are proven to fix this issue and have referenced the dependent .jars in my project classpath. I downloaded them from the TwelveMonkeys github repository. Note i'm using version 3.0.2 because I'm running on Java 6 with JDK 1.6.0_45. These are the .jars I've added to my project:

common-lang-3.0.2.jar
common-io-3.0.2.jar
common-image-3.0.2.jar
imageio-core-3.0.2.jar
imageio-metadata-3.0.2.jar
imageio-jpeg-3.0.2.jar

I was able to test the library is installed and working using the following test:

Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
while (readers.hasNext()) {
    System.out.println("reader: " + readers.next());
}

Which outputs:

reader: com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader@4102799c
reader: com.sun.imageio.plugins.jpeg.JPEGImageReader@33d6f122

When i run my code, it is still trying to read the JPEG with com.sun.imageio.plugins.jpeg.JPEGImageReader and continues to throw the IIOException. Any ideas?

UPDATE: Its looking like iTextPDF is causing the issue which is a library used by the project. I setup a barebone test application that converts a CMYK JPEG to a BufferedImage and then calls ImageIO.read(img) and it works fine. I'm struggling to see a reason why iText wouldn't be finding the TwelveMonkeys plugin when it calls ImageIO.read(img) when they're in the same project and classpath, but that's probably due to my limited knowledge. I should also add that the application i'm working on is part of a web service API.


Solution

  • As often is the case, when an ImageIO plugin isn't used at run-time from a web application, the cause is that the service provider isn't found because ImageIO is already initialized, and has invoked scanForPlugins() before the web app's libraries were available to the JVM.

    From Deploying [ImageIO] plugins in a web app:

    Because the ImageIO plugin registry (the IIORegistry) is "VM global", it doesn't by default work well with servlet contexts. This is especially evident if you load plugins from the WEB-INF/lib or classes folder. Unless you add ImageIO.scanForPlugins() somewhere in your code, the plugins might never be available at all.

    In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context). If you restart your application, old classes will by default remain in memory forever (because the next time scanForPlugins is called, it's another ClassLoader that scans/loads classes, and thus they will be new instances in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions (like NullPointerExceptions when accessing static final initialized fields or NoClassDefFoundErrors for uninitialized inner classes) may occur.

    To work around both the discovery problem and the resource leak, it is strongly recommended to use the IIOProviderContextListener that implements dynamic loading and unloading of ImageIO plugins for web applications.

    The IIOProviderContextListener is contained in the twelvemonkeys-servlet.jar, and must be registered in your application's web.xml (or similar if using Spring or other framework). See the above link for details.

    Another safe alternative to using the context listener, is to place the JAR files in the application server's shared or common lib folder, instead of the WEB-INF/lib folder inside your web application.

    PS: The above problem/solution applies to ImageIO plugins in general, not just the TwelveMonkeys plugins. Because of this, the context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO or other ImageIO plugins as well.