The Maven Cookbook
5.2. Writing Plugins in JRuby
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 5.1. 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 5.2. 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:
- alias
-
An alias for the parameter. An alternate name which can be used to populate the same parameter.
- default-value
-
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". - expression
-
Contains an expression which can resolve to a Maven property or a System property.
- type
-
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:
- @required "<true|false>"
-
Marks the parameter as being required. The default value is false.
- @readonly "<true|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.
- @deprecated "<true|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:
- @goal
-
Specifies the name of the goal.
- @phase
-
The default phase to bind this goal to.
- @requiresDependencyResolution
-
True if the Mojo requires that dependencies be resolved before execution.
- @aggregator
-
Marks this mojo as an aggregator.
- @execute
-
Provides the opportunity to execute a goal or lifecycle phase before executing this Mojo. The @execute annotation takes the following attributes:
- goal
-
Name of the goal to execute
- phase
-
Name of the lifecycle phase to execute
- lifecycle
-
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 5.3. 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 5.4, “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 5.4. 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 5.5. 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.