My Java FX program uses PDFBox and processes PDF embedding JPEG2000 images.
I included the required dependencies in pom.xml file as depicted on PDFBox web site. This is an extract of the "dependencies" section (I also tried adding <scope>compile</scope>
below each <version>
tag except for pdfbox which is included):
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.21</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>jbig2-imageio</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-core</artifactId>
<version>1.4.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-jpeg2000</artifactId>
<version>1.4.0</version>
</dependency>
And the build section from pom.xml reads as follows (it uses Maven Assembly Plugin to gather all jars as explained here by Baeldung):
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>linux-x86-64/**</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>
${mainClass}
</mainClass>
</manifest>
<!--Adds custom entries in manifest-->
<manifestEntries>
<Implementation-Version>${project.version}</Implementation-Version>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Built-By>Me</Built-By>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>${mainClass}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
I tested with JUnit in NetBeans the method that uses Java Advanced Imaging I/O Tools and it works as expected.
However, when running the app either from NetBeans (run triangle which leads to mvn "-Dexec.args=-classpath %classpath my.package.MainApp" -DOMP_THREAD_LIMIT=1 -DskipTests=true exec:java
) or from command line I get:
Cannot read JPEG2000 image: Java Advanced Imaging (JAI) Image I/O Tools are not installed
If I looked at the project dependency tree in NetBeans, I can see jai-imageio-core-1.4.0.jar
and jai-imageio-jpeg2000-1.4.0.jar
.
If I look at the generated jar with dependencies, the jai-imageio-core and jpeg2000 are present :
And I built the program with the standard "build" menu from NetBeans which does under the hood an mvn clean install
.
I also looked at this question that looks similar, but which does not differ from what I did.
What is happening? Why are the jars found during tests but not when program is run?
Edit : The only difference between program and tests is that program uses JavaFx while JUnit test doesn't. Does it make sense ?
Edit 2 : I made the following experiment : I removed the jai and jpeg2000 dependencies from the parent's pom, ran the test and it failed. So these dependencies are well taken into account but only during tests.
Edit 3 : Error does not show up if I remove tika dependency (but then the program does not work as expected). And although PDFBox 2.0.21 dependency is before Tika parser in Pom, the dependency graph shows a Warning "An older version of PDFBox (2.0.15) is required by TIka parser".
Edit 4: Now I've spotted the problem and it has not to do with the initial title. A long time ago I extended PDFStreamEngine
with a PrintImageLocations
class in order to get the embedded image size in PDF. This class was called inside a PrintImageLocationsImproved extends PrintImageLocations
class (this one is described in a SO answer). I did that to add public methods and get the width and height of the image.
Now I wanted to know if it was a JavaFX problem (spoiler : it is not). So I called a method involving the custom PrintImageLocations
(watch out this is not exactly following the SO answer cited above) at the beginning of the program. and automagically the program worked as expected in the program business logic part (embedded JPEG2000 could be processed). But if I don't call this custom PrintImageLocations
class before it is called by the actual business logic code then any subsequent calls show the "JAI I/O Tools missing" error. So I am still missing one part of the puzzle but I think it is linked to this custom class not loading for whatever reason the JPEG2000 dependency. The stacktrace reads as follows :
at org.apache.pdfbox.filter.Filter.findImageReader(Filter.java:163) at org.apache.pdfbox.filter.JPXFilter.readJPX(JPXFilter.java:92) at org.apache.pdfbox.filter.JPXFilter.decode(JPXFilter.java:58) at org.apache.pdfbox.cos.COSInputStream.create(COSInputStream.java:77) at org.apache.pdfbox.cos.COSStream.createInputStream(COSStream.java:175) at org.apache.pdfbox.cos.COSStream.createInputStream(COSStream.java:163) at org.apache.pdfbox.pdmodel.common.PDStream.createInputStream(PDStream.java:236) at org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject.(PDImageXObject.java:140) at org.apache.pdfbox.pdmodel.graphics.PDXObject.createXObject(PDXObject.java:70) at org.apache.pdfbox.pdmodel.PDResources.getXObject(PDResources.java:426) at myPackage.myProg.PrintImageLocations.processOperator(PrintImageLocations.java:71) at at myPackage.myProg.PrintImageLocationsImproved.processOperator(PrintImageLocationImproved.java:69) at org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:503) at org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:477)
Edit 5:
I removed JAI imageio core from the parent pom.xml and now it is provided by tika-parser and it works in test and in Netbeans. But then it does not work in CLI. What is weird is that -verbose:class
in java -Dprism.order=sw --module-path /home/user/pathTo/lib/javafx-sdk-11.0.2/lib --add-modules ALL-MODULE-PATH -verbose:class -jar target/myBig-jar-with-dependencies.jar
shows the JPEG2000 jar :
[7,434s][info][class,load] com.github.jaiimageio.jpeg2000.impl.J2KImageReader source: file://path/to/project/target/myJar_with_dependencies.jar [7,434s][info][class,load] javax.imageio.ImageReadParam source: jrt:/java.desktop [7,435s][info][class,load] com.github.jaiimageio.jpeg2000.J2KImageReadParam source: file://path/to/project/target/myJar_with_dependencies.jar [7,435s][info][class,load] com.github.jaiimageio.jpeg2000.impl.J2KImageReadParamJava source: file://path/to/project/target/myJar_with_dependencies.jar [7,435s][info][class,load] com.github.jaiimageio.jpeg2000.impl.J2KMetadata source: file://path/to/project/target/myJar_with_dependencies.jar
Final Edit :
Now I got it : to make it run from test, inside Netbeans, and CLI, I have to remove the direct dependency to jaiimageio-core in pom.xml.
Now if I run it with maven /pathToNetbeans/mvn "-Dexec.args=-classpath %classpath my.package.MainApp" -DOMP_THREAD_LIMIT=1 -DskipTests=true exec:java
it works as expected (even without replacing my.package.MainApp it works probably because the main entry point is already written in the pom.xml). But running it with java -Dprism.order=sw --module-path /home/user/pathTo/lib/javafx-sdk-11.0.2/lib --add-modules ALL-MODULE-PATH -verbose:class -jar target/myBig-jar-with-dependencies.jar
instead of maven
it doesn't anymore unless I put the "JPEG2000" and "JAI ImageIO core" jars inside the lib path given to --module-path (but then it is harder to maintain with dependencies clearly in the fat jar and other ones on disk).