Because of some incompatibilities between two dependencies, I was forced to make a shaded version of one of my dependencies. This means my project now depends on a local .jar file.
I was previously perfectly fine with just using mvn install-file
to install this .jar to my local repository, before running mvn install
:
mvn org.apache.maven.plugins:maven-install-plugin:2.5.2:install-file -Dfile=lib/my-custom-jar-1.0.0.jar
mvn install
However, my project will now be on an automated build server, who will only do mvn clean install
and nothing else.
By looking for a long while, I have found several solutions, but none are perfect.
I will be writing down the solutions I found as an answer below, but I'm posting this question hoping that somebody has a better idea to solve this problem.
Here's the few solutions I tried, but weren't great for my uses:
1. maven-install-plugin
The idea is to add the install-file goal as part of the install lifecycle by adding this to the pom:
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<file>lib/my-custom-jar-1.0.0.jar</file>
</configuration>
</execution>
</executions>
</plugin>
[...]
<dependency>
<groupId>org.me</groupId>
<artifactId>my-custom-jar</artifactId>
<version>1.0.0</version>
</dependency>
However, even on the very first goal, validate
, Maven will try to resolve dependencies before install-file is run.
I saw the idea of using the clean goal. Annoyingly, this works when you do separate commands (mvn clean && mvn install
) but if you do both in one mvn command (mvn clean install
), Maven will resolve dependencies first. Could there be a solution to this?
2. Multi Module Project
The idea, seen in this Stack Overflow answer, is that you install-file in your parent pom, and add the dependency in your child pom. Maven will only resolve dependencies separately so this should work.
However my project is a single module, and making a fake parent just to solve this problem seems like an over-complication and an ugly hack.
3. System scope with basedir
<dependency>
<groupId>org.me</groupId>
<artifactId>my-custom-jar</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/my-custom-jar-1.0.0.jar</systemPath>
</dependency>
Although this looks like this was made exactly for this kind of situation, the system scope actually expects the dependency to be on every system where you'll run your project, and so it will not be packaged in the .war, making my project non-functional.
4. addjars-maven-plugin
This custom plugin found here includes a .jar in your .war file, then adds it to your pom during compilation.
<plugin>
<groupId>com.googlecode.addjars-maven-plugin</groupId>
<artifactId>addjars-maven-plugin</artifactId>
<version>1.0.5</version>
<executions>
<execution>
<goals>
<goal>add-jars</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/lib</directory>
<includes>
<include>**/my-custom-jar-1.0.0.jar</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
This will work in most normal cases. However, since you don't indicate any dependency to your custom .jar in your actual pom, your IDE will be missing a lot of classes, so you'll need to manually add your custom .jar as an external library.
This still is somewhat hacky and does not work with some special cases (hpi:run for Jenkins debugging for example throws some errors). Plus I preferred that my code did not rely on third-party plugins.
5. In-directory Maven repository
I found this solution after creating this post, and I'm pretty happy with it.
This is pretty much the same result as doing the mvn install-file
command in my question, except you save the result and keep it as part of your project by installing the custom library in a repository located inside your project.
You will need to pre-install the library using this command.
mvn org.apache.maven.plugins:maven-install-plugin:2.5.1:install-file \
-Dfile=lib/cloudfoundry-client-lib-shaded-1.0.3.jar \
-DlocalRepositoryPath=lib
Once that's done your repository is created in the lib folder and you won't need to do this command ever again.
Indicate that you want to use this repository in your pom:
<repository>
<id>Local repository</id>
<url>file://${basedir}/lib</url>
</repository>
[...]
<dependency>
<groupId>org.me</groupId>
<artifactId>my-custom-jar</artifactId>
<version>1.0.0</version>
</dependency>
This solution forces you to commit a bunch of extra folders to your SCM, but that was a manageable drawback for me and I'm satisfied with this.
Do the shade execution in one subproject (call it S), it eats version (a) of your problem dependency, and produces your own G/A/V'd result. Configure shade to produce the main output of the subproject. In other words, S produces group:artifact:version which are all completely different from the coordinates of the thing you are starting from that you need two version of.
In your other subprojects, just declare (S) as the dependency to get the shaded version, and then you can also declare the unshaded other version.
This is all assuming that you renamed the packages when you shaded.
There's no need for install:anything.