Company Super POM – A Maven Practice

Soon after the initial install of Maven you will hopefully like it and use it on a bunch of projects. You will notice that there are a bunch of things in the POM that are the same from project to project. Normally that is where the concept of a company or organisation POM appears. On the other hand internally Maven always uses the Super POM as well. Once multiple projects are involved things quickly get confusing, so lets look at this a bit closer and then get to my hybrid approach of a Company Super POM.

But first lets clear up some of the confusion around the terms. As well explained in the great book Maven: The Definitive Guide the Super POM is the Maven internal setup of all the parameters used by Maven by convention. Among other things these include paths and versions of the most common plugins.

A company POM on the other hand contains all settings and parameters that are shared across a whole company. Most of the time this means multiple projects, many developers, different scm paths and so on. In many cases it also means many more maven plugins and their versions as well as many dependencies in their different versions. Management of such a centralized POM can become difficult when version upgrades of libraries become an issue. E.g. one project might need the latest and greatest, while another one actually can not upgrade to that version due to the large refactoring overhead. This can lead to difficult situations and in my opinion highlights a problem with the company POM approach. Namely it tries to do too many things.

Hence it seems quite reasonable to extract all the Maven specific things from the company POM and we have a Company Super POM. We can then slot the the new POM into the inheritance tree between the the super POM of the Maven install and a newer lighter and more focused company POM and its children in the form of project parent POMs. This will then act as the central place for settings related to Maven across the whole company.

