What's in Maven 3.0 for users?


December 22, 2010 By Bentmann Benjamin

Asking the Maven issue tracker for all the changes or fixes that contribute to our freshly released Maven 3.0, one ends up with about 420 issues. While this is a rather large number, most of these issues deal with regressions we encountered and fixed during refactoring of the internals. But those issues are uninteresting to users that consider upgrading from Maven 2.x and want to know what’s the delta to 3.0.

The primary source of information for this delta are the compatibility notes and the plugin compatibility matrix.  These documents focus on changes that could negatively affect existing builds due to stricter behavior of Maven 3.0, but we also managed to implement a few general improvements here and there.

The support for parallel builds is one of the major improvements in Maven 3.0, thanks to Kristian. For more on this, visit the wiki page.

The POM’s <build> section allows one to define a <defaultGoal> such that a simple invocation of mvn does something useful with the project, for instance running the install phase. In Maven 2.x, only a single default goal could be specified. With Maven 3.0, you can specify multiple goals, each separated by whitespace like <defaultGoal>clean install site</defaultGoal>.

Note that the <defaultGoal> element still only gives zero or more goals, it cannot be used to specify profiles or other arguments one can pass to the command line. The extended format of the <defaultGoal> element is still compatible with Maven 2.x to a certain degree. Most importantly, a POM having multiple default goals can be successfully consumed by Maven 2.x during dependency resolution. And as long as one doesn’t ask Maven 2.x to run the default goal, it’s even possible to still build such a project with Maven 2.x.

One way to activate a profile is to check for the presence/absence of a file. In Maven 2.x, this can get rather frustrating when trying to use a relative path for the activator because here a relative path is resolved against the user’s current working directory. But what you usually want is to resolve the path against the base directory of the currently built project such that the profile can be defined in a parent POM, and that it still works properly across all modules of a reactor build.  We’re happy to announce that it finally works this way in Maven 3.0.  That is, you can use <missing>src/main/marker.txt</missing> or more explicitly <exists>${basedir}/src/main/marker.txt</exists> to point at files within the project.

It’s worth mentioning that only the expression ${basedir}, and not its close relative ${project.basedir}, is supported for this kind of profile activation. The reasoning behind this subtle restriction is to avoid the erroneous generalization that one could use any expression of the form ${project.*} for profile activation. Only system properties, environment variables ${env.*} and ${basedir} are valid within the <file> profile activator. Tip: Support for the ${basedir} expression in this context is new to Maven 3.0 so if you have the hopefully rare need to configure plugins differently when building a project with both Maven 2.x and 3.x, you can use the the condition <exists>${basedir</exists>}} to detect Maven 3 and create a dedicated profile.

There’s another improvement regarding profile activation based on properties. So far, profiles could only be activated by system properties. In Maven 3, profiles in the POM can now also be activated from properties specified in active profiles of the settings.xml. In more detail, if your settings.xml contains a snippet like:

<settings>
  ...
  <profiles>
    <profile>
      <id>development</id>
      <properties>
        <someName>someValue</someName>
      </properties>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>development</activeProfile>
  </activeProfiles>
</settings>

The property someName can be used to activate this POM profile:

<project>
  ...
  <profiles>
    <profile>
      <id>test</id>
      <activation>
        <property>
          <name>someName</name>
        </property>
      </activation>
    </profile>
  </profiles>
</project>

In case of conflicts, system properties specified on the command line take precedence over the properties from the settings. As a final clarification, this feature merely allows profiles from the settings to activate profiles in the POM. Profiles within the settings cannot activate each other and profiles in the POM also cannot activate each other.

One of the other major improvements in Maven 3.0 is the resolution of inter-module dependencies from the current reactor build. In the past, it has been quite frustrating for users to find out that mvn install works but mvn verify sometimes fails to resolve artifacts that have just been built by a previous module, in particular when releasing a new version of their projects.

Likewise, it’s hard to understand why an execution of maven-dependency-plugin:copy-dependencies succeeds to resolve artifacts from the reactor while the similar maven-dependency-plugin:copy goals fails. As part of all the refactoring in Maven 3, a dependency resolution has been reworked to consistently check the reactor output. Apparently, the reactor output depends on the lifecycle phases that a project has completed. So if you invoke mvn compile or mvn test on a multi-module project, the loose class files from target/classes and target/test-classes, respectively, are used to create the required class path. As soon as the actual artifact has been assembled which usually happens during the package phase, dependency resolution will use this file. Last but not least, dependencies using version ranges can now be resolved from the reactor, too.

The last improvement to highlight is the improved class loader hierarchy in Maven 3.0. Maintainers of multi-module projects will likely remember the difficulty using different versions of a plugin in different modules. Trying to use the same plugin version but with different project-level plugin dependencies is another yet related pain point in Maven 2.x that basically reuses the same class loader for a given pluginGroupId:pluginArtifactId coordinate.

The end result were builds that succeeded when invoked on a single module but failed within the reactor or vice versa. The maven-antrun-plugin is probably the most common plugin that suffered from this shortcoming of Maven.  With Maven 3.0 users will find that the new release is capable of distinguishing those differences in plugin use such that modules always build the same, regardless whether built in isolation or within a reactor.

  • Yann Albou

    Regarding profile activation, a feature that would be very useful is activation through the project type: if my project is an EAR then I want some default plugins to be activated…
    Do you know if it exits a such feature or if it is planned ?

  • Geoffrey De Smet

    Good article, except for: The ${basedir} part is not clear.

    Let’s say I have project C (dir /a/b/c/) with a parent B (dir /a/b/) with a parent A (dir /a/).
    If I run a build of C (in dir /a/b/c/) in mvn 3: what does ${basedir} point to?
    If I run a build of B (in dir /a/b/) including building module C in mvn 3: what does ${basedir} point to?
    If I run a build of A (in dir /a) including building module B, C in mvn 3: what does ${basedir} point to?