Make was quite a handy tool in its day. Ant has revolutionized automated builds. Maven has taken build transparency and automated development and deployment to a whole new level. If you haven’t caught the maven bug yet, it’s time to take a deeper look now that the next generation—Maven 2—is available. If you’re already working with Maven but are interested in M2, scroll down a little and get started!
Why? Ant Works for Me!
If you hang out with the types of developers I hang out with, you have probably come into contact with a John at some point. John is a friend and coworker—a mentor. as a matter of fact. One word can sum up his reaction the first time I introduced him to Ant several years ago. Why?
John loved his make files, and quite frankly, he could build a kitchen sink with make and a few shell scripts. It took several months for me to convince John that Ant was more than a poor man’s make. It took more than a simple comparison. John just didn’t get it because Ant changed the playing field. Ant was powerful because it expanded automated builds beyond what they had ever been before. Now that he’s taken the Ant plunge, Now, after taking the Ant Plunge, John can build the same kitchen sink in half the time. The increased productivity is easily explained. John no longer has to create pipes, faucets, and drains by hand. Instead, he uses the prebuilt ones that are readily available.
Maven is to Ant what Ant was to Make. Maven supercedes Ant. Maven provides the same functionality that has been traditionally present in a build tool but introduces new concepts that haven’t been considered in previous build systems. So, what are these new concepts? Why is Maven so great? You should explore.
Understanding Maven Concepts
Unlike Ant, Maven projects do not require the development of one or more build files. Instead, Maven introduces the concept of a project descriptor, or POM. The POM (Project Object Model) describes the project, providing all of the information needed to perform a series of prebuilt tasks. These tasks, or goals as their are called in Maven, use the POM information to execute appropriately. When necessary (almost never for a typical project), plugins can be developed and utilized across multiple projects in the same manner as prebuilt goals.
A project’s POM consists of very basic information about a project, such as the type of project (a jar library, war, and so forth); however, this simple concept of centralizing this information into a single XML file is very powerful. As you will see, by centralizing this information, Maven is able to provide goals that build, assemble, deploy, document, report, produce metrics, and do much more with your project. The POM is often referred to as pom.xml.
Goals, What not How
By introducing a project descriptor, Maven requires a developer to provide information about what is being built. This differs from traditional build systems (like make or ant) where developers instruct the system how to build. This change eliminates the need for developers to rebuilt the same functionality over and over again.
Imagine how many people in the world have written a compile target for ant. Think you’re the only one to write a war target? Think again! By providing customizable goals, this duplication is eliminated and developers can focus on developing business logic rather than developing a build system. In maven, once your POM is defined, add a source file and run the compile goal. Boom! Compiled!
Utilize Best Practices
Have you ever checked out a clean version of a project from source control only to spend the next 15 minutes trying to figure out where the source code is located, how the build file pieces everything together, and what version of jdom is supposed to be in your class path? If not, you’re lucky. You either only work for with projects that all have the same build practices or you have some great documentation. For the rest of us, there’s Maven.
Maven does not enforce best practices for projects. It strongly supports them. Typically, you will find that Maven projects are very consistent in their project setup. They mirror the same directory structures and utilize the same goals. This consistency, along with the centralized dependencies (discussed below), makes it easy to check out a project and work with it immediately. Want to find the Java source code for a Maven project. If it follows conventions, look under src/main/java. Looking for the Java test code? Take a peek at src/test/java. Want to build a distribution? Check out the assembly plugin and run assembly:assembly. How about running the tests? The test goal should do it.
Of course, not all projects are exactly the same, and any build tool needs to be customizable. Maven supports projects with unconventional layouts. Even in these instances Maven can help. Directory locations and custom goals can be discovered by simply looking at the POM.
Configuring dependencies within a build system is often a nightmare. If your build depends upon specific library locations, each developer must maintain the same directory structure and libraries. Libraries that are required for multiple projects often get duplicated throughout the file system. Another alternative, checking dependencies into the source code repository, requires placing compiled binaries, which may be under development themselves, into the repository.
Maven introduces the concept of centralized managed dependencies. In Maven, the build system itself is responsible for downloading and adding dependencies to the appropriate classpath. Projects define their external dependencies in their POM. Maven’s dependency mechanism is then responsible for retrieving the dependency when necessary.
To optimize performance and reduce storage requirements, Maven stores all retrieved dependencies within a single repository. When it encounters an unknown dependency, it searches its external repositories, downloads the dependency, and places within its local dependency.
What’s New in Maven 2
Maven 2.0 is a rewrite of the popular Maven application to achieve a number of goals, and to provide a stable basis to take it into the future. It is currently available as a Technology Preview (from the Maven 2 Web site).
Maven 2 project descriptors allow for the scoping of project dependencies. Scopes allow for dependencies that are not needed in every environment. The four available scopes are:
- compile indicates that the dependency is required for compilation and execution. These dependencies often will be provided with distributions and bundled with deployments.
- provided indicates that the dependency is required for compilation but should not be bundled with deployments and distributions. These dependencies should be provided by an external source (typically a container) during runtime. Most Java extension API dependencies (such as servlet-api) will be defined in the provided scope.
- runtime indicates a dependency that is not needed for compilation but is needed at runtime. Many runtime dependencies are implementations of other APIs. Often, these dependencies are injected or configured. A good example of a runtime dependency is a JDBC driver implementation.
- test dependencies are required for the execution of unit tests. These dependencies are not made available in distributions and deployments.
Maven 2 introduces the concept of Transitive dependencies. Direct dependencies are those upon which a project explicitly depends. Typically, a project will not compile without direct dependencies. Transitive dependencies are those which direct dependencies themselves depend upon.
Consider the following example:
+-- mavenized-webapp + -- struts + -- commons-logging + -- castor
In this example, the mavenized-webapp project has two direct dependencies, Struts and Castor. In this example, the commons-logging dependency would be considered a transitive dependency because it is not directly required by mavenized-webapp.
When utilizing Maven 1, transitive dependencies must be explicitly defined as a dependency to be included within testing and execution classpaths and distributions. Maven 2’s dependency mechanism will take care of determining transitive dependencies. This eliminates the need for the dependency to be defined within the project descriptor.
Lifecycles are groupings of goals that define a process for building a project. Goals are bound to lifecycles to define a sequence that must be accomplished to produce results. Lifecycles ensure that users building projects with Maven only need to learn a small set of commands. Whereas Maven defines a default set of goals for each typical lifecycle, developers can bind additional goals to lifecycles to transparently add functionality to a build.
Typical lifecycles include:
- compile—compile the source code
- test—unit test the compiled source.
- package—bundle the compile and tested source code into a distributable package, such as a jar or war.
- integration-test—employ the package as necessary and run integration tests.
- install—install the package into the local repository. This allows the package to be utilized as a dependency of other projects.
- deploy—copies the final package into the remote repositories for sharing and use with other projects.
Note: Unit tests should not require the classes to be packaged or deployed.
As you can see, each lifecycle depends upon and builds upon the next. As a result, when a goal is bound to the compile lifecycle, all lifecycles will ensure its execution.
Lifecycles can be executed in conjunction with standalone goals. For instance, commonly the clean:clean goal is executed before the install goal. This is done by invoking:
m2 clean:clean install
Native Multiproject Support
One of the best practices that Maven strongly encourages is the idea that a single project should result in a single artifact, or package, being created. This best practice results in simplified builds and organized project structures. By natively supporting a hierarchical structure of projects, Maven now makes developing enterprise projects which implement this approach easier.
Maven 1 included a plugin for working with related of projects—or multiprojects. Maven 2 takes multiproject builds a step further by including a specialized parent descriptor and native multiproject execution support. As a result, any goals or lifecycles invoked upon a multiproject POM will result in each subproject goal being achieved.
Several other changes are included in Maven 2. Maven has been rewritten to be smaller and faster. The architectural changes make embedding Maven into other tools easier and allows for faster command line execution. Maven 2 depends upon fewer dependencies, resulting in a smaller distribution.
The way extensions are made to Maven has been changed in Maven 2. Instead, developers are encouraged to utilize JavaBeans for enhancements. Whereas scripting is allowed (through marmalade support—which includes a jelly facade), it is no longer the tool of choice. All extensions are made through the development of plugins (projects can no longer be customized through scripting in the maven.xml).
The introduction of a settings.xml file replaces the need for properties files. The settings file can be defined at a system, user, or project level. Settings are used to define private configuration information, including usernames and passwords. Project properties (including plugin configuration) are now all defined in the pom.xml.
Take the Plunge
Understanding Maven 2 Project Types
The first step to creating a maven project is determining which project template, or archetype, your Maven project should be configured as. Archetypes define which type of artifact will be produced by a project. Several archetype templates exist. The standard archetype will produce a standard library jar file. Templates also exist for webapps/wars, Maven plugin projects, documentation Web sites, and more.
Creating Project Descriptors
The archetype:create goal can be utilized to create a basic pom.xml for you project. This basic POM will allow you to execute all of the lifecycles and goals associated with any maven project. The archetype:create goal should be executed as follows:
m2 archetype:create -DgroupdId=com.daviddewolf.maven -DartifactId=example
This simple command creates the basic infrastructure needed for a project. It creates the pom.xml as well as the basic directory structure of for both source and test code. The following structure indicates the directories and files produced by issuing the archetype:goal command as listed above.
+ root - pom.xml + src + main + java + com + daviddewolf + maven + example - App.java + test + java + com + daviddewolf + maven + example - TestApp.java
This simple structure that is created is enough to get started with Maven. All of the lifecycles now can be utilized. A simple invocation of the install lifecycle will compile, test, and package the application and then deploy it to the local system repository for use by other projects.
Once created, the POM can be customized to meet the requirements of the specific project. Documentation on the Maven POM can be found on the Maven2 Web site.
Goals You Should Know About
The introduction of lifecycles in Maven 2 has greatly reduced the number of goals that a developer must be aware of. Still, the following goals will undoubtedly be found useful (and can be utilized as soon as the basic descriptor has been generated).
- clean:clean Clean all artifacts and intermediary files created by Maven
- idea:idea Generate project files for the IntelliJ IDEA IDE
- eclipse:eclipse Generate project files for the Eclipse IDE
- javadoc:javadoc Generate the javadocs for the project
- antrun:run Run a specified ant target
- clover:clover Generate a coverage report for the project
- checkstyle:checkstyle Generate a checkstyle report for the project
- site:site Generate a documentation Web site for the project. This site will include many information reports concerning the project.
For more information concerning these and other goals, see the Maven2 Web site.
Learning Maven is not difficult; it simply requires a willingness to accept a new philosophy for building applications. Maven utilizes a a centralized project descriptor to intelligently build applications with prebuilt build tools. This differs greatly from ant and other build tools in that project developers are no longer required to write build systems by using a comprehensive set of utilities. This change will save development teams significant time and has started a revolution in build tools.
It is important to remember that Maven2 is still currently only released as Beta Software. Although it is mature enough to utilize in most projects, all of the Maven 1 plugins have not yet been migrated to Maven 2. With time, Maven 2 will become more widely accepted and the number of available plugins will grow beyond what is even available in Maven 1.
About the Author
David DeWolf is a Senior Architect and Agile Coach at Digital Focus where he mentors agile practices such as automated builds, continuous integration, and test first development. Through his experience, he has found Maven to be the most productive project management (build) tool for achieving these practices. David is a member of the Apache Portals Project Management Committee and an active committer to Apache Pluto.