Maven ties together tools for better code management

The Maven tool gives developers a complete picture of their projects

Have you ever been assigned to an existing project where the original programmers are nowhere to be found? Or given source code from a third party and told to fix a bug, only to find there isn't any decent documentation? Avoid some of these resulting headaches by using Maven to generate code-level documentation for your projects. Maven ties together a build management system and various third-party source code analysis tools to help you get your projects out the door faster.

Maven is a project-oriented tool from the Apache Jakarta Project that builds upon Jelly (see the Jelly Website), Ant, and other open source projects to give developers a complete picture of their projects. Maven bundles many open source projects under one umbrella to let developers easily integrate these third-party tools into their build processes. Maven uses plug-ins for various tools, including:

  • Ant for build management
  • JUnit for unit testing
  • Jalopy for formatting source code
  • Checkstyle for validating Java source against a coding standard
  • Sun's javadoc for standard JDK code documentation

For a complete list of Maven's functionalities, check the plug-ins page on the Maven Website.

The Krysalis Centipede project achieves similar goals. An open source build tool based on Ant, Centipede features cents, which are equivalent to plug-ins, and includes many of the same tools as Maven, along with some that Maven omits. Discussions in the Jakarta Project mailing lists have focused on how the two could work together moving forward. If you decide to evaluate Maven, also evaluate Centipede; either one could be appropriate for your project.

In this article, I discuss some key Maven concepts to give you an idea of how Maven works and then launch into a walkthrough of an example project.xml file from a project already using Maven: the Jakarta Project's Torque, which is an object-relational database tool. You should be able to adapt the Torque example for your own use by simply changing some values to match your existing configuration and project setup.

Maven concepts

Maven's central concept is that all code belongs in projects, each one defined by a project descriptor. These projects are either discrete components or a full application that can be built on its own with various dependencies. The descriptor consists of a Project Object Model (POM), which can be a project.xml file, or another storage format such as a database. The XML format for the POM is specified by the maven-project.xsd XML Schema file in your Maven installation's root directory. Generally speaking, copying an example project.xml file from an existing Maven installation and customizing it for your own needs proves easier than creating a new one from scratch.

Maven defines some common goals for projects. Goals resemble Ant targets; you use them to generate source code documentation, compile your application, unit test the compiled project, or run the plug-ins shipped with Maven. To find a project's available goals, run the command maven -g in the directory that contains your project.xml file. This command will return a list of plug-ins and their defined goals. A sample of this output:

[java] ( NO DEFAULT GOAL )
  compile .................... Compile the project
  jar ........................ Create the deliverable jar file
  jar-resources .............. Copy any resources that must be present in the
                               deployed JAR file
  prepare-filesystem ......... Create the directory structure needed to compile
[javadoc] : Generate API documentation
  generate ................... Generate API documentation
[jdepend] : Generate a dependency report with JDepend
  generate-report ............ Generate a dependency report with JDepend
[junit-report] : Generate a report from the test results

The names in brackets refer to installed Maven plug-ins, with their goals listed underneath. To run the javadoc generate goal, call Maven from the command line with the following syntax: maven javadoc:generate. As it turns out, generate is the javadoc plug-in's default goal, so you can also run it with this command: maven javadoc. However, don't rely on that command for the generate goal.

Additional goals and processing can be defined in the maven.xml file, which is specific to each project. File maven.xml should reside in the same directory as project.xml. Goals originate from the Werken Company's open source werkz project. As mentioned earlier, each goal is identical to an Ant target, except that <goal> replaces the Ant <target> element. The <goal> element has attributes for a name and a description. It can also have a list of prerequisite goals, much like Ant has targets that require other targets to run. The prereqs attribute specifies these goals. Here is an example goal declaration from the Torque 3.0 beta 4 maven.xml:

   <goal name="runtime:prepare" prereqs="java:jar">

Goals can also use the attainGoal command to run a goal directly. An example would look like this:

  <attainGoal name="java:jar">

You can also extend a goal so that pre- or post-processing occurs before or after the goal runs. This proves useful for extending Maven's built-in goals, for example, to archive an old jar file when a new build is created. Instead of using <goal>, use <preGoal> or <postGoal>. These dependent goals use the same syntax internally.

Maven has a strong dependency system for projects as well, although the implementations are quite different. Projects can share dependencies—mostly used for ensuring that every project uses common jar files. For instance, each of your projects may rely on the Jakarta POI jar file; Maven keeps each project current with the latest jar file.

