Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Maven 2.0: Enterprise Peace of Mind Is All Part of the Package : Page 4

If you're a Java developer who hasn't yet experimented with Maven for managing enterprise Java projects, now is a good time to jump in. The 2.0 version of Maven, currently in beta, provides many significant improvements over previous versions. Follow along for a complete tutorial on the benefits of Maven.


advertisement
Plugin API
I have discussed how Maven aims to abstract similarities in the build process across projects. Whenever possible you should use the standard core Maven functionality and plugins to achieve the desired results. But there are times when a custom solution just cannot be avoided. In these cases you will need to write a custom plugin and bind it to one of the lifecycle stages.

In Maven 2.0 plugins can be written in Java or in one of several scripting languages (currently Marmalade is the only scripting language supported but support for others is being developed). Maven plugins are basically command objects. To create a Maven plugin you write a Java class that implements the Mojo interface (which stands for Maven POJO). This interface defines a single-execute method with no parameters and no return value and some logging methods. You must also write a plugin descriptor that provides additional information about your plugin, including lifecycle phase binding and parameters. Parameters defined in this descriptor will be injected into member variables in your plugin at runtime by Maven.

Maven simplifies this process by providing tools to generate the plugin descriptor and to package your plugin for you. All you need to do is annotate your plugin class and Maven will take care of the rest.



Greeter Application—Iteration 3
The best way to understand Maven plugins is to walk through the steps involved in creating one. In this section, you will create a plugin that writes a few useful pieces of project metadata to a properties file that will be read by your application at runtime. This properties file will be bundled in your WAR and will be used by the greeter servlet at runtime to display the version of the application. First, use the archetype plugin to create a simple plugin under your project root:

m2 archetype:create -DgroupId=com.devx.greeter -DartifactId=greeter-
plugin –DpackageName=com.devx.greeter -DarchetypeArtifactId=maven-
archetype-mojo

Next, delete the sample MyMojo class and replace it with a class named PomWriterMojo that will indirectly implement the Mojo interface indirectly by subclassing the AbstractMojo class. This abstract super class will provide default implementations of the log methods defined in the Mojo interface.

Also, you will annotate your class to indicate how you want this plugin used in the build process. You want your properties file created during the generate-sources lifecycle phase, so you'll annotate your plugin class accordingly. During this phase your properties file will be created and placed in the target project's output directory. This will make it available for unit testing, packaging, and deployment as subsequent lifecycle phases are executed.

/greeter/greeter-plugin/src/main/java/com/devx/greeter/PomWriterMojo.java package com.devx.greeter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Properties; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; /** * @goal write * @phase generate-sources * @description Build a version.txt file to provide information at runtime */ public class PomWriterMojo extends AbstractMojo { ... }

Before implementing the actual logic of your plugin, you need to determine where you will get the project metadata from. Instead of having your plugin go out and retrieve this information from Maven, instead ask the Maven to inject it into your class. You'll specify what data needs to be injected by annotating the properties you want to contain this information. The annotation will specify an expression, which will be evaluated by Maven, and the results of the evaluation will be injected into the property. Expressions can reference configuration parameters and system properties but they generally reference POM elements and look similar to "${project.build.resources}".

The annotated properties look as follows:

/greeter/greeter-plugin/src/main/java/com/devx/greeter/PomWriterMojo.java (cont.) public class PomWriterMojo extends AbstractMojo { /** * @parameter expression="${project.version}" * @required * @readonly */ private String version; /** * @parameter expression="${project.build.outputDirectory}" * @required * @readonly */ private String outputDirectory; }

Lastly, you can use the data that has been injected into your plugin to perform the task for which your plugin was written. Namely, to write the version of the project to a file named pom.properties in the output directory.

/greeter/greeter-plugin/src/main/java/com/devx/greeter/PomWriterMojo.java (cont.) public class PomWriterMojo extends AbstractMojo { ... public void execute() throws MojoExecutionException { Properties projectProperties = new Properties(); projectProperties.put("version", version); try { File targetDirectory = new File(outputDirectory); if (!targetDirectory.exists()) { targetDirectory.mkdirs(); } File propertiesFile = new File(outputDirectory + "/pom.properties"); propertiesFile.createNewFile(); OutputStream propertiesStream = new FileOutputStream(propertiesFile); projectProperties.store(propertiesStream, "Properties from Maven POM"); propertiesStream.close(); } catch (IOException e) { getLog().error(e); throw new MojoExecutionException("Unable to open properties file for write"); } } }

To take advantage of your new plugin, declare it in the plugins section of the POM of your greeter-web project. Additionally, you need to add this plugin as a dependency to that project and to the modules section of the parent POM. This will cause your plugin to be built prior to being used by the greeter-web project and, once again, illustrates multi-project support at work.

/greeter/greeter-web/pom.xml (cont.) ... <dependency> <groupId>com.devx.greeter</groupId> <artifactId>greeter-plugin</artifactId> <version>1.0-SNAPSHOT </version> <type>maven-plugin</type> </dependency> </dependencies> <build> <finalName>greeter-web</finalName> <plugins> <plugin> <groupId>com.devx.greeter</groupId> <artifactId>greeter-plugin</artifactId> <version>1.0-SNAPSHOT</version> <goals> <goal> <id>write</id> </goal> </goals> </plugin> </plugins> </build> </model> /greeter/pom.xml <model> <modelVersion>4.0.0</modelVersion> <groupId>com.devx.greeter</groupId> <version>1.0-SNAPSHOT</version> <artifactId>greeter</artifactId> <packaging>pom</packaging> <modules> <module>greeter-app</module> <module>greeter-web</module> <module>greeter-plugin</module> </modules> </model>

Figure 4. Greeter Output: The PomWriter plugin is invoked before the WAR is built and stores project meta-data for the servlet to access at runtime.
Finally, you just want to use your generated POM properties file to display the version number of your servlet in its greeting.

/greeter/greeter-web/src/main/java/com/devx/greeter/GreeterServlet.java package com.devx.greeter; import java.io.IOException; import java.io.PrintWriter; import java.util.Properties; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class GreeterServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter writer = new PrintWriter(response.getOutputStream()); GreeterService service = new GreeterService(); writer.println("<html><body>"); writer.print("<em>Servlet version " + getProjectVersion() + " says: </em>"); writer.println(service.sayHello()); writer.println("</body></html>"); writer.close(); } private String getProjectVersion() { String version; try { Properties pomProperties = new Properties(); pomProperties.load(GreeterServlet.class .getResourceAsStream("/pom.properties")); version = pomProperties.getProperty("version"); } catch (IOException e) { version = "unknown"; } return version; } }

The new greeting displays the greeter-web's version from its POM (see Figure 4).

Maven 2.0 is a powerful tool to organize your projects and to simplify your build process. By following a set of recommended practices and providing some information describing your project, you can let Maven do most of the heavy lifting in your build process. Its recommended practices also promote consistency across projects. And the Maven plugin API will continue to encourage innovative development of plugins useful in enterprise development.



Rod Coffin is an agile technologist at Semantra, helping to develop an innovative natural language ad hoc reporting platform. He has many years of experience mentoring teams on enterprise Java development and agile practices and has written several articles on a range of topics from Aspect-Oriented Programming to EJB 3.0. Rod is a frequent speaker at user groups and technology conferences and can be contacted via his home page.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap