Introduction
This document, as the title suggests describes the best practices of Apache Maven tool.
About Apache Maven
Maven is a software tool for Java project management and build automation created by Sonatype’s Jason van Zyl in 2002. It is similar in functionality to the Apache Ant tool, but has a simpler build configuration model, based on an XML format. Maven is hosted by the Apache Software Foundation.
Maven uses a construct known as a Project Object Model (POM) to describe the software project being built, its dependencies on other external modules and components, and the build order. It comes with pre-defined targets for performing certain well defined tasks such as compilation of code and its packaging.
A key feature of Maven is that it is network-ready. The core engine can dynamically download plug-ins from a repository, the same repository that provides access to many versions of different Open Source Java projects, from Apache and other organizations and developers. Maven provides built in support not just for retrieving files from this repository, but to upload artifacts at the end of the build.
Maven is built using a plugin-based architecture that allows it to make use of any application controllable through standard input.
It is gaining widespread acceptance as well as adoption in large enterprises and organizations as a successor of Ant. For more details visit – http://en.wikipedia.org/wiki/Apache_Maven
Scope
This document is primarily targeted to developers and technical leads working with Apache Maven on Java EE projects. This document captures the best practices of using Maven for Java EE projects and acts as a guideline for new projects migrating to Maven. The reader of this document is assumed to have a basic knowledge of key Maven features, POM fundamentals and Maven goals. Others please start with the External documentation section which points to various Maven articles, primer articles, faqs on the web.
This document is divided in four parts:
- Migrating to Maven
- Maven best practices
- External Documentation
- Maven FAQ
Migrating to Maven
The first step would be download an eBook €œBetter Builds with Maven€ and follow the guidelines given in the book. This is one of the most comprehensive books available on Maven. This book is available at this location: www.topazproject.org/trac/attachment/wiki/MavenInfo/BetterBuildsWithMaven.pdf?format=raw
Note: If link is broken in future, googling the book name should help. This book is available free on many sites
Maven Best practices
This section explains how to organize Maven and use in a very effective way
Project Dependencies Management
Maven supports transitive dependencies. But using this feature leads to complexity and unforeseen errors. Managing the project dependencies explicitly is always recommended. This ensures that there will be no room for ambiguity. The side effect of this is that the artifacts list can be come very lengthy. Below are the guidelines on how to organize this in an effective manner.
- Define artifact’s version as property. Example:
<properties>
<commons-collections.version>3.2</commons-collections.version>
<commons-logging.version>1.0.4</commons-logging.version>
<freemarker.version>2.3.8</freemarker.version>
<junit.version>3.8.1</junit.version>
<ognl.version>2.6.9</ognl.version>
<servlet.version>2.3</servlet.version>
<spring.version>2.0</spring.version>
<webwork.version>2.2.4</webwork.version>
<xwork.version>1.2.1</xwork.version>
</properties>
- Organize dependencies as list, sorted in alphabetical, ordered and grouped by scope. Example:
<dependencies>
<!– explicit compile dependencies –>
<dependency>
<groupId>freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>${ognl.version}</version>
</dependency>
<!– implicit compile dependencies –>
<dependency><!– webwork dep –>
<groupId>com.opensymphony.oscore</groupId>
<artifactId>oscore</artifactId>
<version>${webwork.version}</version>
</dependency>
<!– explicit test dependencies –>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>$ {junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<!– provided dependencies –>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>$ {servlet.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
Managing Profiles
Profiles allow changing build process for different target environments. As a general rule, try to make one profile per environment. Profile name should be self-describing. Example:
env-dev – for build deployed on dev machine
env-uat – for build deployed on test machine (for User Acceptance Tests)
env-prod – for build deployed on prod machine
integration-test – for integration testing purposes
continuous-integration – for continuous integration purposes
Profiles can extend default (top level) properties. Example:
<profiles>
<profile>
<id>env-dev</id>
<properties>
<application.database.url>
<![CDATA[jdbc:oracle:thin:1521:007:ABCD]]>
</application.database.url>
<jdbc.groupId>oracle</jdbc.groupId>
<jdbc.artifactId>ojdbc14</jdbc.artifactId>
<jdbc.version>10.2.0.3.0</jdbc.version>
</properties>
<profile>
</profiles>
<!– default properties (for local developer environment) –>
<properties>
<application.database.url>
<![CDATA[jdbc:mysql://localhost/application]]>
</application.database.url>
<jdbc.groupId>mysql</jdbc.groupId>
<jdbc.artifactId>mysql-connector-java</jdbc.artifactId>
<jdbc.version>5.0.5</jdbc.version>
</properties>
The resources to be filtered are defined by the “resources” build element. Example, all environment specific settings are stored in application.properties file:
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>application.properties</include>
</includes>
</resource>
</resources>
<resources>
The desired profile can be activated via the -P argument on the command line:
mvn -P env-dev package
Integrating with Eclipse
Some useful information at this site – http://docs.codehaus.org/display/MAVENUSER/Eclipse+Integration
Execute the following command to set M2_REPO classpath variable:
mvn -Declipse.workspace=<path-to-eclipse-workspace> eclipse:add-maven-repo
Execute the following command to create Eclipse project:
mvn eclipse:eclipse
Additional eclipse plug-in properties can be added the pom.xml as shown below
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<wtpversion>1.5</wtpversion>
</configuration>
</plugin>
Integrating with NetBeans IDE
NetBeans Wiki maintains an updated wiki on everything related to NetBeans & Maven integration.
The wiki is accessible at: http://wiki.netbeans.org/MavenBestPractices
Generating project site
Execute the following command to generate project site:
mvn site
You can customize generated site content by editing src/site/site.xml file.
<project name=”Project Name”>
<skin>
<groupId>org.apache.maven.skins</groupId>
<artifactId>maven-default-skin</artifactId>
<version>1.0</version>
</skin>
<body>
${reports}
</body>
</project>
Additional reports to project site can be added. Example:
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
</plugin>
<!– Clover coverage
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clover-plugin</artifactId>
</plugin>
–>
<!– Cubertura coverage –>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
</plugin>
</plugins>
</reporting>
Adding additional meta information about project. Example:
<developers>
<developer>
<id>venkat</id>
<name>Venkat Thota</name>
<email>vthota@indiaosl.nl</email>
<timezone>+5:30</timezone>
</developer>
<developer>
<id>prasanna</id>
<name>Prasanna</name>
<email>p.padmanabhan@indiaosl.nl</email>
<timezone>+5:30</timezone>
</developer>
<developer>
<id>prabhu</id>
<name>Prabhu</name>
<email>p.santana@indiaosl.nl</email>
<timezone>+5:30</timezone>
</developer>
</developers>
<issueManagement>
<system>jira</system>
<url>http://jira.intranet.isense.com/jira/browse/projectname</url>
</issueManagement>
<ciManagement>
<system>continuum</system>
<url>http://continuum.isense.com</url>
<notifiers>
<notifier>
<type>mail</type>
<sendOnError>true</sendOnError>
<sendOnFailure>true</sendOnFailure>
<sendOnSuccess>false</sendOnSuccess>
<sendOnWarning>false</sendOnWarning>
</notifier>
</notifiers>
</ciManagement>
Generating project sources and javadocs
Execute the following command to generate jar file with project sources:
mvn source:jar
Execute the following command to generate jar file with project javadocs:
mvn javadoc:jar
Webdeveloping with jetty
Execute the following command to start jetty with application in place. This means any changes made to the ftl/jsp files are visible after page refresh.
mvn jetty:run
Config example:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.4</version>
<configuration>
<scanIntervalSeconds>5</scanIntervalSeconds>
<connectors>
<connector implementation=”org.mortbay.jetty.nio.SelectChannelConnector”>
<port>${jetty.port}</port>
</connector>
</connectors>
<webDefaultXml>src/main/webapp/WEB-INF/jetty.xml</webDefaultXml>
</configuration>
<dependencies>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-nologger</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>
Jetty buffers static content for webapps such as html files, css files, images etc and uses memory mapped files to do this if the NIO connectors are being used. The problem is that on Windows, memory mapping a file causes the file to be locked, so that the file cannot be updated or replaced. The provided jetty.xml (Jetty default web application descriptor) solves the problem with locked files.
There is also a Maven property ${jetty.port}. It allows developer to change default Jetty port number (in case of conflict with another service).
Integration testing
Execute the following command to execute integration tests:
mvn -P integration-test integration-test
First, you have to install container as runtime environment (For e.g. Tomcat container) for integration testing and configure Cargo maven plugin to use it. Below is an example with Cargo (container management), Selenium (web tests) and DBUnit (import initial data):
<profile>
<id>integration-test</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<configuration>
<wait>${cargo.wait}</wait>
<container>
<containerId>tomcat5x</containerId>
<home>${cargo.container.home}</home>
</container>
<configuration>
<home>${project.build.directory}/${cargo.container}/container</home>
<properties>
<cargo.hostname>${cargo.host}</cargo.hostname>
<cargo.servlet.port>${cargo.port}</cargo.servlet.port>
</properties>
</configuration>
</configuration>
<executions>
<execution>
<id>start-container</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>selenium-maven-plugin</artifactId>
<version>1.0-beta-2-SNAPSHOT</version>
<configuration>
<port>${selenium.port}</port>
<background>${selenium.background}</background>
<multiWindow>true</multiWindow>
</configuration>
<executions>
<execution>
<id>start-selenium</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start-server</goal>
</goals>
</execution>
<execution>
<id>stop-selenium</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop-server</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.3</version>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>surefire-it</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/integration/IntegrationTestSuite.java</include>
</includes>
</configuration>
/execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>dbunit-maven-plugin</artifactId>
<version>1.0-beta-1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<!– properties specific to integration tests environment e.g database
connection parameters –>
</properties>
</profile>
Next, configure surefire plugin to prevent executing integration tests in regular test phase.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.3</version>
<configuration>
<excludes>
<exclude>**/integration/*Test.java</exclude>
<exclude>**/integration/*TestSuite.java</exclude>
</excludes>
</configuration>
</plugin>
Continuous Integration
The following command line is recommended as a goal in the builder configuration:
mvn –batch-mode –fail-at-end -P continuous-integration clean package site site:deploy
External documentation
Internet URL
Maven FAQ
http://maven.apache.org/general.html
http://docs.codehaus.org/display/MAVENUSER/FAQs-1
Maven Issue Tracking
http://maven.apache.org/issue-tracking.html
Mailing Lists / User Forum
http://maven.apache.org/mail-lists.html
Books
Better Builds with Maven
Maven, The definitive guide
Articles
Keep Your Maven Projects Portable throughout the Build Cycle (http://www.devx.com/Java/Article/32386)
The Maven 2 POM demystified
(http://www.javaworld.com/javaworld/jw-05-2006/jw-0529-maven.html)
http://maven.apache.org/articles.html
Learn by example
AppFuse
http://appfuse.org/display/APF/Development+Environment
http://appfuse.org/display/APF/Maven+2
FAQ
How do I enable debugging on Maven Jetty?
Use mvnDebug command instead of mvn.
mvnDebug jetty:run
How can I get help about maven plugins?
You can search for plugin documentation on the web, or use mvn command.
For getting help about eclipse plugin execute the following command:
mvn help:describe -Dplugin=eclipse €€œDmedium
If you need more information about particular goal, please execute:
mvn help:describe -Dplugin=eclipse -Dmojo=eclipse €€œDfull
Debugging with the Maven Jetty Plugin in Eclipse
Please refer this link for the details – http://docs.codehaus.org/display/JETTY/Debugging+with+the+Maven+Jetty+Plugin+inside+Eclipse







