Dynamically change maven dependencies - java

I have a POM file with one dependency on Freemarker.jar. In the library folder there are several versions of the freemarker jar. I am wondering if there is an easier way to update which freemarker jar is being used without having to open the pom and change the name of the jar or having to find the jar and rename it manually. A JComboBox with the different freemarker jars would be the best but I have no idea how to make it change during runtime. I would be fine with having to restart the application as long as all I have to do is change the selection of the combobox and restart.
I have read a few similar questions and I believe it might not be possible.
Here's my dependency:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.19</version>
</dependency>

You can use the exec-maven-plugin to start the application together with a dependency management in maven. The version of the freemarker dependency must be overridable by the command line. For that you can use maven properties.
Then your user can restart the application with a different freemarker version by choosing it through a command line parameter.
For example something like this:
mvn exec:java -D=freemarker.version=2.3.19
But there are 3 limitiations:
Your users need to restart the application
This solution is only possible if the freemarker versions are binary compatible
If the freemarker versions are only source compatible, your users additionally need to re-compile the application before starting it.
If you try this solution you should begin with 2 freemarker version that are very close, e.g. 2.3.19. 2.3.18 and try if they are compatible.
Step 1: Add the freemarker dependency to the dependency management.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
Step 2 Add a default version property for the case that the user does not specify one at the command line.
<properties>
<freemarker.version>2.3.19</freemarker.version>
</properties>
Step 3 Configure the exec-maven-plugin
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.your.full.quallified.MainClass/mainClass>
</configuration>
</plugin>
Step 4 Try to execute it with the default freemarker version
mvn exec:java
Step 5 Try to execute it with another freemarker version
mvn exec:java -D=freemarker.version=2.3.18

I don't think you could use maven for this, since maven is (normally) not used during runtime, only during compile/build. You could change the scope of your dependency to "provided", and then tweak the mechanism you're using to start your application, to add the correct jar to your classpath. However, with more details on how you run your application, it's hard to give more details.
EDIT: changed to the correct scope.

Related

How to change the version of a dependency during test execution to test binary compatibility with maven?

I'm currently working on a dbunit extension library. Thus this library depends on dbunit.
I extracted the version as a maven property.
<properties>
<dbunit.version>2.4.8</dbunit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>${dbunit.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
This allows me to run the maven build with another dbunit version
mvn test -Ddbunit.version=2.7.3
But it also change the dbunit version that the compiler uses. Thus I test source code compatibility to another dbunit version.
The question I want to answer with my maven build is:
Does my library work normal, as specified by the tests, if I use the library that was build against dbunit 2.4.8 with dbunit 2.7.3 at runtime?
I would like to introduce version ranges so that clients of the library can choose which version they want to use, but to do that I want to test which versions work.
The maven-surefire-plugin allows me to add additional classpath entries. But only if I know the path of a lib. I would like to use the GAV (group artifact version) format.
How can I solve this? Do you know another plugin that can handle such cases.

Auditing the licenses of libraries used in a Spring Boot project

I'm tasked to audit the licenses used in a project we are releasing, which is a Spring Boot multi-module Maven application.
The project is structured with a parent pom.xml declaring the Spring boot BOM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
And then in one of the submodules (we have 4), which is the actual Web application, we have configured the spring-boot-maven-plugin with repackage task -- this creates a executable JAR of about 31 megabytes.
Running mvn site on a Spring boot project like this, then opening target/site/index.html and click on "Dependency management", we can see a very big list of dependencies provided by Spring Boot. Now, I'm fairly sure that we only use a one-digit quantity of this list (like 9 or 10 libraries), and I can read on the spring-boot-maven-plugin documentation that this only concerns the "provided" scope, as Spring should be treated as a application container.
On the other hand, I'm using the mvn dependency:analyze and mvn dependency:tree goals to check the effectively used stuff, including transitive dependencies. Most of this stuff is not declared in the pom.xml files, as mentioned we have a parent pom that uses the BOM from Spring-boot and then in the single modules we activate dependencies as needed.
My questions:
1) On the legal side, can I assume that all the stuff shown by mvn site for Spring Boot is not the actual list of dependencies used at compile time? Would it be possible to force out of the JAR anything that we are not using? I tried to configure the spring-boot-manven-plugin by using an exclusion list, but the JAR size did not change.
2) how do I programmatically compile a list of dependencies and the attached licenses? I have the feeling that mvn dependency:analyze is the right path but is there a way to poll the License used by any module listed?

