So I finished coding my application to rotate TIFF images which required JAI to manipulate the TIFFs.
It works fine when working under Eclipse, but whenever I build a fat jar for 开发者_高级运维the library and then create one implementing that (per http://fjep.sourceforge.net/fjeptutorial.html), when I do the java -jar Push.jar \path\to\dir, it runs up until it hits the part where it is compressing and saving:
TIFFImageWriterSpi tiffspi = new TIFFImageWriterSpi();
ImageWriter writer = tiffspi.createWriterInstance();
//Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("TIFF");
//ImageWriter writer = iter.next();
ImageWriteParam param2 = writer.getDefaultWriteParam();
param2.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param2.setCompressionType("LZW");
param2.setCompressionQuality(0.0f);
File fOutputFile = workArea[i];
ImageOutputStream ios = ImageIO.createImageOutputStream(fOutputFile);
writer.setOutput(ios);
if (frontPage == 1)
{
writer.write(null, new IIOImage(pg1, null, null), param2);
writer.writeInsert(-1, new IIOImage(pg2, null, null), param2);
}
else if (frontPage == 2)
{
writer.write(null, new IIOImage(pg2, null, null), param2);
writer.writeInsert(-1, new IIOImage(pg1, null, null), param2);
}
remaining = remaining - 1;
if (remaining > 0)
System.out.println(remaining + " remaining.");
else
System.out.println("Done.");
It blows up on the first line of that section with the message:
Exception in thread "main" java.lang.IllegalArgumentException: vendorName == null!
....rest of stack trace.
Since I spent a considerable amount of time debugging this problem, I thought I would share my solution here, despite the age of the question. Srikanth's second link was particularly helpful.
Reason for the error
JAI requires a vendor name for some of its deep internals, particularly the javax.imageio.spi.IIOServiceProvider which gets used by many (all?) of the image readers for their low-level IO. It's not picky what the string is, but it can't be null.
Rather than hard-code the vendor name, the ImageReaderSpi class gets the vendor name from sun.media.imageioimpl.common.PackageUtil.getVendor(). This in turn reads it from the jar's MANIFEST.MF. Normally you link against the standard jai-imageio packagage, so Sun's vendor info gets read. However, since you're making a fat jar file, you replaced Sun's MANIFEST.MF with your own which lacks the required information.
Solution
Include the following lines in your MANIFEST.MF file:
Specification-Title: Java Advanced Imaging Image I/O Tools Specification-Version: 1.1 Specification-Vendor: Sun Microsystems, Inc. Implementation-Title: com.sun.media.imageio Implementation-Version: 1.1 Implementation-Vendor: Sun Microsystems, Inc.
The values for each property can be anything (I used my specific application/version/company), as long as all six are defined.
Maven
If you were using maven's assembly plugin to create your fat jar, maven can automatically include the correct version numbers and such. Update your pom.xml
with the following <archive>
section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Specification-Vendor>MyCompany</Specification-Vendor>
<Implementation-Vendor>MyCompany</Implementation-Vendor>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>create-my-bundle</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
I had to use this ImageIO jar. It worked like a charm. Found it here.
The accepted answer by Quantum7 explains the source of the problem, and in the Maven section it provides a solution when using the Maven Assembly plugin to produce a JAR including dependencies. If you are instead using the Maven Shade plugin to produce the JAR with dependencies, the configuration is slightly different. You can add something like the following to the configuration section of the Shade plugin in your pom.xml
:
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.conveyal.r5.R5Main</Main-Class>
<Specification-Title>Java Advanced Imaging Image I/O Tools</Specification-Title>
<Specification-Version>1.1</Specification-Version>
<Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
<Implementation-Title>com.sun.media.imageio</Implementation-Title>
<Implementation-Version>1.1</Implementation-Version>
<Implementation-Vendor>Sun Microsystems, Inc.</Implementation-Vendor>
<Extension-Name>com.sun.media.imageio</Extension-Name>
</manifestEntries>
</transformer>
</transformers>
</configuration>
DISCLAIMER
The issue that I was having was slightly different, I was getting the "ClassNotFound" error while trying to run a compiled jar file. I happened upon this SO question while researching, so for others who followed the same trail that I did, here you go.
Possible Solution to ClassNotFound Error
To those who may find this question later on, if nothing else seems to work, You could try using the Apache Shader plugin for Maven. Here is some more information on it.
I'm not experienced enough to be able to tell you how it does it, but Apache Shader packages all the used dependencies in your project into the final jar file, so that all the dependencies are included in the META-INF folder when it's built. This increases the size of the jar file (depending on how many libraries you used in your project), but also seems to fix the jar not being able to find classes from outside libraries that are used.
To use the Shader plugin, add the following to your POM. I included it after the dependencies tag and before the properties tag.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.package.name.className</Main-Class>
<Build-Number>1</Build-Number>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
NOTE: Make sure you change the package and class name to reflect your project's package and class names.
Other useful links: Similar stack overflow question
(Would have been a comment on Quantum7's answer if I had enough reputation)
I ran into the same problem. Quantum7's answer saved the day! After putting in manifestEntries Specification-Vendor and Implementation-Vendor, however, the execution of my fat jar still failed with the exception below. Note it is
version == null!
not
vendorName == null!
java.util.ServiceConfigurationError: javax.imageio.spi.ImageReaderSpi: Provider com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReaderSpi could not be instantiated
at java.util.ServiceLoader.fail(Unknown Source)
at java.util.ServiceLoader.access$100(Unknown Source)
at java.util.ServiceLoader$LazyIterator.nextService(Unknown Source)
at java.util.ServiceLoader$LazyIterator.next(Unknown Source)
at java.util.ServiceLoader$1.next(Unknown Source)
at javax.imageio.spi.IIORegistry.registerApplicationClasspathSpis(Unknown Source)
at javax.imageio.spi.IIORegistry.<init>(Unknown Source)
at javax.imageio.spi.IIORegistry.getDefaultInstance(Unknown Source)
at javax.imageio.ImageIO.<clinit>(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: version == null!
at javax.imageio.spi.IIOServiceProvider.<init>(Unknown Source)
at javax.imageio.spi.ImageReaderWriterSpi.<init>(Unknown Source)
at javax.imageio.spi.ImageReaderSpi.<init>(Unknown Source)
at com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReaderSpi.<init>(CLibJPEGImageReaderSpi.java:80)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
... 14 more
Putting in manifestEntries below fixed it.
<manifestEntries>
<Specification-Title>Java Advanced Imaging Image I/O Tools</Specification-Title>
<Specification-Version>1.1</Specification-Version>
<Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
<Implementation-Title>com.sun.media.imageio</Implementation-Title>
<Implementation-Version>1.1</Implementation-Version>
<Implementation-Vendor>Sun Microsystems, Inc.</Implementation-Vendor>
</manifestEntries>
When running the fat jar, I made sure that none of the three values in the code below is null.
import com.sun.media.imageioimpl.common.PackageUtil;
public class ManifestCheck {
public static void main(String[] args) {
System.out.println(PackageUtil.getVendor());
System.out.println(PackageUtil.getVersion());
System.out.println(PackageUtil.getSpecificationTitle());
}
}
These can help you to solve the problem.
How to run jai-imageio with source code
vendorName == null
I was also getting the same error after struggling for many solution found this and worked for me.
Need to give venderName manually in code as:
import com.sun.media.imageioimpl.common.PackageUtil;
try {
MainClass.afVenderNames(PackageUtil.class, "afreen", "afreen", "afreen");
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
} catch (SecurityException e1) {
e1.printStackTrace();
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
System.out.println(PackageUtil.getVendor());
System.out.println(PackageUtil.getVersion());
System.out.println(PackageUtil.getSpecificationTitle());
public static void afVenderNames(Class<?> PackageUtil, String vendor, String version, String specTitle)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field vendorField = PackageUtil.getDeclaredField("vendor");
vendorField.setAccessible(true);
vendorField.set(null, vendor);
Field versionField = PackageUtil.getDeclaredField("version");
versionField.setAccessible(true);
versionField.set(null, version);
Field specTitleField = PackageUtil.getDeclaredField("specTitle");
specTitleField.setAccessible(true);
specTitleField.set(null, specTitle);
}
It doesn't matter what will be the vendorName value is if it's not NULL. My code was running fine in eclipse but giving exceptions in an exported runnable jar.
Finally worked when wrote the above code and exported the jar.
精彩评论