These jar files are retrieved from a repository, which is a directory on a remote Web server that contains all jar files. The jar files are mirrored in a local repository under the Maven installation. The remote repository location is configured in the driver.properties file located in your Maven installation's bin subdirectory. A property called maven.repo.remote takes a comma-separated list of URLs. By default, Maven uses http://www.ibiblio.org/maven as its repository. You will need to set up your own repository with copies of internal jar files, private builds of open source projects, and other resources you might need. Additionally, Maven uses an XML element called repository in the project.xml POM to refer to the source control repository your project uses for storing its source. Maven's source control repository attribute supports CVS (Concurrent Versioning System) and Subversion, an open source Java source control system.

Use Maven to build your project

Once you have your project.xml and maven.xml files set up for your project, just run Maven from the command line in the directory that has those files. Maven will download the appropriate jar files to its local repository. Maven then creates a target directory (called target) for its output. Maven uses javac to compile the source directory's contents to a subdirectory called classes under the target directory. Maven then fires off the unit tests configured under the build element in project.xml. If those tests succeed, Maven will create a jar file with the project's name and version in the target directory.

Use Maven to create documentation for your project

Maven can also generate an impressive Website for your project. Run the site:generate goal for your project with this command:

 maven site:generate

Maven will create a new subdirectory under target called docs. Open index.html in your favorite Web browser and surf around. Maven integrates project information, Javadocs, unit-test information, source code cross referencing, a coding style check, metrics, and more into a single static generated Website—no CGI, servlets, or anything else necessary.

Maven gets its look and feel for the generated Website from the XDoc plug-in. This plug-in is located in the maven/plugins/maven-xdoc-plugin-1.1/ directory, and you can modify its CSS (Cascading Stylesheets), images, and XML templates. Since the Website should be used internally anyway, you won't find many reasons for modifying it, plus you would have to move any changes to a new version of Maven when you upgrade.

Download and install Maven

You can download Maven from the Maven download page, which also features installation instructions. Because Maven's installation process is constantly improving, I won't discuss it here. As of this writing, Maven remains at the 1.0 beta 6 version, and the released version's installation procedure might differ.

Example: Torque 3.0 beta 4

The Jakarta Project's Torque object-relational mapping tool uses Maven in its latest release for building and creating the Website on the Jakarta Project's site. To show you an example project, I'll walk you through Torque's project.xml. You'll find this file in the source code that can be downloaded from the Jakarta Project; you'll find the generated documentation on the Torque project page.

The project.xml included with Torque is used only by Maven and only by a developer building Torque from source or creating documentation. Developers using Torque for their own projects don't need that file. To create a project.xml file, start with a root element of <project>:

 <?xml version="1.0" encoding="UTF-8"?>
<project>

The <pomVersion> element, illustrated below, identifies the POM version used when this project.xml was created. You shouldn't need to change the version. Future Maven versions will have larger numbers.

   <pomVersion>3</pomVersion>

The <id> element, shown below, is Maven's naming convention; for example, you can use it to name jar files. You append the current tool version to the <id> tag's value—Torque's jar file is named torque-3.0-b4.jar. The <name> is a longer version of <id>.

    <id >torque </id >
   <name >jakarta-turbine-torque </name >
   <currentVersion >3.0-b4 </currentVersion >

The organization refers to the project developers. Replace that element with your company's name, homepage, and a logo URL that can be used as an <img> HTML tag's src attribute:

    <organization >
     <name >Apache Software Foundation </name >
     <url >http://jakarta.apache.org/ </url >
     <logo >/images/jakarta-logo-blue.gif </logo >
   </organization >

You use the inception year for copyright notices in the Javadoc:

    <inceptionYear >2000 </inceptionYear >

All of Torque's code should be under this package:

    <package >org.apache.torque </package >

This is the URL for Torque's logo; it resembles the organization's logo described above:

    <logo >/images/blue-logo.gif </logo >

Gump is another Jakarta Project build tool that helps build many interdependent projects. You probably aren't using it, so you can leave this element out:

    <!-- Gump integration -- >
   <gumpRepositoryId >jakarta </gumpRepositoryId >

This description will go on the front page of the Website generated with Maven:

    <description >Torque is a persistence layer. </description >

A brief description of the project:

    <shortDescription >Persistence Layer </shortDescription >

The <url> element is the project's homepage:

    <url >http://jakarta.apache.org/turbine/torque/ </url >

Include a link to your bug tracker if it's Web-based:

   <issueTrackingUrl >http://issues.apache.org/scarab/servlet/scarab/ </issueTrackingUrl >

Maven can optionally deploy the generated Website to another Web server, using the site:deploy goal. Set the values of the XML elements below to your Web server's name and filesystem path:

    <siteAddress >jakarta.apache.org </siteAddress >
   <siteDirectory >/www/jakarta.apache.org/turbine/torque/ </siteDirectory >

Similar to the above code, Maven can publish the entire distribution it creates to the Web server using the dist:deploy goal:

   <distributionDirectory >/www/jakarta.apache.org/builds/jakarta-turbine-torque/ </distributionDirectory >

