Saturday, February 23, 2008

Recipe for Building a Maven Plugin

Recently I've become a maven convert. One of the hardest parts of making the switch from Ant was the fact that I couldn't implement custom logic directly in pom.xml the way I could in an Ant build file. I had to make the mental shift to writing custom plugins instead, which at first glance seemed a lot more complicated than writing Ant targets.

This weekend I finally sat down and worked through the process of writing a custom plugin. It's actually pretty easy, and the end result is a nice reusable component.

I found that I had to refer to both the "Better Builds with Maven" book as well as the plugin developers' guide on the maven web site to get the gist of writing a plugin. Here is the general recipe I came up with.

A mojo implements a maven goal. If you want to be able to type: mvn my:mygoal

and have something happen, then you need to implement a single mojo.

To do this, first use maven to generate the boilerplate for a plugin project:


mvn archetype:create -DgroupId=com.mycompany.maven.plugins \
  -DartifactId=maven-my-plugin \
  -DarchetypeArtifactId=maven-archetype-mojo

Edit pom.xml:

  • Change name and artifact ID
  • Set build source and target to Java 5:
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>very
            <configuration>
              <source>1.5</source>
              <target>1.5</target>
            </configuration>
          </plugin>
        </plugins>
      </build>
    
  • Add dependencies

Rename the generated MyMojo.java file to the desired name of your mojo, and edit the file.

First change the class-level mojo annotations. For an explicitly invoked task (not automatically bound to a lifecycle phase), remove the @phase annotation and only specify the @goal annotation.

Update the field-level annotations. Use @parameter to set a field value based on the plugin configuration.

Finish implementing and testing mojo.

Install the finished plugin to your local repository using mvn install. If you look in target/classes/META-INF/maven/plugin.xml, you'll see that maven has extracted all the annotations from your mojo into a plugin descriptor file. Maven uses this file at runtime to map configuration properties onto mojo fields.

Next switch over to a module where you want to use your newly implemented goal. Add your new plugin to the module's pom.xml:


  <build>
    <plugins>
      <plugin>
        <groupId>com.mycompany.maven.plugins</groupId>
        <artifactId>maven-my-plugin</artifactId>
        <executions>
          <execution>
            <id>mygoal</id>
            <goals>
              <goal>mygoal</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <myfield>myvalue</myfield>
        </configuration>
      </plugin>
    </plugins>
  </build>

Now you can execute the goal: mvn my:mygoal

Certainly not as easy as adding an Ant target, but the nice thing about maven is that you rarely have implement custom build logic if you just follow the maven conventions.