Category Archives: Review

Articles that describe my experiences with a certain product. Usually giving details on the functionalities of the product and things I liked or disliked about the product.

Maven 3 and the versions dilemma

Like many companies NBIC uses Maven to build  Java projects and manage the dependencies of the projects. Especially in combination with IDEs like Netbeans that understand Maven projects you have a powerful tool to quickly get started with your project and keep your builds in check.

Recently we have started the migration to Maven 3, because some of our builds had been triggering an issue in Maven 2 where the metadata for the plugins lost all version information. However, since then we have seen warnings about missiong versions for the plugins on projects that used to compile without warning.

I started to investigate the problem, but so far haven’t found a satisfactionary solution.

The problem

If you have a fairly simple configuration you might not notice the problem. Consider the following small example. This will build fine without any warnings.

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.example</groupId>
    <artifactId>app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Example App</name>
</project>

However, in many cases you might want to configure some of the compilation parameters. For example if you need to build your project for a specific (older) version of Java. Like in the following example:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.example</groupId>
    <artifactId>app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Example App</name>
    <build>
        <plugins>
            <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

The small change introduced here to specify the target version of Java will give you the warnings below.

[WARNING] Some problems were encountered while building the effective model for net.example:app:pom:0.0.1-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-compiler-plugin is missing. @ line 11, column 12
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.

So, Maven tells us that we should specify the version of the plugin, but what is the best approach? I have played with a couple of approaches, but so far I haven’t found a satisfactory solution.

Approach 1: hardcoded versions

The easy way out seems to be to hardcode the version of the plugin in your pom.xml. Adding the <version> tag to the plugin definition will suffice.

However, while it is not a problem if you just have a simple project, this solution soon becomes a hassle when you have to work with complex projects that consist of dozens of modules or when you have many projects that should all use the same version of the plugin.

There are a couple of approaches that can alleviate this issue.

Approach 2: using properties

Using properties makes it easier to maintain a consistent set of plugin versions, because you can refer to the version by property and only define the version once. It does require some discipline to maintain, because plugins introduced by IDEs like NetBeans will not have their version set by properties, and you have to change the pom.xml file to adjust.

Creating the properties however is fairly easy. With the <properties> tag you can introduce any property by creating a tag named after the property. Afterwards you can refer to it by the name you gave to the tag, like in the following example:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.example</groupId>
    <artifactId>app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Example App</name>
    <properties>
        <maven-compiler-plugin-version>2.3.2</maven-compiler-plugin-version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin-version}</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

If your project has multiple modules, the modules can use the properties defined in the parent project. However, you have to take care that you always introduce the properties in the parent pom.xml.

Approach 3: using a company wide master pom

Using a company wide master pom is a variant on creating a modular project. Each project will have the company wide master pom as parent, and you can introduce the plugins in this master pom file. The main disadvantage is that it will introduce yet another dependency and changes in the master pom might trigger all your projects to be rebuild in your CI environment.

Automating version management

The versions plugin is a way to automate the management of versions. It takes the hard labour out of finding out about the latest versions of the plugins and updating the pom.xml. It works with hard-coded versions as well as parameterized versions using properties. It can be used both for the build dependencies as well as the plugin dependencies. It is however dangerous to just blindly update your versions, because you might upgrade to incompatible newer releases or stumble upon the issue that an illegal version of commons logging was pushed as shown below:

[INFO] Updated ${commons.logging.version} from 1.1.1 to 99.0-does-not-exist

Conclusions

There are several ways to remove the warning from Maven 3, however none of the offers the definitive solution to the problem of managing the versions in your maven project. Using properties in the parent pom.xml seems to give the best trade-off between the requirement of encoding your versions and management of your versions. The versions plugin seems to me to be a very dangerous plugin. It is a convenient tool to set the versions at the start of your project, but for updating a complex project it misses the required granularity in operations.