Below is the source control repository I discussed above. It only works with CVS. Modify this code to use your CVS connection string if you have one. The URL proves useful if you run WebCVS or another Web-accessible CVS server; developers that visit your site don't have to load CVS or a GUI (graphical user interface) to get one or two source code files.

    <repository >
 <connection >scm:cvs:pserver:anoncvs@cvs.apache.org:/home/cvspublic:jakarta-turbine-torque </connection >
     <url >http://cvs.apache.org/viewcvs/jakarta-turbine-torque/ </url >
   </repository >

Maven creates a mailing-lists page for your documentation. Modify the value of the mailingList XML elements below, if it has any. The <subscribe> and <unsubscribe> elements should be email addresses monitored by the list-manager software. The <archive> element can point to the URL for your mailing list's back emails.

    <mailingLists >
     <mailingList >
       <name >Torque User List </name >
 <subscribe >turbine-torque-user-subscribe@jakarta.apache.org </subscribe >
 <unsubscribe >turbine-torque-user-unsubscribe@jakarta.apache.org </unsubscribe >
 <archive >http://archives.apache.org/eyebrowse/SummarizeList?listName=turbine-torque-user@jakarta.apache.org </archive >
     </mailingList >
     <mailingList >
       <name >Torque Developer List </name >
      
 <subscribe >turbine-torque-dev-subscribe@jakarta.apache.org </subscribe >
 <unsubscribe >turbine-torque-dev-unsubscribe@jakarta.apache.org </unsubscribe >
 <archive >http://archives.apache.org/eyebrowse/SummarizeList?listName=turbine-torque-dev@jakarta.apache.org </archive >
     </mailingList >
   </mailingLists >

Maven also creates an HTML page that displays the project developers' names and emails. This can prove extremely useful in big companies where development teams are scattered across locations. For smaller development groups, this page probably isn't as important:

    <developers >
     <developer >
       <name >Eric Dobbs </name >
       <id >dobbs </id >
       <email >dobbs@apache.org </email >
       <organization/ >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >Stephen Haberman </name >
       <id >stephenh </id >
       <email >stephenh@chase3000.com </email >
       <organization/ >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >Fedor Karpelevitch </name >
       <id >fedor </id >
       <email >fedor@karpelevitch.net </email >
       <organization/ >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >John McNally </name >
       <id >jmcnally </id >
       <email >jmcnally@collab.net </email >
       <organization >CollabNet </organization >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >Martin Poeschl </name >
       <id >mpoeschl </id >
       <email >mpoeschl@marmot.at </email >
       <organization >Tucana.at </organization >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >Daniel Rall </name >
       <id >dlr </id >
       <email >dlr@finemaltcoding.com </email >
       <organization >CollabNet, Inc. </organization >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >Kurt Schrader </name >
       <id >kschrader </id >
       <email >kschrader@karmalab.org </email >
       <organization/ >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >Jon Scott Stevens </name >
       <id >jon </id >
       <email >jon@latchkey.com </email >
       <organization >CollabNet, Inc. </organization >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >James Taylor </name >
       <id >jtaylor </id >
       <email >james@jamestaylor.org </email >
       <organization/ >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
     <developer >
       <name >Jason van Zyl </name >
       <id >jvanzyl </id >
       <email >jason@zenplex.com </email >
       <organization >Zenplex </organization >
       <roles >
         <role >Java Developer </role >
       </roles >
     </developer >
   </developers >

As mentioned earlier, Maven can manage all jar file dependencies for the build machines. Each dependency refers to a jar file in a remote repository. In Maven's future versions, these dependencies might extend beyond jar files. Maven caches the jar files on the local machine under the Maven directory. The <dependency> element has three child elements, <id>, <version>, and <url>. Maven looks in the remote repository for a directory with the same name as the <id>. Under that directory, it looks for a jars subdirectory. Inside the jars directory should be a jar file that has the filename id-version.jar. So for Torque's first dependency, the filename would be ant-1.4.1.jar. The external component's homepage should be specified in the <url> element, so Maven can generate site documentation for the dependencies with correct links.

