6

I'm using the maven-shade-plugin to create an executable jar that contains all of my project's dependencies. Sometimes, these dependencies bring in dependencies of their own that clash with the dependencies of other libraries, and the maven-shade-plugin warns me that it isn't sure which version to include in the uber jar.

[WARNING] maven-shade-plugin has detected that some .class files
[WARNING] are present in two or more JARs. When this happens, only
[WARNING] one single version of the class is copied in the uberjar.
[WARNING] Usually this is not harmful and you can skeep these
[WARNING] warnings, otherwise try to manually exclude artifacts
[WARNING] based on mvn dependency:tree -Ddetail=true and the above
[WARNING] output

In general, my response to this warning is to use the <exclusions> element of the dependency declaration in my pom file to remove the offending dependencies from my project:

<!-- Amazon ElastiCache Client -->
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>elasticache-java-cluster-client</artifactId>
    <version>1.0.61.0</version>
    <exclusions>
        <!-- this junit dependency clashes with our test-scoped one and causes integration tests to fail to run -->
        <exclusion>
            <groupId>junit</groupId>
            <artifactId>junit-dep</artifactId>
        </exclusion>
        <!-- this dependency brings in two versions of cglib that clash with one another -->
        <exclusion>
            <groupId>jmock</groupId>
            <artifactId>jmock-cglib</artifactId>
        </exclusion>
        <!-- newer versions of these dependencies come with dropwizard-core -->
        <exclusion>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </exclusion>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
      </exclusions>
</dependency>

When I do this, I use mvn dependency:tree to make sure that I'm excluding the lower version of the offending dependency, in hopes that the newest version is the most mature and bug free.

Cases like the one above that end up with a lot of exclusions raise two questions about this practice:

  1. In the example above, why do I have to manually exclude junit and jmock? Both of these dependencies are marked as <scope>test</scope> in the elasticache-java-cluster-client pom.xml, so I would expect that they wouldn't be included in the jar that I get from maven.
  2. While my practice of always taking the newer version of a dependency seems to have worked so far, I'm afraid that one of these days I'm going to break something. Is there a better way to determine which version of an dependency to keep?
javapapo
  • 1,342
  • 14
  • 26
MusikPolice
  • 1,699
  • 4
  • 19
  • 38
  • Hi can you provide which version of the plugin you use. In general it wont add the libraries that are marked with scope test. Is there any case some of them are not marked as scope test in your pom? The exclusion mechanism on the dependency section, works for the Maven reactor that resolves your dependencies and not the shade plugin. So by defining exclusions in a dependency you just indicate to Maven what dependency to consider and which not. On the other hand if you want to control (filter) the dependencies and the incluses of the shade plugin you need to provide configuration on the plugin – javapapo Feb 10 '16 at 16:50
  • See [here](https://maven.apache.org/plugins/maven-shade-plugin/examples/includes-excludes.html#) for the shade plugin config – javapapo Feb 10 '16 at 16:52
  • I'm using version 2.3 of the maven-shade-plugin. I'm familiar with the maven-shade-plugin configuration and have already set it up as recommended on the page that you linked. My question is about the tactics that people typically take when choosing which exclusions to add, not about how to add exclusions in the first place. – MusikPolice Feb 10 '16 at 20:38

1 Answers1

3

Have you tried adding the maven-enforcer-plugin with the DependencyConvergence rule? This worked well for me in combination with the shade plugin. It will tell you which artifacts are bringing in different versions of the same classes. It allowed me to find out what I have to exclude.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-enforcer-plugin</artifactId>
            <executions>
                <execution>
                    <id>enforce</id>
                    <configuration>
                        <rules>
                            <DependencyConvergence/>
                        </rules>
                    </configuration>
                    <goals>
                        <goal>enforce</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
Ingo
  • 1,552
  • 10
  • 31
  • Solid suggestion, but it might be too strict for the real world. For instance, my project depends on `ru.vyarus:dropwizard-guicey:3.1.1`, which in turn has a dependency convergence error - one of its dependencies in turn depends on `com.google.inject:guice:3.0`, while two others depend on `com.google.inject:guice:4.0`. Maven exclusions don't let me specify which version of something to include, so I don't know how to resolve this issue. – MusikPolice Feb 11 '16 at 16:02