Reason this makes sense are:

  • most of the time there are only a few people (or one champion) responsible for all things Maven that bother tracking new versions and issues with existing versions of Maven and the host of plugins
  • it makes sense to standardize on one version of Maven across the company, even if the projects using it vary widely and have large differences with regards to libraries used and more, they could even work on different platforms thanks to the variety of Maven plugins. E.g. there are plugins for all things Java also for Flex or Android
  • Confused. No problem. Lets look at it all in more detail and create our mosabuam company super pom and then use it. First lets create the Company Super POM. Since it is a normal top level POM the header will just look like this:

    <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/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.mosabuam.maven</groupId>
        <artifactId>super-pom</artifactId>
        <packaging>pom</packaging>
        <version>0.1</version>
        <name>Mosabuam Maven Super POM</name>
    

    Notice how the packaging is just pom. Next we are going to manage our plugin versions. This is done with pluginManagement like this

    ...
        <build>
            <pluginManagement>
                <plugins>
                    <plugin>
                        <groupId>com.agilejava.docbkx</groupId>
                        <artifactId>docbkx-maven-plugin</artifactId>
                        <version>2.0.9</version>
                    </plugin>
                    <plugin>
                        <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                        <artifactId>maven-android-plugin</artifactId>
                        <version>2.2.0</version>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-assembly-plugin</artifactId>
                        <version>2.2-beta-4</version>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-changelog-plugin</artifactId>
                        <version>2.1</version>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-checkstyle-plugin</artifactId>
                        <version>2.3</version>
                    </plugin>
    ...
    

    Depending on the plugins used in your various builds this list can get quite long. Ideally you would have all plugin versions locked down and defined in here. Luckily with Maven 3 (which you would have to build yourself at the moment or get from the build grid) you will actually get a warning if some plugin version is undefined.


    [WARNING]
    [WARNING] Some problems were encountered while building the effective model for com.jayway.maven.plugins.android.generation2:maven-android-plugin:maven-plugin:2.2.1-SNAPSHOT
    [WARNING] 'build.plugins.plugin.version' is missing for org.apache.maven.plugins:maven-scm-plugin @ com.jayway.maven.plugins.android.generation2:maven-android-plugin:2.2.1-SNAPSHOT, /media/sdc1/java/dev/maven-android-plugin/pom.xml
    [WARNING] 'reporting.plugins.plugin.version' is missing for org.apache.maven.plugins:maven-plugin-plugin @ com.jayway.maven.plugins.android.generation2:maven-android-plugin:2.2.1-SNAPSHOT, /media/sdc1/java/dev/maven-android-plugin/pom.xml
    [WARNING]
    [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
    [WARNING]
    [WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
    [WARNING]

    The reason you want all version locked down is that it will allow you to consciously upgrade and ensure things don’t regress in your builds causing havoc for the development team (especially those not so deeply into Maven..).

    Some plugins like in my case the gmaven plugin actually require other dependencies declared and ideally you want to keep all the versions of the plugin and the dependencies in sync. You can use properties and the dependencyManagement section for that:

    ...
        <properties>
            <gmaven.version>1.0</gmaven.version>
            <maven.version>2.2.1</maven.version>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.codehaus.groovy.maven</groupId>
                    <artifactId>gmaven-mojo</artifactId>
                    <version>${gmaven.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.codehaus.groovy.maven.runtime</groupId>
                    <artifactId>gmaven-runtime-1.6</artifactId>
                    <version>${gmaven.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    

    You might have noticed there there is also a maven.version property. This is used to enforce that everybody uses the correct version of Maven with the enforcer plugin:

            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-enforcer-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>enforce-maven</id>
                            <goals>
                                <goal>enforce</goal>
                            </goals>
                            <configuration>
                                <rules>
                                    <requireMavenVersion>
                                        <version>[${maven.version},)</version>
                                        <message>Check for Maven version &gt;=${maven.version} failed. Update your Maven install.</message>
                                    </requireMavenVersion>
                                </rules>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <!--
                    the versions plugin can be used to check for new plugin versions like this:
                    mvn versions:display-plugin-updates
                 -->
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>versions-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    

    The versions plugin as configured above allows you to easily check for new versions with
    mvn versions:display-plugin-updates. With all that slapped together you are pretty much ready to go. Just put the pom file in a folder for the project e.g

    mosabuam-super-pom/pom.xml

    and run

    mvn clean install

    in the folder. This will put the pom into your local .m2 repository and it will be ready to be used from any project. To do that you simply declare it as the parent of your project like this

        <parent>
            <groupId>com.mosabuam.maven</groupId>
            <artifactId>super-pom</artifactId>
            <version>0.1</version>
        </parent>
        <groupId>com.mosabuam.example</groupId>
        <artifactId>example</artifactId>
        <version>1.1</version>
    

    Then you can remove all the versions from any plugin usage in your project so that e.g. an invocation of the maven-android-plugin could look like this

        
    <build>
            <plugins>
                <plugin>
                    <groupId>com.jayway.maven.plugins.android.generation2</groupId>
                    <artifactId>maven-android-plugin</artifactId>
                    <configuration>
                        <sdk>
                            <path>${env.ANDROID_HOME}</path>
                            <platform>1.5</platform>
                        </sdk>
                        <deleteConflictingFiles>true</deleteConflictingFiles>
                    </configuration>
                    <extensions>true</extensions>
                </plugin>
    

    Now all you have to do for a version upgrade of a plugin across all your projects is to update the version in the company super pom and update its version to e.g. 0.2

        <groupId>com.mosabuam.maven</groupId>
        <artifactId>super-pom</artifactId>
        <packaging>pom</packaging>
        <version>0.2</version>
        <name>Mosabuam Maven Super POM</name>
    

    and reference the new version number in the project POM. In a development team you would add a bit of configuration for your internal Nexus server and the version control system used along those lines:

        <scm>
            <connection>scm:svn:http://mosabuamsvn/repo/super-pom/trunk</connection>
            <developerConnection>scm:svn:http://mosabuamsvn/repo/super-pom/trunk</developerConnection>
        </scm>
        <distributionManagement>
            <repository>
                <id>nexus</id>
                <name>Mosabuam Nexus</name>
                <url>http://nexusserver:8080/nexus/content/repositories/releases</url>
            </repository>
        </distributionManagement>
    

    With the passwords in your settings.xml you can then use the release plugin to cut a new release version easily as well as deploy it to Nexus, so it is available to all your project teams.

    mvn clean install
    mvn release:prepare
    mvn release:perform

    With this approach you have all your maven related plugin versions fixed and you still have the freedom to have separate company POMs or project parent POMs that use different version of the same libraries and a whole slew of other settings. Let me know what you think.

6 thoughts on “Company Super POM – A Maven Practice

  1. Great post Manfred!
    This actually applies just as much for individuals using maven as it does companies. Just so long as the use of the pom doesn’t impose behaviour on the inheritors, it’s great to have a way to keep your entire maven tool stack in sync.
    As far as the release strategy goes, it seems pretty limited to internal usage, unless of course a company is prepared to release a company pom publicly along with any maven modules they release publicly.

  2. I am contemplating putting my full mosabuam super pom on a nexus instance on the mosabuam/vijug vps. Not sure if there is demand but it would also make it easier for me. Stay tuned.

  3. If you have structured SCM and CI paths based on each projects artifact-id, you could even configure all SCM and CI settings in the super-pom using constructions like these:

    hudson
    http://:8080/hudson/job/${artifactId}/

  4. You could do that but it sort of mixes responsibilities. The company super pom as I suggest it here does not contain project specific stuff. Only Maven specific settings. The project specific ones would go in a project parent or project super pom..

Comments are closed.