Maven shaded jar used as external project dependency

The classic scenario is:

  • A project producing an uber-jar has its own dependencies (dependency elements in its pom.xml file) which then are packaged together in one uber-jar as Maven artifact
  • When using this uber-jar as a dependency (dependency element) of another project, Maven would then inspect its <artifact>-<version>.pom file (published together with the final artifact into the Maven repository), which is basically a renamed copy of its original pom.xml file, where dependencies (dependency element) were declared (exactly the dependencies packaged into the uber-jar).
  • Since you already have them packed, you would then like to ignore the .pom file (and its dependencies element), for that you need to add exclusions as following:

    <dependency>
        <groupId>com.sample</groupId>
        <artifactId>something-uber</artifactId>
        <version>some-version</version>
        <exclusions>
            <exclusion>
                <groupId>*</groupId>
                <artifactId>*</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    

Note: the feature above is only available since Maven 3.2.1.

As such, you are making clear to Maven you don't want any transitive dependency and Maven dependency mediation would then not trigger them.


As a side note: this is not a good practice to have an uber-jar as dependency of a project: it will just make maintenance harder since you can't control transitive dependencies via dependencyManagement or dependencies order of the dependent project. As such you will always need to re-pack the uber jar whenever a dependency (one of its transitive one) would need maintenance (change version and so on) and have much less control on the dependent project (again, harder maintenance).



As the pom.xml on your source project that produces the uber jar declares transitive dependencies, if you include it as dependency into an external project then Maven will try to get these ( even if these are already included on the uber jar ).

I share a solution that let you avoid to explicitly exclude all transitive dependencies including it into an external project as explained by @A_DiMatteo on his solution ( I agree also with him about to avoid to use uber jar as dependency if not strictly necessary to do it for some reason ). As result then you should be able to include your uber jar dependency without using exclusions element as follow:

<dependency>
  <groupId>com.sample</groupId>
  <artifactId>something-uber</artifactId>
  <version>some-version</version>
</dependency>

Premise: my goal was to provide both uber ( without transitive dependency declared on pom ) and thin jars on my repository. So my solution "A" is based on this scenario and currently has the limit that the shaded jar is uploaded 2 times on repository.

  • To provide only the uber jar see "B" solution below
  • For a possible solution for "A" limit see the UPDATE section at the end

A) Provide both thin and uber jars on repository

1- On your source project configure on your something module pom.xml the following maven-shade-plugin as follow:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>2.2</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <finalName>something-uber-${project.version}</finalName>
      </configuration>
    </execution>
  </executions>
</plugin>

Then use the build-helper-maven-plugin plugin to attach the new artifact with "uber" classifier on module:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>build-helper-maven-plugin</artifactId>
  <version>1.2</version>
  <executions>
    <execution>
      <id>attach-artifacts</id>
      <phase>package</phase>
      <goals>
        <goal>attach-artifact</goal>
      </goals>
      <configuration>
        <artifacts>
          <artifact>
            <file>
              ${project.build.directory}/something-uber-${project.version}.jar
            </file>
            <classifier>uber</classifier>
          </artifact>
        </artifacts>
      </configuration>
    </execution>
  </executions>
</plugin>

This will generate as results of the maven install phase the following two jars on target/ directory:

40K something-0.1.0.jar
7M  something-uber-0.1.0.jar

WARN: Executing then the maven deploy phase both jars will be uploaded on repository! The target here should be to upload only the thin jar skipping deploy for shaded jar in order to leave it as a local artifact ( See the UPDATE section at the end for a possible fix )

2- Create then another module on your source project named something-uber adding following dependencies and plugin:

<dependencies>
    <dependency>
        <groupId>com.sample</groupId>
        <artifactId>something</artifactId>
        <version>${project.version}</version>
        <classifier>uber</classifier>
        <exclusions>
            <exclusion>
                <artifactId>*</artifactId>
                <groupId>*</groupId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

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

Note the followings on including the dependency:

  • the classifier should be equals to uber ( the one you've specified attaching new artifact using build-helper-maven-plugin on first module )
  • the exclusions are specified.

Executing at the end the maven deploy phase on this module the shaded jar will be uploaded on repository and you we'll be able to add it as dependency into an external project as follow:

<dependency>
 <groupId>com.sample</groupId>
 <artifactId>something-uber</artifactId>
 <version>0.1.0</version>
</dependency>

B) Provide only uber jar on repository

Starting from solution "A" if you want to avoid to provide the thin jar on repository you should avoid on point 1 to specify finalName on maven-shade-plugin configuration and so avoid also the build-helper-maven-plugin plugin as there isn't a new artifact to attach. Doing this, deploying the module you'll have only the uber jar on target/ as default one ( without classifier ):

7M  something-0.1.0.jar

You should also skip the upload otherwise you'll have also here two fat jars uploaded ( something-0.1.0.jar & something-uber-0.1.0.jar ). To do this add the following plugin on the same module:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-deploy-plugin</artifactId>
    <version>2.8.2</version>
    <configuration>
        <skip>true</skip>
    </configuration>
</plugin>

At the end on point 2 just avoid to specify the classifier on adding the dependency as follow:

<dependencies>
 <dependency>
    <groupId>com.sample</groupId>
    <artifactId>something</artifactId>
    <version>${project.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>*</artifactId>
            <groupId>*</groupId>
        </exclusion>
    </exclusions>
 </dependency>
</dependencies>

UPDATE: Skip first shaded jar upload on A) solution

After searching for a solution for a while without success I decided to fork the maven-deploy-plugin plugin from GitHub and work for a new feature in order to skip shaded jar created on first module adding the plugin configured as follow:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-deploy-plugin</artifactId>
  <version>3.0.0-SNAPSHOT</version>
  <configuration>
    <skipAttachedArtifacts>
      <artifact>
        <groupId>com.sample</groupId>
        <artifactId>something</artifactId>
        <version>${project.version}</version>
        <packaging>jar</packaging>
        <classifier>uber</classifier>
      </artifact>
    </skipAttachedArtifacts>
  </configuration>
</plugin>

Currently using the maven-deploy-plugin plugin all artifacts are excluded from deploy while target here is to exclude only a specific one. On my fork I've introduced the "skipAttachedArtifacts" configuration parameter in order to specify attached artifacts to exclude from deploy.

Here is the link on my forked project on GitHub: https://github.com/gregorycallea/maven-deploy-plugin

Here the link instead to the pull request I've submitted on apache plugin project: https://github.com/apache/maven-deploy-plugin/pull/3