Maven versions plugin, limit versions:update-properties damage

I have a multimodule maven project, where module share dependencies. By share I mean use the same dependencies. However each module declares dependencies itself. To keep sanity (yeah, maven, sanity, I know), and to have all modules using the same version of dependencies, parent pom declares properties with version numbers:
<properties>
<dependency1.version>1.0-SNAPSHOT</dependency1.version>
<dependency2.version>1.1-SNAPSHOT</dependency2.version>
</properties>
and all modules use that like:
<dependency>
<groupId>group</groupId>
<artifactId>dependency1</artifactId>
<version>${dependency1.version}</version>
</dependency>
I'm quite happy with this setup, as it allows me to change dependencies versions in 1 place.
Now I have a bunch of dependencies that I maintain myself. Release of those is automatic and very simple, basically:
mvn release:prepare release:perform -B
now I want to automate further and in the main project I run:
mvn versions:update-properties
(basically I also run: "mvn versions:use-releases" to change usual dependencies if needed, but it's out of the scope of this question).
After this update-properties run, properties in my main projects pom point to releases (which is good). However if my modules use properties to define versions of other dependencies and those projects have newer versions available, those properties are also changed.
Is there any way to limit damage from update-properties? versions:use-release takes includes property, so I can use it only on mine artefacts. Cannot find anything similar for update-properties.
I can revert all poms besides parent one and commit/push only that, but it doesn't seem elegant.
It sounds that you didn't understand the concept of maven.
In such circumstances you should use dependencyManagement in the parent pom like the following:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
...
</dependencies>
</dependencyManagement>
In you modules you just use a dependency like this:
<dependencies>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
...
</dependencies>
The important step is not to define the version. In this case the version will be used which is defined by the dependency management block. So you don't need to define properties etc. and furthermore you have a single point where you can define and change the dependencies in particular the versions.
Apart from that it's possible to limit the properties which will be changed defining it on the command line on the version:update-properties call.

Is there a way to configure the version of the Maven POM from command line?

Is there a way to change the version number without editing the POM?
<groupId>org.example</groupId>
<artifactId>example</artifactId>
<version>1.0.0</version>
We have a CI system where we want to release nightly builds, but without using the -SNAPSHOT solution of Maven, so if 1.0.0 is the current version, we just want to have CI-NIGHTLY-BIULD-20120426.
I suggested this would be possible with something like mvn deploy -Dversion=CI-NIGHTLY-BIULD-20120426, but obviously not. The bad solution would be to let the CI server edit the pom.xml every time, but I think this is very unhandy.
Thank you!
I suggest to use classifier.
<groupId>foo</groupId>
<artifactId>bar</artifactId>
<version>1.0</version>
<properties>
<!-- default classifier is empty -->
<my.project.classifier></my.project.classifier>
</properties>
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<classifier>${my.project.classifier}</classifier>
</configuration>
<executions>...</executions>
</plugin>
</plugins>
</build>
and
mvn package -Dmy.project.classifier=NIGHTLY-2012-04-26_02-30
Maven documentation says about classifier:
classifier: You may occasionally find a fifth element on the
coordinate, and that is the classifier. We will visit the classifier
later, but for now it suffices to know that those kinds of projects
are displayed as groupId:artifactId:packaging:classifier:version.
and
The classifier allows to distinguish artifacts that were built from
the same POM but differ in their content. It is some optional and
arbitrary string that - if present - is appended to the artifact name
just after the version number. As a motivation for this element,
consider for example a project that offers an artifact targeting JRE
1.5 but at the same time also an artifact that still supports JRE 1.4. The first artifact could be equipped with the classifier jdk15 and the
second one with jdk14 such that clients can choose which one to use.
Another common use case for classifiers is the need to attach
secondary artifacts to the project's main artifact. If you browse the
Maven central repository, you will notice that the classifiers sources
and javadoc are used to deploy the project source code and API docs
along with the packaged class files.
I think you could also use versions maven plugin. I find it quite useful for things like this.
You could do it in 2 steps:
set necessary version: mvn versions:set -DnewVersion=CI-NIGHTLY-BIULD-20120426
deploy: mvn deploy
in case you need to revert back the changes, use mvn versions:revert (as Mark suggests)
I highly recommend reading Maven Releases on Steroids (part 2, part 3) by Axel Fontaine. It is great, and I'm quite happy using it.
It not only details how you con do what you ask, but also contains good advice how you can tie your build versions with your CI server.
In a nutshell, here are the main points:
Maven Release is slow, needs to be done faster
You parametarize your project version like
<version>${VERSION_NUMBER}</version>
...
<properties>
...
<VERSION_NUMBER>1.0-SNAPSHOT</VERSION_NUMBER>
...
</properties>
Local builds get that version: 1.0-SNAPSHOT
Release builds are done only from your CI server
In your Jenkins/Hudson project configuration you use
clean deploy scm:tag -DVERSION_NUMBER=${BUILD_NUMBER}
That way you get a new release with each Jenkins build, not only nightly.
You can change the configuration to use
clean deploy scm:tag -DVERSION_NUMBER=1.0.0-CI-NIGHTLY-BIULD-${BUILD_ID}
and you would get versions like 1.0.0-CI-NIGHTLY-BIULD-2012-04-26_12-20-24
You could parameterize the version number as
<groupId>foo</groupId>
<artifactId>bar</artifactId>
<version>${my.project.version}</version>
<properties>
<my.project.version>1.0</my.project.version>
</properties>
and drive the version number from command line using
mvn package -Dmy.project.version=NIGHTLY
Although this is possible, Maven 3 discourages it.

Maven2: use ${basedir} in jar path

I am using an external, proprietary jar in my project. When I hard-code the path as follows in my pom.xml, it works fine:
<dependency>
<groupId>com.foo.bar</groupId>
<artifactId>bar</artifactId>
<version>5.2</version>
<scope>system</scope>
<type>jar</type>
<systemPath>D:\workspace\myproj\external\companyname\lib\proprietary_api.jar</systemPath>
</dependency>
However, when I try to use the ${basedir} variable, maven can't find the jar:
<dependency>
<groupId>com.foo.bar</groupId>
<artifactId>bar</artifactId>
<version>5.2</version>
<scope>system</scope>
<type>jar</type>
<systemPath>${basedir}\external\companyname\lib\proprietary_api.jar</systemPath>
</dependency>
The pom is located in D:\workspace\myproj
This also needs to be cross-platform compatible (dev on Windows, deploy on Linux).
Thanks!
It is wrong to use system scope for your proprietary JARs. You should deploy or install it into the local/central repository.
I'm not sure this will help, but try using forward (/) instead of backward (\) slashes. Also, try running it with mvn -e and mvn -X (the latter will produce a lot of debugging lines) - this might help you pinpoint the problem.
Here's an example:
http://jmonkeyengine.org/groups/contribution-depot-jme3/forum/topic/maven-2-pomxml
of using ${basedir} in the same way you want.
Btw, why don't you mvn install:install-file the dependency instead of using systemPath? See:
http://maven.apache.org/plugins/maven-install-plugin/usage.html
In order to be cross-platform compatible use ${file.separator} instead of the slashes..
so that will automatically convert to OS specified format
To make it work both on windows and linux, you have to start using profiles. In that way, a particular profile will get activated based on the OS and the build will become portable.
In each profile, you can define a property called jarPath (just an example) and refer that property in your dependency.
Maven Profiles Introduction
Look into the OS tag and configuration tag of the profile. Make sure your build are always portable and less manual steps needs to be done.involved.
Use forward slashes in the path.
The ${basedir} placeholder is extrapolated only once per Maven run. If this project is not the topmost project in your project hierarchy, then ${basedir} will be extrapolated to the location of the topmost project (i.e. the project where Maven started), not the current project.

Categories