Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maven - Is there a way to easily create an 'uber' jar with all the dependencies relocated?

We need to build a jar, using Maven, in a way that all its dependencies are included, but also that all those dependencies are renamed (relocated).

Let's say our own packages all start with com.mycompagny.projectx.*". We want the project dependencies to have their package renamed to starts with "embedded", but not our own classes.

Using maven-shade-plugin for example, I'm not able to achieve this :

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <shadedArtifactAttached>true</shadedArtifactAttached>
        <createDependencyReducedPom>true</createDependencyReducedPom>
        <artifactSet>
            <includes>
                 <include>*.*</include>
            </includes>
        </artifactSet>
        <relocations>
            <relocation>
                <pattern>*</pattern>
                <shadedPattern>embedded.</shadedPattern>
                <excludes>
                    <exclude>com.mycompagny.projectx.*</exclude>
                </excludes>
            </relocation>
        </relocations>
    </configuration> 
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Here <pattern>*</pattern> is not valid. Also, if I use <pattern></pattern> (empty string), then everything is relocated to the "embedded" package, even the resources (the "META-INF" directory too)! Of course we want the resources to stay at the root of the jar.

I guess we could create multiple <relocation> elements, one for each package of the dependencies, but that would be a lot of work : <relocation>com</relocation>, <relocation>net</relocation>, <relocation>javax</relocation>, etc.

Any idea on how to easily relocate all dependencies inside the uber jar, without touching our own classes, resources and the "META-INF" directory?

like image 553
electrotype Avatar asked Jan 30 '15 17:01

electrotype


People also ask

Which build plugin allows you to create a fat JAR file that contains all the dependencies in the final JAR file?

Maven provides other plugins using which we can generate the jar with all its dependencies which is usually called FatJar, which is used as an executable Jar file.

What is relocation in Maven?

Sometimes it is necessary to relocate artifacts in the repository. One example of that is when a project moves from one groupId to a different groupId. Making changes to the repository can have far reaching consequences. So it is best to get it right the first time, hence this guide.


1 Answers

UPDATE : This solution doesn't really work, please read to the end.

I found a solution by looking at the source code of maven-shade-plugin! It doesn't seem to be documented anywhere, but there is a <rawString> parameter that you can add to a <relocation> element so it consideres the <pattern> and <shadedPattern> as regular expression patterns and not as package/file names.

The maven-shade-plugin code then uses something like :

path.replaceAll(pattern, shadedPattern)

To deal with those patterns.

Example :

<relocation>
    <pattern>^([^/]*\.properties)$</pattern>
    <shadedPattern>embedded/$1</shadedPattern>
    <rawString>true</rawString>
</relocation>

This is a dummy example which relocates all .properties files which are in the root. Using this technique, it will be possible to control exactly what is relocated and how, I'm pretty sure.

Here is a better example, which does what I need (still some testing to do though):

<relocation>
    <pattern>^(?!(com/mycompagny/|META-INF))(.*/.*)$</pattern>
    <shadedPattern>embedded/$2</shadedPattern>
    <rawString>true</rawString>
</relocation>

UPDATE : Sadly, this last pattern means that everything used will be renamed, except "com.mycompagny" and the META-INF folder. Problem is, things like java.lang.Object will be renamed! And when the code is ran, exceptions like such will be throwned :

java.lang.ClassNotFoundException: embedded.java.lang.Object
like image 120
electrotype Avatar answered Oct 04 '22 00:10

electrotype