Implementing Mojo

What is a Mojo? A simple definition for Mojo is that Mojo is a Maven goal. This is not far from reality.

In order to be more accurate, we can say that a Mojo is a Maven plain Old Java Object. Each Mojo is an executable goal in Maven, and a plugin is a distribution of one or more related Mojos.

In practice, to create a Mojo, we must create a class extending the abstract class, as follows:

org.apache.maven.plugin.AbstractMojo

Such a class provides a utility method for common operations. Furthermore, it provides the abstract method, public void execute() throws MojoExecutionException, to perform all the dirty work when the plugin is executed.

In order to associate our Mojo to a goal for our plugin, we have to use an annotation in Java style on our class definition:

@Mojo(name = "mark-resolved", 
      defaultPhase = LifecyclePhase.PACKAGE, 
      requiresOnline = true, 
      requiresProject = true, 
      threadSafe = true)
public class MarkResolved extends AbstractMojo

With the @Mojo annotation, we indicate that the MarkResolved class is a Mojo, and its goal is mark-resolved. With other parameters of annotation, we specified the default phase when the plugin is executed. In the same annotation, we can indicate other desirable characteristics as follows:

  • requiresOnline: This indicates that the operation requires online mode to be executed
  • requiresProject: This indicates that Mojo requires a project in order to be executed
  • threadSafe: This marks Mojo to be thread safe; with this flag it can support parallel execution during parallel build

This kind of notation is quite different from the old-fashioned way. The old, and still supported, annotation system provides the use of comments. With such a system, our Mojo definition would look like the following code:

/**
 * @goal mark-resolved 
 * @phase package
 * @requiresOnline true
 * @requiresProject true
 * @threadSafe true
 */
public class MarkResolved extends AbstractMojo

As we can see, the functionality and parameters are the same. The only change is represented by the declaration syntax.

All the plugins have a configuration section in which the users can set parameters used for accomplishing the plugin goal. Parameters can also be passed through the command line:

$ mvn plugin-name:goal -Dmy.custom.parameter=somevalue

In order to match the configuration properties with Mojo's fields, we have to annotate our Mojo fields as follows:

@Parameter( property="basedir", required=true)
protected File baseDir;

The @Parameter annotation's function is to pass the base directory of the project. Within the annotation, we specified the name of the configuration parameter with property="basedir".

We also specified whether the parameter is mandatory or not with the required=true notation.

Our basedir property is automatically injected into our object without the need for other coding. We can thank the Plexus Inversion of Control framework for this.

With the old annotation, we could obtain the same result with the following code:

/**
 * @parameter expression="${basedir}"
 * @required
 */
protected File baseDir;

In this way, all the annotated Mojo fields will be associated with a relative parameter without the need for getter and setter methods.

Tip

The direct injection is only possible if the name of the field matches the name of the property specified within the annotation, property="propertyNameToInject".

If we want to inject a property with a different name with respect to the field, we have to specify a setter method for such a property.

In order to use a property name different from the field name, we have to indicate the configuration property name used in the plugin configuration. We can do this through the alias="dbUserName" notation.

This indication is not enough; we also have to create a setter for the new aliased property name:

public void setDbUserName(String dbUserName) {
    this.dbUser = dbUserName;
}

Tip

As we can see, we linked the dbUserName property to the dbUser private field.

Using this method, we can remap the configuration properties on different Mojo field names. With this feature, we can remap private fields that are incomprehensible to users, to friendly name parameters.

Another powerful feature is the capability of assigning a default value if there is no value for nonmandatory parameters:

@Parameter(property="project.desctiption", defaultValue="${project.description}")
protected String projectDesctiption;

Tip

The projectDescription field is a nonmandatory field so, if the configuration does not provide a value for this parameter, we can assign the value of the internal property inherited from the Maven's central POM, {project.description}.

Note that using Maven 3.0, all the pom.* properties are deprecated. All the properties must be named project.*.

We have some specific properties, defined in the Maven's central POM, as follows:

  • ${basedir}: This is a built-in property representing the directory in which the pom.xml is stored.
  • ${version}: This is a built-in property, equivalent to ${project.version}, containing the version of the project.
  • ${project.build.directory}: This is a property defined in the Maven's central POM, containing the path of the build directory. Its default value is target.
  • ${project.build.outputDirectory}: This is a property defined in the Maven's central POM, containing the directory in which the class files are stored during the build process. Its default value is target/classes.
  • ${project.name}: This contains the name of the project.
  • ${project.build.finalName}: This contains the final name of the file created when the built project is packaged.
  • ${settings.localRepository}: This refers to the path of the user's local repository.
  • ${env.M2_HOME}: This is an environment variable containing the Maven2 installation folder.
  • ${java.home}: This is an environment variable specifying the path to the current JRE_HOME folder.

Notice how some of these properties have a common name prefix. Such a prefix is used to specify the scope of the property. So, properties defined at the project level will be prefixed with project, while properties defined in the user's settings.xml file will be prefixed with settings.

The convention for referring to the parent project's variables provides using the ${project.parent} syntax.

Maven also gives users the possibility of defining custom properties. We can simply add a property into our pom.xml file with the following syntax:

<project>
...
 <properties>
   <my.property>hello</my.property>
 </properties>
...
</project>

Tip

If we add this kind of snippet into our project, the ${my.property} property would result in hello.

As we saw before, Maven links each property to a getter/setter method. Thus, properties such as ${project.build.directory} will be matched with the getProject().getBuild().getDirectory()method chain.

Tip

In order to implement a Mojo, we need to extend the AbstractMojo abstract class. This class provides some common utility methods.

The first inherited method that we will see is getLog(). This method returns a logger of type org.apache.maven.plugin.logging.Log. Such a logger allows you to write on output of the plugin's execution. The following line of code will produce the [INFO] Jdbc Driver :: com.mysql.jdbc.Driver output:

log.info("Jdbc Driver :: " + jdbcDriver);

The plugin logger provides three different levels of output: info, error, and debug.

Another inherited method is getPluginContext(). This method returns java.util.Map containing all the property mappings defined in Mojo's context. These mappings contain all the properties explained before, that are visible from Mojo's POM. We can access the basedir property using the following notation:

String baseDir = (String) getPluginContext("basedir");

Using the setter method, we set a custom plugin context to our Mojo, as follows:

public void setPluginContext(Map pluginContext)

We can get the plugin context map using getPluginContext, add some parameters, and set the new map with setPluginContext.

Tip

Best practices discourage this kind of manipulation of the context map.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset