Sharing Our Passion For Technology
& continuous learning
〈  Back to Blog

Maven 3 Tutorial - Project Setup

By David Kessler | June 11, 2011 | Development

  • Overview
    • What is Maven?
    • Plugins
    • Why not Ant and Ivy?
    • But It Downloads the Internet
  • Getting Started
    • Install
    • What is a POM?
    • Convention over Configuration
    • New Project
  • Project Management
    • Parent POM
    • Local Maven Repository
    • Multiple Artifacts from a Single Source
    • Release Plugin
    • Aggregate POM
    • Dependency Management
    • Distribution Zip
  • Conclusions

Overview

What is Maven?

Maven is a software project management and comprehension tool that includes: build tools, dependency management, project reporting and much more. I say “much more” because at the core Maven is a plugin execution framework. There are plugins supported by the Maven project (http://maven.apache.org/plugins/index.html), plugins supported by Mojo Project (http://mojo.codehaus.org/plugins.html), and third party plugins.  If you can find or write a plugin, Maven can run it.

Plugins

Plugins are configured to run during specific phases in the Maven lifecycle.  The high-level phases that make up the build lifecycle include: validation, compile, test, package, integration-test, verify, install, and deploy.  An exhaustive list of build phases can be found at http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference.

Why not Ant and Ivy?

People always gravitate to this question of whether Maven is better than Ant and Ivy. Unfortunately this is not a fare comparison. Ant is a build tool and Ivy is a dependency management tool. Maven is not just a build tool or just a dependency management tool.  On the contrary, Maven is a project management tool that embodies software development best practices.

To illustrate this point I will share a story from my early years in software development. Our shop used Ant without Ivy.  Ivy didn’t become a full fledged Apache project until October, 2007. Each team had their own Ant build. As we began to move from team to team we had to find out which targets needed to be run. Over time we decided on a set of names that every team needed to implement. This helped but just because the target was named the same thing it did not mean they worked the same way.

Long story short, we began to meet to create a shared standard. During this long process we were introduced to Maven.  Ironically, Maven had accepted standards for many of the issues that we were trying address. Maven was not just a standard for our company it was a standard for all Maven users. New employees that were familiar with Maven had a good grasp of how we managed our projects.

Maven provided a standard that not only addressed our concerns as software developers. It provided a standard solution that supported our corporate: audits, disaster recovery, tracability, and the list goes on. We were trying to reinvent the wheel with Ant when the Maven community had already solved these problems.

Ant and Ivy are powerful ingredients for designing a custom build process. Maven however is a powerful solution for building and managing software. In our case we wanted a solution instead of the ingredients and tools to create our own solution.  Maven satisfied our needs and saved us significant time and money.

But It Downloads the Internet

I can’t tell you how many people have told me that they don’t like Maven because it “downloads the internet”. I find it ironic that many of them like Ivy.  I hate to break the news, but Ivy downloads the internet too. I’m not sure why this is a concern. Is there a fear that Maven will fill up their hard drive?  Disk space is cheap. The computer I am working on right now has 150GB of hard disk space and my Maven local repository is eating up 300MB of it. This computer has pleanty of room for more of the internet.

I recall the first time that I tried to setup a new project without a dependency management tool. I spent over a week trying to download the correct version of each JAR.  I was ready to take a vacation by the time I finished this ugly puzzle. After using Maven and Ivy for managing my  dependencies I will never go back. By the time I downloaded all the JARs that we needed, I felt like I had in fact downloaded the entire internet.

I love dependency management tools and they can have as much of my hard drive as they need.

Getting Started

The new stable version Maven 3.0.3 was released March 3rd of this year. This tutorial will demonstrate how to setup and run this newest version. The only system requirements for Maven are JDK 1.5 or above and an internet connection.

Install

  1. Download Maven 3.0.3 from http://maven.apache.org/download.html
  2. Unzip the contents in your root Maven folder.  In my case I unzipped it in C:/java/maven.
  3. Setup M2_HOME in your environment variables and point it to your installation directory.  In my case this is C:/java/maven/apache-maven-3.0.3.  (Note: You need to use M2_HOME instead of M3_HOME because the maven plugin for eclipse will not work with M3_HOME.)
  4. Add ‘%M2_HOME%/bin’ to your PATH variable.
  5. Open a command window and run the ‘mvn -version’ command.
  6. You should see version information about Maven 3.0.3.

What is a POM?

POM stands for “Project Object Model”.  This XML file stores information about your project.

Project Information

  1. Basic information about the project including version, artifcat type, dependencies, properties, etc.
  2. Build Settings allow you to declare custom directory structures, configure custom plugin steps, and project reporting.
  3. Project Information includes: license, organization, developers, and contributors.
  4. Environment Settings allow you to configure locations for issue management, continuous itegration, mailing lists, versin control, Maven repositories, and download urls.

If you really hate XML there are a few options. One option that has been around for a while is to use an IDE that provides tooling for Maven configuration. In my experience these are very helpful but advanced configurations will still need to be added by hand. Maven 3 breaks this wide open by introducing configuration alternatives in Groovy, Scala, Clojure, and JRuby (http://polyglot.sonatype.org/index.html). If you have an existing Maven 3 project that you want to switch from XML to a JVM language there is a translate command http://polyglot.sonatype.org/porting-languages.html.

Convention over Configuration

Maven uses project information from the pom.xml file to manage the project. The package type (pom, jar, maven-plugin, ejb, war, ear, rar, par) determines which actions are applied to your project by convention. These standard actions can be modified in the build secion of the POM. This is one of the primary reasons that comparing Ant and Maven is like comparing apples and oranges. Every action or task in Ant has to be explicitly declared for each project. While Ant allows you to extract common tasks to be reused they do not provide an industry standard. Ant is not a project management tool.  Ant merely provides the building blocks for you to create your own project management infrastructure. Maven decided that all projects should be treated the same by default. Maven hits the ground running. Ant gives you the tools to begin running.

Maven conventions include:

  1. Standard package structures
  2. Standard output directories
  3. Standard artifact structure and naming
  4. Standard build steps and order
  5. Standard 3rd party dependency repository locations
  6. Standard Release process
  7. Standard artifact publishing

These standards are best practices within our industry. Nevertheless, all of these defaults are easily extended and modified. Maven provides a simple way to focus on developing software instead of focusing on project management.

New Project

For this example I will be using Eclipse for my IDE.

  1. Download Eclipse IDE for Java EE Developers from http://www.eclipse.org/downloads/
  2. Unzip the contents in your root Eclipse folder. In my case I unzipped it in c:/java/eclipse
  3. Run the ‘eclipse.exe’ in [eclipse root]/eclipse
  4. Select a workspace location for Eclipse to store your projects.  In my case I used the default location
  5. Click on the ‘Workbench’ icon
  6. Select Help -> Install New Software
  7. Click the ‘Add...’ button
  8. Name: m2eclipse
  9. Location: http://m2eclipse.sonatype.org/sites/m2e
  10. Click ‘OK’
  11. Check the ‘Maven Integration for Eclipse’ option
  12. Click ‘Next’
  13. Click ‘Next >’
  14. Select ‘I accept the terms of the license agreements’
  15. Click ‘Finish’
  16. Click ‘Restart Now’
  17. Select Window -> Preferences
  18. Add the Maven 3 location the you installed earlier
  19. Create a new Maven project by selecting File -> New -> Other...
  20. >
  21. Select Maven -> Maven Project
  22. Check ‘Create a simple project (skip archetype selection)’
  23. Click ‘Next >’
  24. Enter Project Information
    1. Enter Group Id. This is the domain where the project is hosted. For example if the ‘Awesome’ project was hosted at GitHub then the Group Id would be ‘com.github.awesome’.
    2. Enter Artifact Id. This is the name of the resulting artifact. For example if Artifact Id is ‘awesome’ and the packaging is JAR the artifact will be named ‘awesome.jar’.
    3. The version can remain 0.0.1-SNAPSHOT
    4. Select the packaging that you desire
    5. Enter a Name. This name will show up in the console when you run Maven. This will also show up in the documentation that Maven generates
    6. Enter a Description. This is used in the documentation
    7. Click ‘Next >’
  25. Add a dependency on JUnit. One of the best features of m2eclipse plugin is the ability to search for available dependencies. (This feature does not work if you configure ‘M3_HOME’ instead of ‘M2_HOME’)
  26. Click ‘Finish’
    • src - source and resource files
    • target - compiled files, reports and generated artifacts
    • main - application/library files
    • test - test files
    • java - java files
    • resources - non java files like XML
  27. Maven’s default package structure:
  28. Open the pom.xml file.
  29. Add the compiler plugin so you can configure your JDK version. Search for the maven-compiler-plugin.
  30. For this example set the JDK version to 1.6
  31. 
    	1.6</source>
    	1.6
    
    
  32. Run ‘Maven install’ to verify that the project and environment is setup correctly
  33. The build was successful

Project Management

Parent POM

Parent POMs are an simple way to extract common configurations and reuse them in multiple projects. We will extract the compiler plugin configuration into a parent pom.

Create a new Maven Project with a packaging type of ‘pom’.

<project xmlns=”http://maven.apache.org/POM/4.0.0” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”> <modelVersion>4.0.0</modelVersion>
<groupId>com.sourceallies.parent-sample</groupId>
<artifactId>parent-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Parent Sample POM</name>
<description>This contains common company settings for all projects.</description>
<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId> 			
      <artifactId>maven-compiler-plugin</artifactId>
			<version>2.3.2</version>
			<configuration>
				<source>1.6</source>
				<target>1.6</target>
			</configuration>
		</plugin>
	</plugins>
</build>
</project>

In the child pom file add the following

<parent>
	<artifactId>parent-sample</artifactId>
	<groupId>com.sourceallies.parent-sample</groupId>
	<version>0.0.1-SNAPSHOT</version>
</parent>

Note: Update the project configuration after you change the Java version. Right click your project -> Maven -> Update Project Configuration

Local Maven Repository

It’s a good idea for organizations to host their own local Maven repository. This provides more control over the hosting and management of project dependencies. This also provides a place to easily add third-party artifacts that may not be available in external Maven repositories.

I’m most familiar with Archiva so I will set it up in this tutorial. Nevertheless, there are other Build Artifact Repository Management software available such as Sonatype.

For this example I am going to follow the steps outlined in the<a href=”http://archiva.apache.org/docs/1.3.5/quick-start.html”> Archiva Quick Start</a> guide.

  1. Download and unzip Archiva
  2. Open a Commandline window
  3. Navigate to the ‘bin’ directory in the exploded Archiva directory
  4. Run ‘archiva.bat console’
  5. Setup a new Admin account

Add this repository to the ‘settings.xml’ file in [Maven 3 Home]/conf. This sets Archiva as your dependency repository. Add this to the ‘mirrors’ section.

<mirror>
	<id>archiva.default</id>
	<url>http://localhost:8080/archiva/repository/internal/</url>
	<mirrorOf>*</mirrorOf>
</mirror>

To publish your artifacts to Archiva add the following to the ‘settings.xml’ file. Replace archiva-deployment-user with the Admin username (this is ‘admin’ by default) that you setup. Replace archiva-deployment-pwd withthe Admin password that you setup.

<server>
	<id>archiva.internal</id>
	<username>{archiva-deployment-user}</username>
	<password>{archiva-deployment-pwd}</password>
</server>
<server>
	<id>archiva.snapshots</id>
	<username>{archiva-deployment-user}</username>
	<password>{archiva-deployment-pwd}</password>
</server> 	

Add the following to the parent pom in the ‘parent-sample’ project. This tells your project where to publish it’s artifacts.

<distributionManagement>
	<repository>
		<id>archiva.internal</id>
		<name>Internal Release Repository</name>
		<url>http://localhost:8080/archiva/repository/internal/</url>
	</repository>
	<snapshotRepository>
		<id>archiva.snapshots</id>
		<name>Internal Snapshot Repository</name>
		<url>http://localhost:8080/archiva/repository/snapshots/</url>
	</snapshotRepository>
</distributionManagement>

Since the ‘distributionManagement’ information was modified you need to first run ‘mvn clean install’ on the ‘parent-example’ project. Then run ‘mvn clean deploy’ on the ‘example’ project. This will download the dependencies to the local Archiva dependency repository and publish the ‘example.jar’ to Archiva.

Navigate to http://localhost:8080/archiva/repository/snapshots/com/sourceallies/sample/sample/ to view the published files for the ‘example’ project.

Navigate to http://localhost:8080/archiva/repository/internal/ to view the dependencies that were downloaded during the installation of the ‘parent-example’ project and the deployment of the ‘example’ project. Refer to the Archiva Administration Guide for more details on hosting an internal Maven repository.

Multiple Artifacts from a Single Source

Maven restricts projects to one artifact per ‘pom’ file. While we can argue whether this is the right approach, there is a simple solution. If you have a project called ‘sample-service’ that produces a WAR and you want to create a JAR from the same source code then you need to create a second package containing a single pom file.

|_sample-service
 |_src
 |_pom.xml
|_sample-client
 |_pom.xml

The ‘sample-client’ pom is configured to point to ‘sample-service’ for it’s source files. This should be added to the ‘build-helper-maven-plugin’ configuration. Please refer to http://mojo.codehaus.org/build-helper-maven-plugin/usage.html for more information.

<configuration>
	<sources>
		<source>**/src/main/java/**</source>
		<source>**/src/main/resources/**</source>
	</sources>
</configuration>

Release Plugin

This is a very powerful plugin. If you’ve ever released a project by hand you will appreciate this plugin. One of my favorite features is that it checks to see if all of the dependencies refer to released versions. Without this automatic check you are forced to search through all of the dependency management configurations for unreleased dependencies. Depending on the size of your hierarchy this can be quite painful.

Along with this dependency check it also runs all of the tests, tags the code with the release name, and publishes the artifacts in the Maven repository. This plugin embodies the best standards for releasing within our industry. Standards that I easily forget and/or mess up. I highly recommend that you use this plugin to release your projects. Please refer to http://maven.apache.org/plugins/maven-release-plugin for more information.

Aggregate POM

An aggregate POM joins multiple modules together.

_pom.xml (aggregate)
 |_sample-service
 |_pom.xml (module 1)
 |_sample-client
 |_pom.xml (module 2)</pre>
The aggregate pom configures multiple modules in the modules section.
<pre lang=”xml”>
<project>
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mycompany.app</groupId>
	<artifactId>sample-app</artifactId>
	<version>1</version>
	<packaging>pom</packaging>
	<modules>
		<module>sample-service</module>
		<module>sample-client</module>
	</modules>
</project>

When you execute a Maven command at the aggregate level it will execute the same command on all of the configured modules. If any of these modules depends on another module Maven builds them in the correct order. This is a simple way to break up a large project into focused modules without loosing a coordinated project management strategy. While this could be replicated with Ant and Ivy it would be painful to configure and maintain. Aggregate projects are first class citizens in Maven.

Dependency Management

The ‘dependencymanagement’ section defines the acceptable versions for a project. This configuration does not result in a JAR being downloaded and included in the project. On the other hand, this is a directive specifying which version of a dependency should be used if a project depends on it. This is a simple way to force Maven to select a specific dependency version. In the case of an aggregate project it is best to add this to the aggregate pom. Configure the dependency versions in one place for all of the modules.

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>test</groupId>
			<artifactId>a</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>test</groupId>
			<artifactId>b</artifactId>
			<version>1.0</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>test</groupId>
			<artifactId>c</artifactId>
			<version>1.0</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>test</groupId>
			<artifactId>d</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>
</dependencyManagement>

Distribution Zip

Maven provides a simple way to package all of your artifacts in a single zip file. This simplifies the deployment process and increases traceability. For example I have used this feature to package my WAR, SQL, and external PDF files in the same zip file. Our deployment team extracted the WAR and PDF files and deployed them. Then they passed the SQL on to the DBA’s to run the scripts. All of the files necessary to deploy our project were packaged together and published to our Maven repository. Please refer to http://maven.apache.org/plugins/maven-assembly-plugin for more information.

Conclusions

Maven provides a holistic approach to project management. Maven provides standard conventions that can be easily modified and extended. When our development team switched from Ant/Ivy to Maven we spent less time focusing on how to build our projects. In the end Maven provided industry project management standards that we could depend on. Even when we spent hours trying to perfect our Ant build process we were getting some things wrong. We were focused on deploying the product at that moment. Unfortunately companies need to insure that deployments are traceable, dependable, and reproducible. Maven freed us up to focus on getting the product out while protecting our companies interests right out of the box.

〈  Back to Blog