14

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?

electrotype
  • 8,342
  • 11
  • 59
  • 96

1 Answers1

8

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
Naman
  • 27,789
  • 26
  • 218
  • 353
electrotype
  • 8,342
  • 11
  • 59
  • 96
  • 1
    While this did not solve your problem... This _IS_ an extremely useful undocumented feature. I have resources that needed to be moved to a different folder (not source code, and not in a package structure). Thanks for the write up. – Lucas Sep 01 '16 at 15:31