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
.