Apache Maven 1.x has reached its end of life, and is no longer supported. For more information, see the announcement. Users are encouraged to migrate to the current version of Apache Maven.

Scripting References

Customised scripting in Maven is achieved in one of two ways:

  • Creating a maven.xml file containing custom goals
  • Creating your own plugin 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.

Jelly

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

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.

Ant

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.

Using Werkz in Jelly

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 Goals

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.

Calling Goals

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 Goals

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>

Accessing the Current 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}.

Using Dependencies

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>

Working with Plugin Properties

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}" />