由Maven Shade插件构建的无效或损坏的JAR文件

问题描述:

在将Maven jFree依赖项添加到现有应用程序后,我无法执行创建的jar.

After adding the Maven jFree dependency to my existing application, I'm not able to execute the created jar.

我收到的唯一错误消息如下:

The only error message I get is the following:

java -jar target/com.company.product-1.0.0-SNAPSHOT.jar 
Error: Invalid or corrupt jarfile target/com. company.product-1.0.0-SNAPSHOT.jar

完整的pom.xml看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion
<groupId>com.mycompany</groupId>
<artifactId>com.mycompany.test</artifactId>
<name>${project.artifactId}</name>
<version>1.0.0-SNAPSHOT</version>

<properties>
    <java-version>1.7</java-version>
    <org.springframework-version>3.1.1.RELEASE</org.springframework-version>
    <org.springframework.data-version>1.0.3.RELEASE</org.springframework.data-version>
    <org.springframework.ws-version>2.0.4.RELEASE</org.springframework.ws-version>
    <org.springframework.ws.oxm-version>1.5.10</org.springframework.ws.oxm-version>
    <org.aspectj-version>1.6.12</org.aspectj-version>
    <org.slf4j-version>1.5.10</org.slf4j-version>
    <selenium-java-version>2.25.0</selenium-java-version>
    <browser-mob-version>2.0-beta-6</browser-mob-version>
</properties>

