1

I'm using Reactive Spring Boot with Netty and Gradle as a build system. To build production's executable I use a task bootJar with launchScript().

I'm also using various TwelveMonkeys' plugins for ImageIO processing. Everything works fine when I run the application from IntelliJ but when I build it with bootJar - those plugins are not working - just like they were not installed.

I opened the jar produced by Spring, and all plugins jars are inside.

On the TwelveMonkeys' manual, they say:

The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar. Re-packaging is not necessary to use the library, and not recommended.

However, if you like to create a "fat" JAR, or otherwise like to re-package the JARs for some reason, it's important to remember that automatic discovery of the plugins by ImageIO depends on the Service Provider Interface (SPI) mechanism. In short, each JAR contains a special folder, named META-INF/services containing one or more files, typically javax.imageio.spi.ImageReaderSpi and javax.imageio.spi.ImageWriterSpi. These files exist with the same name in every JAR, so if you simply unpack everything to a single folder or create a JAR, files will be overwritten and behavior be unspecified (most likely you will end up with a single plugin being installed).

So here's a couple of questions:

  1. Does the bootJar task create a fatJar or is this something else?
  2. Is the jar, created by bootJar, capable of discovering the plugins using SPI, as described in above comment?
  3. If it's capable of doing that - why it's not working and what are possible places to start investigating? (perhaps is this configuration problem?)
  4. If it's not capable - is the shadowJar the only solution to that problem?
Mariusz.v7
  • 2,322
  • 2
  • 16
  • 24
  • 2
    1. Yes but differently it just includes the jars it doesn't repackage them. 2. Yes, 3. It should work, you must be doing something you aren't showing. 4. no as that would mean you need to work around Spring Boot as well. – M. Deinum Apr 29 '21 at 06:13
  • `ImageIO.scanForPlugins()` (which trigger the SPI lookup) uses `Thread.getContextClassLoader()`, and according to [Executable Jar Restrictions](https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-executable-jar-format.html#executable-jar-restrictions) in the Spring reference docs, that's all that's required. – Harald K Apr 30 '21 at 06:54
  • 1
    I've just connected remote debugger and it turns out that plugins are available as you mentioned. However, the following fragment is not working on remote machine (bootJar): `ImageWriter imageWriter = ImageIO.getImageWriter(imageReader);` - it returns null. The instance of `imageReader` variable is: `com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader`. On the other hand, when I do: `ImageIO.getImageWritersByMIMEType("image/jpeg").next()` - it returns properly `com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriter`. @HaraldK – Mariusz.v7 Apr 30 '21 at 13:22
  • 2
    Unfortunately, `ImageIO.getImageReader(ImageWriter)`and `ImageIO.getImageWriter(ImageReader)` use `Class.forName(name, true, ClassLoader.getSystemClassLoader())` (for reasons I don't understand, could be an oversight). We should probably file a bug with OpenJDK, as the behavior is inconsistent with the normal SPI lookup. TwelveMonkeys [issue #16](https://github.com/haraldk/TwelveMonkeys/issues/16) from 2013 is basically the same problem... – Harald K Apr 30 '21 at 13:32
  • 1
    Wow... Allright, so all the mystery is gone... :) I'll take image writer using different method. Big thanks, I would never figure it out by myself! – Mariusz.v7 Apr 30 '21 at 13:43

0 Answers0