Creating a Custom Build Extension for Maven 3.0

In the process of testing the Maven 3.0 release that’s being voted on, I wanted to try out the additional extension capability, and have a little fun with it in the process.

The result is an old friend of mine:

[INFO] Scanning for projects...
[INFO]  __  __
[INFO] |  \/  |__ _Apache__ ___
[INFO] | |\/| / _` \ V / -_) ' \  ~ intelligent projects ~
[INFO] |_|  |_\__,_|\_/\___|_||_|  v. 3.0
[INFO] 
[INFO] --------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] retro-example
[INFO] retro-example-1
[INFO] retro-example-2
[INFO]                             

This is a trivial example of course, but it shows that you can jump into the startup process much earlier than in the past. This is useful for those that want to extend Maven in a consistent way for a given project or type of project. I believe it is already in use by Tycho, and is a likely mechanism to simplify and enhance NPanday in the future.

While plugins and extensions could already offer additional components, it is now possible load components from within a project that can be set up before the build starts at all. The feature would most often be used to perform additional validation or processing of an entire set of projects in the reactor before they are executed. It is possible that it might also be used to adjust settings, execution properties or dependency resolution – though bearing in mind that these may already have been used to load the projects.

The above example makes use of the afterProjectsRead method (the whole project is in Subversion):

package org.apache.maven.examples.retro;

import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.RuntimeInformation;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;

@Component( role = AbstractMavenLifecycleParticipant.class, hint = "retro" )
public class RetroMavenExtension
  extends AbstractMavenLifecycleParticipant
{
  @Requirement
  private Logger logger;

  @Requirement
  private RuntimeInformation runtime;

  public void afterProjectsRead( MavenSession session ) {
    logger.info( " __  __" );
    logger.info( "|  \\/  |__ _Apache__ ___" );
    logger.info( "| |\\/| / _` \\ V / -_) ' \\  ~ intelligent projects ~" );
    logger.info( "|_|  |_\\__,_|\\_/\\___|_||_|  v. " + runtime.getApplicationVersion() );
    logger.info( "" );
  }
}

There are a couple of things to note here, particularly if you are familiar with writing components for Maven. Firstly, there are now some real annotations to use instead of the Javadoc-based version (though both would continue to work). This example still uses the Plexus annotations and the generated descriptor from the corresponding POM, though in the future it could use the JSR-330 equivalents.

Obtaining a logger is much easier than before, using a simple @Requirement to inject it instead of the LogEnabled interface or abstract class. Other requirements (in this case, the legacy RuntimeInformation class) continue to be injected as they have before.

The rest is self explanatory – based on the “role” and the abstract class, the method is called by the Maven core after the projects have been assembled but before they have been sorted and executed. We just output the ASCII art, however the method has the MavenSession object available to it to obtain the projects, as well as execution properties, repository access and settings.

To use the extension in a project, the following would need to appear somewhere in the POM hierarchy:

<build>
  <extensions>
    <extension>
      <groupId>org.apache.maven.examples</groupId>
      <artifactId>retro-maven-extension</artifactId>
      <version>1.0-SNAPSHOT</version>
    </extension>
  </extensions>
</build>

It’s worth noting that Maven 3.0’s extension handling and classloading is significantly better than Maven 2.x – you can now feel relatively free to use extensions and plugin dependencies in a multi-module project without conflicts.

That’s it! Every Maven command on a project with the extension will carry the banner lost for the last 5 years.

4 responses to “Creating a Custom Build Extension for Maven 3.0

  1. can one write an extension for anything other than AbstractMavenLifecycleParticipant.
    ?

    • You can extend most of Maven’s classes, though with some caution about relying on internal implementation details that might change. However, since much of that is already accessible either through the plugins, or plugin extensions – this mechanism is really just intended to provide an interface to the parts that need to be modified before plugins can be used, like the lifecycle participant.

  2. I set everything right and I’m able to use the afterProjectsRead().

    But I would like to use the afterSessionStart(), because I need to inject userProperties BEFORE the dependencies are resolved in the pom.xml.

    I’m using mvn 3.2.5 core.
    In the DefaultMaven.class,
    method private MavenExecutionResult doExecute( MavenExecutionRequest request )

    there is this code :

    for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.emptyList() ) )
    {
    listener.afterSessionStart( session );
    }

    the getLifecycleParticipants() calls
    container.lookupList( AbstractMavenLifecycleParticipant.class )

    but cannot find my extension, which is declared in the components.xml :

    org.apache.maven.AbstractMavenLifecycleParticipant
    default
    my.plugin.MytoolsExtension

    false

    org.codehaus.plexus.logging.Logger

    logger

    What do I need to do so my extension is reconized by the container and calls the afterSessionStart() method ??

  3. @Marco Lessard Could it be because of what it says here, https://maven.apache.org/examples/maven-3-lifecycle-extensions.html? Search for “NOTE: if you use the build extension mechanism, the method afterSessionStart won’t be called since the extension is loaded later in the build”.

Leave a comment