
You can write a Mojo in Java, or you can write a Mojo in an alternative language. Maven has support for a number of implementation languages, and this chapter is going show you how to create plugins in three languages: Groovy, Ant, and Ruby plugins.
Ant isn't a language as much as it is a build tool which allows you
to describe a build as a set of tasks grouped into build targets. Ant then
allows you to declare dependencies between build targets; for example, in
Ant you are essentially creating your own lifecycle. An Ant
build.xml might have an install target which depends
on a test target which depends on a compile target. Ant is something of a
ancestor to Maven, it was the ubiquitous procedural build tool that almost
every project used before Maven introduced the concept of wide-scale
reusability of common build plugins and the concept of a universal
lifecycle.
While Maven is an improvement on Ant, Ant can still be useful when describing parts of the build process. Ant provides a set of tasks which can come in handy when you need to perform file operations or XSLT transformations or any other operation you could think of. There is a large library of available Ant tasks for everything from running JUnit tests to transforming XML to copying files to a remote server using SCP. An overview of available Ant tasks can be found online in the Apache Ant Manual. You can use these tasks as a low-level build customization language, and you can also write a Maven plugin where, instead of a Mojo written in Java, you can pass parameters to a Mojo which is an Ant build target.
To create a Maven plugin using Ant, you will need to have a
pom.xml and a single Mojo implemented in Ant. To get
started, create a project directory named firstant-maven-plugin. Place the
following pom.xml in this directory.
Example 18.1. POM for an Ant Maven Plugin
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook.plugins</groupId>
<artifactId>firstant-maven-plugin</artifactId>
<name>Example Ant Mojo - firstant-maven-plugin</name>
<packaging>maven-plugin</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-script-ant</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>2.4</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-tools-ant</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>Next, you will need to create your Ant Mojo. An Ant mojo consists of
two parts: the Ant tasks in an XML file, and a file
which supplies Mojo descriptor information. The Ant plugin tools are going
to look for both of these files in
${basedir}/src/main/scripts. One file will be named
echo.build.xml and it will contain the Ant
XML.
Example 18.2. Echo Ant Mojo
<project> <target name="echotarget"> <echo>${message}</echo> </target> </project>
The other file will describe the Echo Ant Mojo and will be in the
echo.mojos.xml file also in
${basedir}/src/main/scripts.
Example 18.3. Echo Ant Mojo Descriptor
<pluginMetadata>
<mojos>
<mojo>
<goal>echo</goal>
<call>echotarget</call>
<description>Echos a Message</description>
<parameters>
<parameter>
<name>message</name>
<property>message</property>
<required>false</required>
<expression>${message}</expression>
<type>java.lang.Object</type>
<defaultValue>Hello Maven World</defaultValue>
<description>Prints a message</description>
</parameter>
</parameters>
</mojo>
</mojos>
</pluginMetadata>This echo.mojos.xml file configures the Mojo
descriptor for this plugin. It supplies the goal name "echo", and it tells
Maven what Ant task to call in the call element. In addition to
configuring the description, this XML file configures
the message parameter to use the expression ${message}
and to have a default value of "Hello Maven World."
If you've configured your plugin groups in
~/.m2/settings.xml to include
org.sonatype.mavenbook.plugins, you can install this Ant
plugin by executing the following command at the command-line:
$ mvn install
[INFO] ------------------------------------------------------------------------
[INFO] Building Example Ant Mojo - firstant-maven-plugin
[INFO] task-segment: [install]
[INFO] ------------------------------------------------------------------------
[INFO] [plugin:descriptor]
[INFO] Using 3 extractors.
[INFO] Applying extractor for language: java
[INFO] Extractor for language: java found 0 mojo descriptors.
[INFO] Applying extractor for language: bsh
[INFO] Extractor for language: bsh found 0 mojo descriptors.
[INFO] Applying extractor for language: ant
[INFO] Extractor for language: ant found 1 mojo descriptors.
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
Note that the plugin:descriptor goal found a
single Ant mojo descriptor. To run this goal, you would execute the
following command-line:
$ mvn firstant:echo
...
[INFO] [firstant:echo]
echotarget:
[echo] Hello Maven World
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
The echo goal executed and printed out the
default value of the message parameter. If you are used
to Apache Ant build scripts, you will notice that Ant prints out the name
of the target executed and then adds a logging prefix to the output of the
echo Ant task.
Ruby is an object-oriented scripting language which provides a rich set of facilities for meta-programming and reflection. Ruby's reliance on closures and blocks make for a programming style that is both compact and powerful. Although Ruby has been around since 1993, most people came to know Ruby after it was made popular by a Ruby-based web framework known as Ruby on Rails. JRuby is a Ruby interpreter written in Java. For more information about the Ruby language, see: http://www.ruby-lang.org/, and for more information about JRuby, see: http://jruby.codehaus.org/.
To create a Maven plugin using JRuby, you will need to have a
pom.xml and a single Mojo implemented in Ruby. To
get started, create a project directory named
firstruby-maven-plugin. Place the following
pom.xml in this directory.
Example 18.4. POM for a JRuby Maven Plugin
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook.plugins</groupId>
<artifactId>firstruby-maven-plugin</artifactId>
<name>Example Ruby Mojo - firstruby-maven-plugin</name>
<packaging>maven-plugin</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jruby-maven-plugin</artifactId>
<version>1.0-beta-4</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>2.4</version>
<dependencies>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jruby-maven-plugin</artifactId>
<version>1.0-beta-4</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>Next, you will need to create a Mojo implemented in Ruby. Maven is
going to look for a Ruby Mojo in
${basedir}/src/main/scripts. Put the following Ruby
class in
${basedir}/src/main/scripts/echo.rb.
Example 18.5. The Echo Ruby Mojo
# Prints a message
# @goal "echo"
# @phase "validate"
class Echo < Mojo
# @parameter type="java.lang.String" default-value="Hello Maven World" \
expression="${message}"
def message
end
def execute
info $message
end
end
run_mojo Echo
The Echo class must extend
Mojo, and it must override the
execute() method. At the end of the
echo.rb file, you will need to run the mojo with
"run_mojo Echo". To install this plugin, run mvn
install:
$ mvn install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building Example Ruby Mojo - firstruby-maven-plugin
[INFO] task-segment: [install]
[INFO] ------------------------------------------------------------------------
...
[INFO] [plugin:descriptor]
...
[INFO] Applying extractor for language: jruby
[INFO] Ruby Mojo File: /echo.rb
[INFO] Extractor for language: jruby found 1 mojo descriptors.
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
During the build, you should see that the Maven Plugin Plugin's
descriptor goal applies the JRuby extractor to create a
plugin.xml which captures the annotations in the
Echo class. If you've configured your default
plugin groups to include org.sonatype.mavenbook.plugins,
you should be able to run this echo goal with the following
command-line:
$ mvn firstruby:echo
...
[INFO] [firstruby:echo]
[INFO] Hello Maven World
...Ruby Mojos are annotated using comments in Ruby source files. A
single annotation like @parameter takes a number
of attributes, and each of these attributes must be specified on the
same line. There can be no line-breaks between an annotations attribute
in the Ruby source. Both classes and parameters are annotated.
Parameters are annotated with four annotations:
@parameter, @required,
@readonly, and
@deprecated. The
@parameter attribute takes the following
attributes:
An alias for the parameter. An alternate name which can be used to populate the same parameter.
Provides a default value to the parameter if the supplied
value or the parameter expression produces a null result. In
echo.rb, we specify the default as "Hello
Maven World".
Contains an expression which can resolve to a Maven property or a System property.
The fully qualified Java type of the parameter. If the type
is not specified it will default to
java.lang.String.
In addition to the @parameter annotation, a
parameter can take the following annotations:
Marks the parameter as being required. The default value is false.
Marks the parameter as read-only. If this is true, you may not override the default value or the value from the expression from the command line. The default value is false.
Marks the parameter as deprecated. The default value is false.
Putting this altogether, a fully annotated message parameter from
echo.rb would look like the following code:
# @parameter type="java.lang.String" default-value="Hello Maven World" \
expression="${message}"
# @readonly true
# @required false
# @deprecated false
def message
end
Ruby Mojo classes are annotated with the following attributes:
Specifies the name of the goal.
The default phase to bind this goal to.
True if the Mojo requires that dependencies be resolved before execution.
Marks this mojo as an aggregator.
Provides the opportunity to execute a goal or lifecycle phase before executing this Mojo. The @execute annotation takes the following attributes:
Name of the goal to execute
Name of the lifecycle phase to execute
Name of the lifecycle (if other than default)
For an example of an annotated Mojo class, consider the following code example:
# Completes some build task # @goal custom-goal # @phase install # @requiresDependencyResolution false # @execute phase=compile class CustomMojo < Mojo ... end
Mojo parameters can reference Java classes and Maven properties. The following example shows you how to get access to the Maven Project object from a Ruby Mojo.
Example 18.6. Referencing a Maven Project from a Ruby Mojo
# This is a mojo description
# @goal test
# @phase validate
class Test < Mojo
# @parameter type="java.lang.String" default-value="nothing" alias="a_string"
def prop
end
# @parameter type="org.apache.maven.project.MavenProject" \
expression="${project}"
# @required true
def project
end
def execute
info "The following String was passed to prop: '#{$prop}'"
info "My project artifact is: #{$project.artifactId}"
end
end
run_mojo Test
In the previous example, we can access properties on the
Project class using standard Ruby syntax. If
you put test.rb in
firstruby-maven-plugin's
src/main/scripts directory, install the plugin,
and then run it, you will see the following output:
$ mvn install ... [INFO] [plugin:descriptor] [INFO] Using 3 extractors. [INFO] Applying extractor for language: java ... [INFO] Applying extractor for language: jruby [INFO] Ruby Mojo File: /echo.rb [INFO] Ruby Mojo File: /test.rb [INFO] Extractor for language: jruby found 2 mojo descriptors. ... $ mvn firstruby:test ... [INFO] [firstruby:test] [INFO] The following String was passed to prop: 'nothing' [INFO] My project artifact is: firstruby-maven-plugin
To log from a Ruby Mojo, call the info(),
debug(), and error()
methods with a message.
# Tests Logging
# @goal logtest
# @phase validate
class LogTest < Mojo
def execute
info "Prints an INFO message"
error "Prints an ERROR message"
debug "Prints to the Console"
end
end
run_mojo LogTest
If there is an unrecoverable error in a Ruby Mojo, you will need
to raise a MojoError. Example 18.7, “Raising a MojoError from a Ruby Mojo” shows you how to raise a
MojoError. This example mojo prints out a message
and then raises a MojoError.
Example 18.7. Raising a MojoError from a Ruby Mojo
# Prints a Message
# @goal error
# @phase validate
class Error < Mojo
# @parameter type="java.lang.String" default-value="Hello Maven World" \
expression="${message}"
# @required true
# @readonly false
# @deprecated false
def message
end
def execute
info $message
raise MojoError.new( "This Mojo Raised a MojoError" )
end
end
run_mojo Error
Running this Mojo, produces the following output:
$ mvn firstruby:error ... INFO] [firstruby:error] [INFO] Hello Maven World [ERROR] This Mojo Raised a MojoError
A Ruby Mojo can depend on a Plexus component. To do this, you
would use the expression attribute of the
@parameter annotation to specify a role and a
hint for Plexus. The following example Ruby Mojo, depends upon an
Archiver component which Maven will retrieve from Plexus.
Example 18.8. Depending on a Plexus Component from a Ruby Mojo
# This mojo tests plexus integration
# @goal testplexus
# @phase validate
class TestPlexus < Mojo
# @parameter type="org.codehaus.plexus.archiver.Archiver" \
expression="${component.org.codehaus.plexus.archiver.Archiver#zip}"
def archiver
end
def execute
info $archiver
end
end
run_mojo TestPlexus
Please note that the attributes for an annotation in a Ruby Mojo
cannot span multiple lines. If you were to run this goal, you would see
Maven attempt to retrieve a component from Plexus with a role of
org.codehaus.plexus.arhiver.Archiver and a hint of
zip.
Groovy is a dynamic language based on the Java Virtual Machine which compiles to Java bytecode. Groovy is a project in the Codehaus community. If you are fluent in Java, Groovy will seem like a natural choice for a scripting language. Groovy takes the features of Java, pares down the syntax a bit, and adds features like closures, duck-typing, and regular expressions. For more information about Groovy, please see the Groovy web site at http://groovy.codehaus.org.
To create a Maven Plugin using Groovy, you only need two files: a
pom.xml and a single Mojo implemented in Groovy. To
get started, create a project directory named
firstgroovy-maven-plugin. Place the following
pom.xml in this directory.
Example 18.9. POM for a Groovy Maven Plugin
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook.plugins</groupId>
<artifactId>firstgroovy-maven-plugin</artifactId>
<name>Example Groovy Mojo - firstgroovy-maven-plugin</name>
<packaging>maven-plugin</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.codehaus.mojo.groovy</groupId>
<artifactId>groovy-mojo-support</artifactId>
<version>1.0-beta-3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>2.4</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo.groovy</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<version>1.0-beta-3</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
What's going on in this POM? First, notice that
the packaging of the POM is
maven-plugin because we are creating a project that
will package a Maven plugin. Next, note that the project depends on the
groovy-mojo-support artifact in the
org.codehaus.mojo.groovy group.
Then under src/main/groovy in a directory
org/sonatype/mavenbook/plugins, create a file named
EchoMojo.groovy which contains the EchoMojo
class.
Example 18.10.
package org.sonatype.mavenbook.plugins
import org.codehaus.mojo.groovy.GroovyMojo
/**
* Example goal which echos a message
*
* @goal echo
*/
class EchoMojo extends GroovyMojo {
/**
* Message to print
*
* @parameter expression="${echo.message}"
* default-value="Hello Maven World"
*/
String message
void execute() {
log.info( message )
}
}