<dependencies>

    <!-- Hint A: If we delete this dependency it works -->
    <dependency>
        <groupId>org.jfree</groupId>
        <artifactId>jfreechart</artifactId>
        <version>1.0.14</version>
                    <exclusions>
            <exclusion>
                <artifactId>itext</artifactId>
                <groupId>com.lowagie</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itextpdf</artifactId>
        <version>5.3.2</version>
    </dependency>

    <dependency>
        <groupId>de.schlichtherle.io</groupId>
        <artifactId>truezip</artifactId>
        <version>6.6</version>
    </dependency>

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>apache-log4j-extras</artifactId>
        <version>1.1</version>
    </dependency>

    <!-- Caching with ehcache -->
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>2.5.2</version>
        <type>pom</type>
    </dependency>

    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${org.springframework-version}</version>
        <exclusions>
            <!-- Exclude Commons Logging in favor of SLF4j -->
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${org.springframework-version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>3.0.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>3.0.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>3.0.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${org.springframework-version}</version>
        <scope>test</scope>
    </dependency>
    <!-- Hibernate -->
    <dependency>
        <groupId>org.hibernate.java-persistence</groupId>
        <artifactId>jpa-api</artifactId>
        <version>2.0-cr-1</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>3.5.1-Final</version>
    </dependency>

    <!-- Database Connectors (HSQL should be removed later) -->
    <dependency>
        <groupId>hsqldb</groupId>
        <artifactId>hsqldb</artifactId>
        <version>1.8.0.7</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.16</version>
    </dependency>

    <!-- AspectJ -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${org.aspectj-version}</version>
    </dependency>

    <!-- Logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${org.slf4j-version}</version>
    </dependency>

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>

    <!-- @Inject -->
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <!-- Spring Data JPA dependencies -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>${org.springframework.data-version}</version>
    </dependency>
    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>jsr311-api</artifactId>
        <version>1.1.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>${org.springframework.ws.oxm-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-xml</artifactId>
        <version>${org.springframework.ws-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-ws-core</artifactId>
        <version>${org.springframework.ws-version}</version>
    </dependency>
    <dependency>
        <groupId>javax.xml</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.1</version>
    </dependency>
    <dependency>
        <groupId>javax.xml</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.1</version>
    </dependency>

    <!-- Test -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.7</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.9.0</version>
        <scope>test</scope>
    </dependency>

    <!-- Common Utils -->
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>2.6</version>
    </dependency>

    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
    </dependency>

    <dependency>
        <groupId>commons-cli</groupId>
        <artifactId>commons-cli</artifactId>
        <version>1.2</version>
    </dependency>       

    <!-- Selenium -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>${selenium-java-version}</version>
    </dependency>

    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-firefox-driver</artifactId>
        <version>${selenium-java-version}</version>
    </dependency>

    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-chrome-driver</artifactId>
        <version>${selenium-java-version}</version>
    </dependency>

    <!-- CSV Lib for Keyword Checker -->
    <dependency>
        <groupId>net.sf.opencsv</groupId>
        <artifactId>opencsv</artifactId>
        <version>2.0</version>
    </dependency>

    <!-- Google Places API -->
    <dependency>
        <groupId>com.google.api-client</groupId>
        <artifactId>google-api-client</artifactId>
        <version>1.10.3-beta</version>
        <exclusions>
            <exclusion>
                <artifactId>jackson-core-asl</artifactId>
                <groupId>org.codehaus.jackson</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>com.google.api-client</groupId>
        <artifactId>google-api-client-appengine</artifactId>
        <version>1.10.3-beta</version>
    </dependency>

    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.9.9</version>
    </dependency>

    <!-- Google Geocode -->
    <dependency>
        <groupId>com.google.code.geocoder-java</groupId>
        <artifactId>geocoder-java</artifactId>
        <version>0.9</version>
    </dependency>

    <dependency>
        <groupId>com.googlecode.json-simple</groupId>
        <artifactId>json-simple</artifactId>
        <version>1.1</version>
    </dependency>

    <dependency>
        <groupId>com.googlecode.json-simple</groupId>
        <artifactId>json-simple</artifactId>
        <version>1.1</version>
    </dependency>

    <dependency>
        <groupId>net.sf.jgrapht</groupId>
        <artifactId>jgrapht</artifactId>
        <version>0.8.3</version>
    </dependency>

    <dependency>
        <groupId>jgraph</groupId>
        <artifactId>jgraph</artifactId>
        <version>5.13.0.0</version>
    </dependency>

    <!-- Apache Http Client -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.2.1</version>
    </dependency>

    <!-- Amazon web services client -->
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk</artifactId>
        <version>1.2.15</version>
        <exclusions>
            <exclusion>
                <artifactId>jackson-core-asl</artifactId>
                <groupId>org.codehaus.jackson</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Docx4j - reading excel files  -->
    <dependency>
        <groupId>org.docx4j</groupId>
        <artifactId>docx4j</artifactId>
        <version>2.8.0</version>
    </dependency>

    <!-- Browser Mob Proxy -->

    <dependency>
        <groupId>biz.neustar</groupId>
        <artifactId>browsermob-proxy</artifactId>
        <version>${browser-mob-version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.seleniumhq.selenium</groupId>
                <artifactId>selenium-api</artifactId>
            </exclusion>
            <exclusion>
                <artifactId>icu4j</artifactId>
                <groupId>com.ibm.icu</groupId>
            </exclusion>
            <exclusion>
                <artifactId>jackson-mapper-asl</artifactId>
                <groupId>org.codehaus.jackson</groupId>
            </exclusion>
            <exclusion>
                <artifactId>jackson-core-asl</artifactId>
                <groupId>org.codehaus.jackson</groupId>
            </exclusion>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-jdk14</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Hint B: If we copy this Apache POI dependencies to the top, it works -->
    <!-- Apache POI - for reading xlsx files -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.8</version>
    </dependency>

    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.8</version>
    </dependency>

    <dependency>
        <groupId>org.apache.xmlbeans</groupId>
        <artifactId>xmlbeans</artifactId>
        <version>2.5.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>ooxml-schemas</artifactId>
        <version>1.1</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>${java-version}</source>
                <target>${java-version}</target>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>1.7</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>

                            <!-- must be SURE to do this with both spring.handlers and spring.schemas. 
                                otherwise you won't be able to use them in the spring config files. -->
                            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.handlers</resource>
                            </transformer>

                            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.schemas</resource>
                            </transformer>

                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.mycompany.test.Start</mainClass>
                            </transformer>
                        </transformers>

                        <filters>
                          <filter>
                            <artifact>bouncycastle:bcprov-jdk15</artifact>
                            <excludes>
                              <exclude>META-INF/BCKEY.DSA</exclude>
                              <exclude>META-INF/BCKEY.SF</exclude>
                              <exclude>META-INF/MANIFEST.MF</exclude>
                            </excludes>
                          </filter>
                        </filters>                          

                    </configuration>
                </execution>
            </executions>
        </plugin>

    </plugins>
</build>

如果执行jar,则不会得到其他任何调试信息.

I don't get any other debug information if I execute the jar.

是否可以验证jarfile?还是有类似java -verbose ......

Is there any option to validate the jarfile? Or having something like java -verbose...?

复制步骤:

  1. 创建一个新文件夹
  2. 从上方将Pom.xml复制到文件夹中.
  3. 保存小型的`public static void main(...).进入src/main/java/com/mycompany/test/Start.java
  4. 执行mvn clean package
  5. 执行java -jar target/com.mycompany.test-1.0.0-SNAPSHOT.jar
  1. create a new folder
  2. Copy the Pom.xml from above into the folder.
  3. Save the small `public static void main(...). into src/main/java/com/mycompany/test/Start.java
  4. execute mvn clean package
  5. execute java -jar target/com.mycompany.test-1.0.0-SNAPSHOT.jar

附件1:

package com.mycompany.test;


public class Start
{
    public static void main(final String[] args) 
    {
        System.out.println("If you are able to get this printed with java -jar you made it. Thanks a lot! :)");
    }
}

我开始删除一些依赖项以识别问题.但是我还不清楚发生了什么.

I started to delete some dependencies to identify the problem. But I haven't got a clear understanding what goes wrong.

现在我弄清楚了(请参阅pom.xml中的提示B),将Apache POI依赖项移到顶部将解决此问题.但是我仍然不知道如何解决问题以及问题出在什么地方.

Now I figured out (See hint B in the pom.xml) that moving the Apache POI dependencies to the top will solve the problem. But still I don't know how to why and what is the problem.

最后,如果我使用pom.xml构建您的项目,那么您向我们展示了我们,并在jfreechart之后声明了apache-poi ve提到我收到了一个损坏的JAR.交换这两个依赖关系的顺序确实可以为我提供正确的JAR.

On my end if I build your project using the pom.xml you've showed us, with apache-poi declared after jfreechart, then as you've mentioned I get a corrupt JAR. Swapping the order of these two dependencies indeed gives me a correct JAR.

我以前对maven-shade-plugin有一定的经验,当我使用它时,在创建JAR的META-INF目录时遇到了麻烦,因此我检查了缺陷.

I've some previous experience with the maven-shade-plugin and when I used it I had trouble with the created JAR's META-INF directory, so I've checked that for defects.

我试图在本地文件系统上的某个地方复制(使用Total Commander)META-INF目录,我注意到在复制META-INF/licences/中的文件时出错.如果我尝试将它们分别复制到某个地方并且可以正常工作,但无法复制整个内容.我的结论是JAR/ZIP存档已损坏.

I've tried to copy (using Total Commander) the META-INF directory somewhere on my local filesystem and what I've noticed is that there were errors while copying the files in META-INF/licences/. If I've tried to copy those somewhere individually and it worked but I couldn't copy the whole thing. My conclusion was that the JAR/ZIP archive is corrupt.

我所做的是我输入 了Total Commander中的JAR(在JAR文件中通过 Ctrl + PgDown ),然后我重命名 thirdpartylicenses.txtthirdpartylicenses.txt.wtf.这样做,Total Commander可以保存并重新打包JAR(我已经安装了Total7zip Total Commander Packer插件-如果有人尝试过但不起作用,请尝试安装此插件).

What I did was I entered that JAR in Total Commander (Ctrl+PgDown over the JAR file) and I renamed thirdpartylicenses.txt to thirdpartylicenses.txt.wtf. Doing this Total Commander offers to save this and it repackages the JAR (I've the Total7zip Total Commander packer plugin installed - if anyone tries this and it doesn't work try it with this installed).

在此之后.可以.

((我也尝试过使用Cygwin的unzip/zip命令重新打包整个内容,而不重命名任何内容,但这没用,新的存档仍然损坏.TotalCommander或我提到的插件做一些魔术.)

(I've also tried to repack the whole thing without renaming anything using Cygwin's unzip/zip commands, but that didn't work the new archive was still corrupt. Total Commander or the plugin I've mentioned does some magic.)

我想maven-shade-plugin只会创建损坏或无效的ZIP/JAR存档.我不确定为什么,也许我所描述的内容对其他任何人都无效,但我想我会提一下,以便也许我能帮上忙.

I guess the maven-shade-plugin merely creates a corrupt or invalid ZIP/JAR archive. I'm not sure why and maybe what I've described won't work for anyone else but I thought I'd mention it so maybe I could help.

我不能只留下这个,所以我更深入地研究了,我想我已经找到了答案.

I couldn't just leave this alone, so I've dug deeper and I think I've found the answer.

JAR包含65608个条目. good JAR包含65450个条目.

The bad JAR contains 65608 entries. The good JAR contains 65450 entries.

猜出的条目数纯 ZIP文件?是的. Wiki文章谈到了克服此限制的ZIP64格式.

Guess what's the upper limit of the number of entries for a plain ZIP file? Yeah. The Wiki article speaks about a ZIP64 format which overcomes this limitation.

good JAR的条目较少,因为由于依赖声明在pom.xml中的位置,实际的依赖关系发生了变化. (如该答案所述.)

The good JAR has less entries because the actual dependencies change because of the position of the dependency declarations in your pom.xml. (As described by this answer.)

我已经数过这样的条目.

I've counted the entries like this.

Collections.list(new JarFile("...").entries()).size();

我使用的是 Java 7,它似乎支持新的ZIP64格式,所以也许有人尝试计算条目数使用Java 5或6的 bad JAR中的代码会收到错误消息(我不确定).

I was using Java 7 which seems to support the new ZIP64 format, so maybe if someone tries to count the entries in the bad JAR using Java 5 or 6 will receive an error (I'm not sure though).

我还尝试运行JAR 爆炸.我已经将整个JAR解压缩到一个目录中,然后像这样运行整个程序.

I also tried to run the JAR exploded. I've unpacked the whole JAR into a directory and run the whole thing like this.

java -cp <dir/ com.mycompany.test.Start

像护身符一样工作.

底线.请勿过度使用 maven-shade-plugin.

我有一个正在工作的项目,在那里我要像这样构建我的项目.

I have a project at work where I build my project like this.

  • I copy my project's dependencies using the maven-dependency-plugin. Checkout the copy-dependencies goal. This copies your dependencies into target/dependency if I remember correctly.
  • Using the maven-jar-plugin I add these dependencies to my final JAR's MANIFEST.MF as Class-Path entries using these options in the plugins configuration.

<classpathPrefix>dependency/</classpathPrefix>
<addClasspath>true</addClasspath>

所以我将有Class-Path项,例如dependency/<artifactId>-<version>.jar等.

So I'll have Class-Path entries like dependency/<artifactId>-<version>.jar, etc.

主要是我选择使用此解决方案,因为在我的项目中,我使用了一些Bouncy Castle JAR,这些JAR在其META-INF目录中有一些 this和that .当我使用maven-shade-plugin创建我最终的可运行JAR时,整个地狱都崩溃了,我变得烦人的无法找到该方法,并且此签名不是很正确错误.

Primarily I've opted to use this solution, because in my project I use some Bouncy Castle JARs which JARs have some extravagant this and that inside their META-INF directory. When I used the maven-shade-plugin to create my final runnable JAR all hell broke loose and I got nasty that method couldn't be found and this signature is not quite right errors.

您也应该这样做.这个Maven阴影业务太阴暗而无法使用(双关语意).

You should be doing something like this also. This Maven shade business is too shady to be useful (pun intended).

这是 baba ),也许它将在将来对某人有所帮助.

Here's a blog post on the whole process that I've tried to describe just above (thanks to baba), maybe it'll help someone in the future.