A supplier has given a couple of jars to add to our java project which uses Spring Boot and Maven. Those files are not published in any Maven repository and we do not have one at work yet.
Development phase
I am not very knowledgeable of Spring Boot and Maven and had trouble adding those jars to share with my colleague. Because of the lack of a Maven repository, I just wanted to add the jars into our repository in a libs
directory, at the same level than the pom.xml
.
I did not want to use the method described on the Maven website because it would force every developer to execute those commands.
After some Googling, I got the following:
<dependency>
<groupId>supplier</groupId>
<artifactId>jar1</artifactId>
<version>8.6.2.2</version>
<scope>system</scope>
<systemPath>${basedir}/libs/supplier/jar1-8.6.2.2.jar</systemPath>
</dependency>
<dependency>
<groupId>supplier</groupId>
<artifactId>jar2</artifactId>
<version>8.6.2.2</version>
<scope>system</scope>
<systemPath>${basedir}/libs/supplier/jar2-8.6.2.2.jar</systemPath>
</dependency>
However, system scope are deprecated. Nonetheless, it did the job for a first version and allowed me to continue with developing the feature.
Release phase
After the feature was developped and tested locally, it was time to release. Creating the jar and deploying to our staging environment, and … the service did not start, there was a cyclic dependency in the initialized beans (excerpt of simplified log):
βββββββ
| subscriberService defined in URL [jar:file:xxx.jar!/BOOT-INF/classes!/SubscriberService.class]
β β
| emailSenderService (field private SiteService EmailSenderService.siteService)
β β
| siteService (field private SubscriberService SiteService.subscriberService)
βββββββ
Testing in development mode again started the application correctly, but running the generated package locally, I also had the same cyclic dependency. There was such a cycle in the code base, but it was still running fine locally. Anyway, we tried fixing the cycle, run tests, all good, package, run the package and … NoClassDefFoundError
…
The two jars I mentioned earlier were not packaged in the final package. Maven does not add system scope packages by default. There were several ways to solve this quickly because we had to release.
Bundle system scope dependencies
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-Maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
This works fine, but is deprecated, it did the job for this release as it was the quickest.
Create a local Maven repository in the jar directory
As explained here: https://gist.github.com/timmolderez/92bea7cc90201cd3273a07cf21d119eb
Define the directory where the jars are located into a Maven repository and add it to our pom.xml
. This is just the equivalent of taking the result of the following maven command in the local repository:
$ mvn install:install-file -Dfile=${jarfile} -DartifactId=${artifactId} -Dversion=${version} -Dpackaging=jar -DgroupId=${groupId}
It seems only ok if there are a few files that do not change.
Create a remote Maven repository
The long term and good way if more external jars are to be added is to create a remote Maven repository so that developers can share packages together. We do not publish individual libs and just have one monorepo for the java code, that’s why a Maven repository was not needed before. However, it adds another external service to maintain.
Install local jars
Dependencies aside, there are several ways to add a local jar to the final package. However, I find this method redundant with declaring dependencies and not very clean.
addjars-Maven-plugin
Using a specific plugin: https://code.google.com/archive/p/addjars-Maven-plugin/
<plugin>
<groupId>com.googlecode.addjars-Maven-plugin</groupId>
<artifactId>addjars-Maven-plugin</artifactId>
<version>1.0.2</version>
<executions>
<execution>
<goals>
<goal>add-jars</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/lib/foo</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
maven-install-plugin
The solution is described here https://www.baeldung.com/install-local-jar-with-maven/ for the validation
phase and here for multiple files during the initialize
phase. They both use the standard maven install plugin.
Excerpt:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<executions>
<execution>
<id>inst_1</id>
<phase>initialize</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<!-- config for file 1 -->
</configuration>
</execution>
<execution>
<id>inst_2</id>
<phase>initialize</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<!-- config for file 2 -->
</configuration>
</execution>
<!-- execution file 3... -->
</executions>
</plugin>
Conclusion
Still bitten by packaging software woes.
I just included the system scope dependencies to release quicky during the development phase, not expecting it would blow up during release and packaging. Because we do not require a remote repository for our own needs and two jars, I will settle on the local maven repository for the moment.
I found the following page describing how a maven repository works very useful to understand the layout and the role of each file.