Maven also uses this list to create the classpath for the builds:

    <dependencies >
     <dependency >
       <id >ant </id >
       <version >1.4.1 </version >
       <url >http://jakarta.apache.org/ant/ </url >
     </dependency >
     <dependency >
       <id >commons-beanutils </id >
       <version >1.4 </version >
     </dependency >
     <dependency >
       <id >commons-collections </id >
       <version >2.0 </version >
       <url >http://jakarta.apache.org/commons/collections.html </url >
     </dependency >
     <dependency >
       <id >commons-configuration </id >
       <version >1.0-dev </version >
       <url >http://jakarta.apache.org/commons/ </url >
     </dependency >
     <dependency >
       <id >commons-dbcp </id >
       <version >1.0-dev-20020806 </version >
       <url >http://jakarta.apache.org/commons/ </url >
     </dependency >
     <dependency >
       <id >commons-lang </id >
       <version >1.0-b1 </version >
       <url >http://jakarta.apache.org/commons/ </url >
     </dependency >
     <dependency >
       <id >commons-logging </id >
       <version >1.0.1 </version >
       <url >http://jakarta.apache.org/commons/ </url >
     </dependency >
     <dependency >
       <id >commons-pool </id >
       <version >1.0 </version >
       <url >http://jakarta.apache.org/commons/pool/ </url >
     </dependency >
     <dependency >
       <id >jcs </id >
       <version >1.0-dev </version >
       <url >http://jakarta.apache.org/turbine/jcs/ </url >
     </dependency >
     <dependency >
       <id >jdbc </id >
       <version >2.0 </version >
       <url >http://java.sun.com/products/jdbc/download.html#spec </url >
     </dependency >
     <dependency >
       <id >jndi </id >
       <version >1.2.1 </version >
       <url >http://java.sun.com/products/jndi/ </url >
     </dependency >
     <dependency >
       <id >log4j </id >
       <version >1.2.6 </version >
       <url >http://jakarta.apache.org/log4j/ </url >
     </dependency >
     <dependency >
       <id >stratum </id >
       <version >1.0-b3 </version >
       <url >http://jakarta.apache.org/turbine/stratum/ </url >
     </dependency >
     <dependency >
       <id >tomcat </id >
       <version >1.0 </version >
       <jar >tomcat-naming-1.0.jar </jar >
       <url >http://jakarta.apache.org/tomcat/ </url >
     </dependency >
     <dependency >
       <id >velocity </id >
       <version >1.3 </version >
       <url >http://jakarta.apache.org/velocity/ </url >
     </dependency >
     <dependency >
       <id >village </id >
       <version >1.5.3 </version >
       <url >http://share.whichever.com/index.php?SCREEN=village </url >
     </dependency >
     <dependency >
       <id >xerces </id >
       <version >2.0.2 </version >
       <jar >xercesImpl-2.0.2.jar </jar >
       <url >http://xml.apache.org/xerces2-j/ </url >
     </dependency >
     <!-- Packaged EntityResolver conflicts with the one in Xerces 1.4.4 -- >
     <dependency >
       <id >xml-apis </id >
       <version >2.0.2 </version >
       <url >http://xml.apache.org/xerces2-j/ </url >
     </dependency >
     <dependency >
       <id >junit </id >
       <version >3.7 </version >
       <url >http://www.junit.org </url >
     </dependency >
   </dependencies >

The build element controls the project's build process:

   <build>

Maven can send automated build notifications to a mailing list if you use a build tool plug-in that runs builds by itself:

     <nagEmailAddress>turbine-torque-dev@jakarta.apache.org</nagEmailAddress>

This directory points to your source code so Maven can just compile the entire directory using the classpath it creates from the dependencies:

     <sourceDirectory>src/java</sourceDirectory>

If you use JUnit for unit testing, Maven will compile your unit tests if they are located under this directory, which would be separate from your source directory:

     <unitTestSourceDirectory>src/test</unitTestSourceDirectory>

Torque apparently doesn't use AspectJ, an aspect-oriented extension to Java that uses nonstandard Java source files that must be compiled with the AspectJ compiler. If you use AspectJ, specify your sources here:

     <aspectSourceDirectory></aspectSourceDirectory>

If you use JUnit, you can specify the unit tests that Maven runs in the below unitTest XML element. The unitTest XML element's children are standard Ant include and exclude elements. Be sure to leave .java alone; Maven uses the source code files for the classes.

      <unitTest >
       <includes >
         <include >**/*Test*.java </include >
       </includes >
       <excludes >
         <exclude >**/BaseTestCase.java </exclude >
       </excludes >
     </unitTest >

Torque doesn't include any other jar files in its build process, so these elements are unused:

      <resources > </resources >
     <jars > </jars >
   </build >
 </project >

Enhance your development environment

An open source project developed by a solid team, Maven is an impressive tool that will clear up some of your development headaches by smoothing out your build process, creating high-quality documentation for your project, and managing your test code. With the code quality and documentation tools built into Maven, you can better control large Java projects.

I recommend evaluating both Maven and Centipede to see if either can improve your development environment. Both tools are in beta form, so wait for their released versions to actually implement them.

I would like to thank Jason van Zyl for reviewing this article.

Jeff Linwood is a developer at the Gossamer Group, a software company based in Austin, Texas, that develops custom software for enterprise clients. Previously, he was developer and lead developer on several high-end e-commerce projects using Java and XML for the Fortune 500 at Trilogy and pcOrder. Jeff has more than seven years of Web application development experience.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies