Maven provides a comprehensive approach to managing software projects. From compilation, to distribution, to documentation, to team collaboration, Maven provides the necessary abstractions that encourage reuse and take much of the work out of project builds.
For more information about Maven, visit the Maven Web site at http://maven.apache.org/.
In this article we'll explore configuring and running Maven.
Maven depends on repositories for storing and managing Java build artifacts. The Maven community provides public repositories for common libraries, such as log4j, junit, etc. The Maven runtime will cache these artifacts to a local repository when you declare them as dependencies in a project and you can install/deploy build artifacts to your own local repository, but others cannot access your local repository. Hence the need for an internal repository, which is similar to a public repository—both use webdav—except that you can publish to it!
Currently there are not many production quality repository apps, which made the selection easier. For me it came down to two choices: Archiva and Artifactory. Artifactory won—it just works. Artifactory listens on port 8081 and can be browsed at http://(host-name):8081/artifactory.
Login with admin/password and create a non administrator user account for Maven or Eclipse plugin command line client access to Artifactory; for the sake of configuration examples to follow, the user name repouser
and password repopasswd
are used.
Add the following xml snippets to your $M2_HOME/conf/settings.xml file to configure your local Maven environment to use Artifactory.
Insert this after <servers> tags.
<server>
<id>my-repo-releases</id>
<username>repouser</username>
<password>repopasswd</password>
</server>
<server>
<id>my-repo-snapshots</id>
<username>repouser</username>
<password>repopasswd</password>
</server>
Insert this after the <profiles> tag.
<profile>
<id>myprofile</id>
<repositories>
<repository>
<id>central</id>
<url>http://localhost:8081/artifactory/repo</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>snapshots</id>
<url>http://localhost:8081/artifactory/repo</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://localhost:8081/artifactory/repo</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>snapshots</id>
<url>http://localhost:8081/artifactory/repo</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</profile>
And replace commented <activeProfiles> (if exists, otherwise simply add at end of settings.xml).
<activeProfiles>
<activeProfile>myprofile<activeProfile>
</activeProfiles>
The following (simpler) addition to settings.xml is intended to accomplish the same purpose as the profile/activeProfile combo, but it doesn't work in all versions of Maven (need to test 2.1.0).
<mirrors>
<mirror>
<id>maven.mirror.com</id>
<name>My Maven Proxy</name>
<url>http://localhost:8081/artifactory/repo</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
Alternatively, each project's pom.xml file can override the default repo with essentially the same bit of xml. Insert the following into project's pom.xml (within <project> tags):
<repositories>
<repository>
<id>central</id>
<url>http://localhost:8081/artifactory/repo</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>snapshots</id>
<url>http://localhost:8081/artifactory/repo</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://localhost:8081/artifactory/repo</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>snapshots</id>
<url>http://localhost:8081/artifactory/repo</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
The general flow of Maven is: clean (only if necessary), compile, test, package, install, deploy.
clean
Removes any previously compiled classes.
compile
Compiles all the classes.
test
Runs junit or testng tests via SureFire plugin.
Use mvn test or to run isolated tests use mvn test -Dtest=NameOfTestClass.
package
Compiles and creates the appropriate file for the plugin target.
-Jar plugin -> jar file.
-War plugin -> war file.
-Ear plugin -> ear file.
install
Installs the jar file into your local Maven repository.
deploy
Installs artifacts into repository or an application to an app server.
Some 3rd party jars are not available in the public repos. Resist the temptation to create a lib folder for your project. Maintaining a hybrid approach to dependency management more than defeats the advantage of Maven. Instead, deploy the 3rd party jar to the internal repository.
Download the 3rd party jar. If the jar name does not currently conform to Maven file naming standard, change its name accordingly. For example, if the file name is foo.jar and the version is 1.2.2, then rename the jar to foo-1.2.2.jar. Deploy the jar to the internal repository with the following command line:
C:\data\temp>mvn deploy:deploy-file -DgroupId=foo \
-DartifactId=bar \
-Dversion=1.2.2 \
-Dpackaging=jar \
-Dfile=foo-1.1.1.jar \
-DrepositoryId=cim.releases \
-Durl=http://localhost:8081/artifactory/3rdp-releases@repo
Alternatively, you can upload or import artifacts via the Artifactory user interface.
Common utility libraries developed by your development team should be regarded no differently than 3rd party libraries. Versioned utility jars should be declarable as dependencies in pom.xml files just like any 3rd party jar.
The Maven install
goal will make a jar available to all projects on a single workstation, but the deploy
goal must be used to install it into the internal repository. Add the following to your utility jar's pom.xml.
<distributionManagement>
<repository>
<id>my-repo-releases</id>
<!-- id must match settings.xml: servers/server/id -->
<name>Releases</name>
<url>http://localhost:8081/artifactory/libs-releases@repo</url>
</repository>
<snapshotRepository>
<id>my-repo-snapshots</id>
<!-- id must match settings.xml: servers/server/id -->
<name>Snapshots</name>
<url>http://localhost:8081/artifactory/libs-snapshots@repo</url>
</snapshotRepository>
</distributionManagement>
Run the Maven build with the deploy
goal to install the jar into internal repository.
Maven functionality can be integrated into Eclipse via the m2eclipse plugin. You can find more information about the plugin here:
http://m2eclipse.codehaus.org/.
To install and configure the plugin, add the following to your Eclipse update sites:
http://m2eclipse.sonatype.org/update/.
Beyond simple projects, Eclipse and Maven don't integrate very well. The problem is rooted in the fact that Maven projects are typically organized in a hierarchical manner, whereas Eclipse project structure is flat. In other words, Maven allows project nesting and Eclipse doesn't. There is a workaround, but it requires some manual intervention.
The general approach is outlined here: http://maven.apache.org/plugins/maven-eclipse-plugin/reactor.html.
More specifically, follow these steps to enable Eclipse to handle a nested, multi-module Maven project:
1. Check out the parent module (top level project) into your workspace or create a Maven multi-module project if starting from scratch.
2. If the Eclipse .project, .classpath, etc. files are not checked into Software Configuration Management (SCM), run mvn eclipse:eclipse to create .project, .classpath, etc. files in the sub-modules.
3. Switch to the Java perspective in Eclipse.
4. Select the parent module and hit F5 to refresh.
5. Choose File->Import...
, Existing Projects into Workspace
, and browse into one of the sub-modules under the parent module.
6. Select the sub-module, make sure Copy projects into workspace
is not checked and click Finish.
7. Repeat steps 5 and 6 with the other sub-modules.
The submodules will appear as projects in the Eclipse Package Explorer. Changes made to code in these projects will be reflected in the parent project since projects were not copied.
You can run the Maven command from within Eclipse but it is usually faster from the command line. For bootstrapping only, disable tests.
mvn install -Dmaven.test.skip=true
In part 2 we'll discuss how to streamline dependency management of multi-module projects including JEE projects.
Read Improving Java Software Builds with Maven - Part 2