Customised scripting in Maven is achieved in one of two ways:
maven.xml
file containing custom goals
Note that there are several best practices related to scripting that it is worth adhering to.
All scripts in Maven 1.x are written in the Jelly XML scripting language.
For an explanation of how to develop a plugin, please see
Developing
Plugins, or for details about
maven.xml
see the section on
Customising Maven, both in the User's Guide.
Maven uses
Jelly
as it's scripting language, and any valid jelly tags can be placed in
the
maven.xml
, or inside a plugin.
Here's an example
maven.xml
:
<project xmlns:j="jelly:core" xmlns:ant="jelly:ant" xmlns:u="jelly:util"> <goal name="nightly-build"> <j:set var="goals" value="java:compile,test" /> <ant:mkdir dir="${maven.build.dir}" /> <u:tokenize var="goals" delim=",">${goals}</u:tokenize> <j:forEach items="${goals}" var="goal" indexVar="goalNumber"> Now attaining goal number ${goalNumber}, which is ${goal} <attainGoal name="${goal}" /> </j:forEach> </goal> </project>
XML Namespaces are used to declare the Jelly tag libraries you want to use, much like JSTL (in fact, many of the Jelly tag libraries closely follow their JSTL equivalents).
More information on Jelly can be found at:
Jexl is the expression language used by Jelly. This allows you to get the values of properties, but also to execute functions on the Java objects behinf those variables. For example:
${myVar}
gets the value of the variable
myVar
${empty(myVar)}
calls the "empty" function in Jexl to test if the string is null or 0-length
${pom.inceptionYear.equals('2001')}
tests equality
${pom.build.resources.isEmpty()}
calls the
isEmpty()
method on the
List
given by
pom.build.resources
.
For more information about Jexl, see it's home page.
It is important to note that most of the functionality in Maven 1.x comes from the equivalent Ant tasks. To check the functionality of that task, or use it within your own script, refer to the Ant Documentation.
Werkz is the library used to handle the goal chain in Maven. You can use Jelly scripts to define new goals for works or attach actions to existing ones. This is described in the sections that follow.
Declaring new goals is quite simple and is done in both
maven.xml
and plugins. To declare a goal
add code such as this:
<goal name="my-goal" prereqs="some-other-goal" description="My Goal"> ... </goal>
The goal name can be anything you desire. If an existing goal exists by that name is defined, any given in
maven.xml
takes precedence (overriding plugins). If it exists in multiple plugins, then the
results are undefined. For this reason, goal names in plugins are usually prefixed with the plugin name, eg:
java:compile
.
prereqs
is a comma separated list of goals that must be executed before this goal is. If they
have already been executed, they will not be run again.
The description is used for generating plugin documentation, and for displaying on the command line when the
-g
, or
-P
parameters are used.
If, within a goal, you need to execute another goal, it can be done using:
<attainGoal name="my-goal" />
This is usually used in
preGoal
and
postGoal
definitions that can not specify
prereqs
, as you'll see in the next section.
Customising existing Maven goals is quite simple as long as you know where to hook in to. As an example, let's consider you want to generate some resources and have them placed into the produced JAR file.
Note: Generally resources should be specified in the
<resources>
section of the
project.xml
file. However, if they are generated as part of the build, you
will want to copy them to the destination yourself.
So, here is a sample
maven.xml
file that demonstrates how this would be achieved:
<project xmlns:ant="jelly:ant"> <!-- The java:jar-resources goal copies resources into target/classes so that they are included in the JAR at / --> <postGoal name="java:jar-resources"> <attainGoal name="generate-dynamic-resources" /> <!-- maven.build.dest is the location where resources are put --> <ant:copy todir="${maven.build.dest}"> <ant:fileset dir="${maven.build.dir}/generated-resources" /> </ant:copy> </postGoal> <!-- Here is the goal you've defined to generate the resources in some fashion --> <goal name="generate-dynamic-resources"> <!-- Place them into ${maven.build.dir}/generated-resources in accordance with the fragment above --> ... </goal> </project>
The current project is always available under the context variable
${pom}
. This allows the retrieval
of project variables in your scripts. This is the same as how they are used within the project file itself when
it is interpolated.
The variables and functions you can access are those listed in the
javadoc. For example:
${pom.artifactId}
calls
getArtifactId()
to retrieve the artifact ID.
${pom.getVersionById(pom.currentVersion).tag}
gets the tag associated with the version which has an ID equivalent to the current version of the project.
Note that inside a plugin, the ${pom}
variable refers the project currently being built, not the
project that the plugin is defined in. To access the project variables of the plugin itself, use the variable
${plugin}
.
There are three ways to access the dependencies declared on the current project being built.
The first is to use the entire set as an Ant path reference, if you are only passing it to an Ant task.
The reference that contains all the dependencies in the classpath is maven.dependency.classpath
.
For example:
<javac ...> <classpath refid="maven.dependency.classpath" /> </javac>
If you need the path to a specific dependency, you can use the
getDependencyPath()
method. For example:
<ant:path id="my-classpath"> <ant:path refid="maven.dependency.classpath"/> <ant:pathelement path="${maven.build.dest}"/> <ant:pathelement location="${plugin.getDependencyPath('junit:junit')}"/> </ant:path>
Finally, you can iterate through the artifacts within a project to process them individually. For example:
<j:forEach var="lib" items="${pom.artifacts}"> <j:set var="dep" value="${lib.dependency}"/> <j:if test="${dep.getProperty('war.bundle')=='true'}"> <ant:copy todir="${webapp.build.lib}" file="${lib.path}"/> </j:if> </j:forEach>
As you will have seen many plugins have their own properties. You can set these values in the current project before the plugin is initialised, and they will be recognised. However, once it is initialised, setting them in the current project will have no effect, and changes made within the plugin are not visible to the current project.
If you need to get a property from the plugin, you can use the
maven:get
tag. For example:
<maven:get var="out_var" plugin="maven-war-plugin" property="maven.war.final.name" />
Note: you may also see references to a maven:pluginVar
tag, and the expression
${pom.getPluginContext('maven-war-plugin').getVariable('maven.war.final.name')}
. These are both
deprecated forms of the above tag.
Likewise, you can set a plugin property using the
maven:set
tag. For example:
<maven:set plugin="maven-war-plugin" property="maven.war.final.name" value="${in_var}" />