Install local jar dependency as part of the lifecy

2019-02-03 07:12发布

问题:

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.

回答1:

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.



回答2:

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.