问题
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.
回答1:
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>
回答2:
I had to use this ImageIO jar. It worked like a charm. Found it here.
回答3:
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>
回答4:
These can help you to solve the problem.
How to run jai-imageio with source code
vendorName == null
回答5:
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
回答6:
(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());
}
}
来源:https://stackoverflow.com/questions/7051603/jai-vendorname-null