Maven’s Principles
According to Christopher Alexander “patterns help create a shared language for communicating insight and experience about problems and their solutions”. The following Maven principles were inspired by Christopher Alexander’s idea of creating a shared language:
- Convention over configuration
- Declarative execution
- Reuse of build logic
- Coherent organization of dependencies
Maven provides a shared language for software development projects. As mentioned earlier, Maven provides a structured build life cycle so that problems can be approached in terms of this structure. Each of the principles above enables developers to describe their projects at a higher level of abstraction, allowing more effective communication and freeing team members to get on with the important work of creating value at the application level. This chapter will examine each of these principles in detail. You will see these principles in action in the following chapter, when you create your first Maven project.
Convention Over Configuration
One of the central tenets of Maven is to provide sensible default strategies for the most common tasks, so that you don’t have to think about the mundane details. This is not to say that you can’t override Maven’s defaults, but the use of sensible default strategies is highly encouraged, so stray from these defaults when absolutely necessary only.
This “convention over configuration” tenet has been popularized by the Ruby on Rails (ROR) community and specifically encouraged by ROR’s creator David Heinemeier Hansson who summarizes the notion as follows:
“Rails is opinionated software. It eschews placing the old ideals of software in a primary position. One of those ideals is flexibility, the notion that we should try to accommodate as many approaches as possible, that we shouldn’t pass judgment on one form of development over another. Well, Rails does, and I believe that’s why it works.
With Rails, you trade flexibility at the infrastructure level to gain flexibility at the application level. If you are happy to work along the golden path that I’ve embedded in Rails, you gain an immense reward in terms of productivity that allows you to do more, sooner, and better at the application level.
One characteristic of opinionated software is the notion of ‘convention over configuration’. If you follow basic conventions, such as classes are singular and tables are plural (a person class relates to a people table), you’re rewarded by not having to configure that link. The class automatically knows which table to use for persistence. We have a ton of examples like that, which all add up to make a huge difference in daily use.”
David Heinemeier Hansson articulates very well what Maven has aimed to accomplish since its inception (note that David Heinemeier Hansson in no way endorses the use of Maven, he probably doesn’t even know what Maven is and wouldn’t like it if he did because it’s not written in Ruby yet!): that is that you shouldn’t need to spend a lot of time getting your development infrastructure functioning Using standard conventions saves time, makes it easier to communicate to others, and allows you to create value in your applications faster with less effort.
With Maven you slot the various pieces in where it asks and Maven will take care of almost all of the mundane aspects for you. You don’t want to spend time fiddling with building, generating documentation, or deploying. All of these things should simply work, and this is what Maven provides.
There are three primary conventions that Maven employs to promote a standardized development environment:
- Standard directory layout for projects
- The concept of a single Maven project producing a single output
- Standard naming conventions
Reuse of Build Logic
As you have already learned, Maven promotes reuse by encouraging a separation of concerns (SoC) . Maven puts this SoC principle into practice by encapsulating build logic into coherent modules called plugins. Maven can be thought of as a framework that coordinates the execution of plugins in a well defined way.
In Maven there is a plugin for compiling source code, a plugin for running tests, a plugin for creating JARs, a plugin for creating Javadocs, and many other functions. Even from this short list of examples you can see that a plugin in Maven has a very specific role to play in the grand scheme of things. One important concept to keep in mind is that everything accomplished in Maven is the result of a plugin executing. Plugins are the key building blocks for everything in Maven.
Declarative Execution
Everything in Maven is driven in a declarative fashion using Maven’s Project Object Model (POM) and specifically, the plugin configurations contained in the POM. The execution of Maven’s plugins is coordinated by Maven’s build life cycle in a declarative fashion with instructions from Maven’s POM.
Maven’s project object model (POM)
Maven is project-centric by design, and the POM is Maven’s description of a single project. Without the POM, Maven is useless – the POM is Maven’s currency. It is the POM that drives execution in Maven and this approach can be described as model-driven or declarative execution.
The POM below is an example of what you could use to build and test a project. The POM is an XML document and looks like the following (very) simplified example:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
This POM will allow you to compile, test, and generate basic documentation. You, being the observant reader, will ask “How this is possible using a 15 line file?”. The answer lies in Maven’s implicit use of its Super POM.
Maven’s Super POM carries with it all the default conventions that Maven encourages, and is the analog of the Java language’s java.lang.Object class.
In Java, all objects have the implicit parent of java.lang.Object. Likewise, in Maven all POMs have an implicit parent in Maven’s Super POM. The Super POM can be rather intimidating at first glance, so if you wish to find out more about it you can refer to Appendix B. The key feature to remember is the Super POM contains important default information so you don’t have to repeat this information in the POMs you create.
The POM contains every important piece of information about your project. The POM shown previously is a very simple POM, but still displays the key elements that every POM contains.
project– This is the top-level element in all Mavenpom.xmlfiles.modelVersion– This required element indicates the version of the object model that the POM is using. The version of the model itself changes very infrequently, but it is mandatory in order to ensure stability when Maven introduces new features or other model changes.groupId– This element indicates the unique identifier of the organization or group that created the project. ThegroupIdis one of the key identifiers of a project and is typically based on the fully qualified domain name of your organization. For exampleorg.apache.maven.pluginsis the designatedgroupIdfor all Maven plugins.artifactId– This element indicates the unique base name of the primary artifact being generated by this project. A typical artifact produced by Maven would have the form --.. (for example,myapp-1.0.jar). Additional artifacts such as source bundles also use theartifactIdas part of their file name.packaging– This element indicates the package type to be used by this artifact (JAR, WAR, EAR, etc.). This not only means that the artifact produced is a JAR, WAR, or EAR, but also indicates a specific life cycle to use as part of the build process. The life cycle is a topic dealt with later in this chapter. For now, just keep in mind that the selected packaging of a project plays a part in customizing the build life cycle. The default value for the packaging element is jar so you do not have to specify this in most cases.version– This element indicates the version of the artifact generated by the project. Maven goes a long way to help you with version management and you will often see the SNAPSHOT designator in a version, which indicates that a project is in a state of development.name– This element indicates the display name used for the project. This is often used in Maven’s generated documentation, and during the build process for your project, or other projects that use it as a dependency.url– This element indicates where the project’s site can be found.description– This element provides a basic description of your project.
For a complete reference of the elements available for use in the POM please refer to the POM reference at http://maven.apache.org/maven-model/maven.html.
Maven’s build life cycle
Software projects generally follow similar, well-trodden build paths: preparation, compilation, testing, packaging, installation, etc. The path that Maven moves along to accommodate an infinite variety of projects is called the build life cycle. In Maven, the build life cycle consists of a series of phases where each phase can perform one or more actions, or goals, related to that phase. For example, the compile phase invokes a certain set of goals to compile a set of classes.
In Maven you do day-to-day work by invoking particular phases in this standard build life cycle. For example, you tell Maven that you want to compile, or test, or package, or install. The actions that have to be performed are stated at a high level, and Maven deals with the details behind the scenes. It is important to note that each phase in the life cycle will be executed up to and including the phase you specify. So, if you tell Maven to compile, Maven will execute the validate, initialize, generate-sources, process-sources, generate-resources, and compile phases that precede it automatically.
The standard build life cycle consists of many phases and these can be thought of as extension points. When you need to add some functionality to the build life cycle you do so with a plugin. Maven plugins provide reusable build logic that can be slotted into the standard build life cycle. Any time you need to customize the way your project builds you either use an existing plugin, or create a custom plugin for the task at hand. See Chapter 2.7 Using Maven Plugins and Chapter 5 Developing Custom Maven Plugins for examples and details on how to customize the Maven build.
Coherent Organization of Dependencies
We are now going to delve into how Maven resolves dependencies and discuss the intimately connected concepts of dependencies, artifacts, and repositories. If you recall, our example POM has a single dependency listed for Junit
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
This POM states that your project has a dependency on JUnit, which is straightforward, but you may be asking yourself “Where does that dependency come from?” and “Where is the JAR?” The answers to those questions are not readily apparent without some explanation of how Maven’s dependencies, artifacts and repositories work. In “Maven-speak” an artifact is a specific piece of software.
In Java, the most common artifact is a JAR file, but a Java artifact could also be a WAR, SAR, or EAR file. A dependency is a reference to a specific artifact that resides in a repository. In order for Maven to attempt to satisfy a dependency, Maven needs to know what repository to search as well as the dependency’s coordinates. A dependency is uniquely identified by the following identifiers: groupId, artifactId and version.
At a basic level, we can describe the process of dependency management as Maven reaching out into the world, grabbing a dependency, and providing this dependency to your software project. There is more going on behind the scenes, but the key concept is that Maven dependencies are declarative.
In the POM you are not specifically telling Maven where the dependencies are physically located, you are simply telling Maven what a specific project expects.
Maven takes the dependency coordinates you provide in the POM, and it supplies these coordinates to its own internal dependency mechanisms. With Maven, you stop focusing on a collection of JAR files; instead you deal with logical dependencies. Your project doesn’t require junit-3.8.1.jar, instead it depends on version 3.8.1 of the junit artifact produced by the junit group. Dependency Management is one of the most powerful features in Maven.
When a dependency is declared within the context of your project, Maven tries to satisfy that dependency by looking in all of the remote repositories to which it has access, in order to find the artifacts that most closely match the dependency request. If a matching artifact is located, Maven transports it from that remote repository to your local repository for project use.
Maven has two types of repositories: local and remote. Maven usually interacts with your local repository, but when a declared dependency is not present in your local repository Maven searches all the remote repositories to which it has access to find what’s missing. Read the following sections for specific details regarding where Maven searches for these dependencies.
Local Maven repository
When you install and run Maven for the first time, it will create your local repository and populate it with artifacts as a result of dependency requests. By default, Maven creates your local repository in <user_home>/.m2/repository. You must have a local repository in order for Maven to work. The following folder structure shows the layout of a local Maven repository that has a few locally installed dependency artifacts such as junit-3.8.1.jar.

Figure 1-1: Artifact movement from remote to local repository
So you understand how the layout works, take a closer look at one of the artifacts that appeared in your local repository,. In theory, a repository is just an abstract storage mechanism, but in practice the repository is a directory structure in your file system. We’ll stick with our JUnit example and examine the junit-3.8.1.jar artifact that are now in your local repository.
Above you can see the directory structure that is created when the JUnit dependency is resolved. On the next page is the general pattern used to create the repository layout:

Figure 1-2: General pattern for the repository layout
If the groupId is a fully qualified domain name (something Maven encourages) such as x.y.z then you will end up with a directory structure like the following:

Figure 1-3: Sample directory structure
In the first directory listing you can see that Maven artifacts are stored in a directory structure that corresponds to Maven’s groupId of org.apache.maven.
Locating dependency artifacts
When satisfying dependencies, Maven attempts to locate a dependency’s artifact using the following process: first, Maven will generate a path to the artifact in your local repository; for example, Maven will attempt to find the artifact with a groupId of “junit”, artifactId of “junit”, and a version of “3.8.1” in /.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar. If this file is not present, Maven will fetch it from a remote repository.
By default, Maven will attempt to fetch an artifact from the central Maven repository at http://repo1.maven.org/maven2 If your project’s POM contains more than one remote repository, Maven will attempt to download an artifact from each remote repository in the order defined in your POM. Once the dependency is satisfied, the artifact is downloaded and installed in your local repository.
From this point forward, every project with a POM that references the same dependency will use this single copy installed in your local repository. In other words, you don’t store a copy of junit-3.8.1.jar for each project that needs it; all projects referencing this dependency share a single copy of this JAR.
Your local repository is one-stop-shopping for all artifacts that you need regardless of how many projects you are building. Before Maven, the common pattern in most projects was to store JAR files in a project’s subdirectory. If you were coding a web application, you would check the 10-20 JAR files, upon which your project relies, into a lib directory, and you would add these dependencies to your classpath.
While this approach works for a few projects, it doesn’t scale easily to support an application with a great number of small components. With Maven, if your project has ten web applications, which all depend on version 1.2.6 of the Spring Framework, there is no need to store the various spring JAR files in your project. Each project relies upon a specific artifact via the dependencies listed in a POM, and it is a trivial process to upgrade all ten web applications to Spring 2.0 by changing your dependency declarations.
Instead of adding the Spring 2.0 JARs to every project, you simply change some configurations in Maven. Storing artifacts in your SCM along with your project may seem appealing, but it is incompatible with the concept of small, modular project arrangements. Dependencies are not your project’s code, and they shouldn’t be versioned in an SCM. Declare your dependencies and let Maven take care of details like compilation and testing classpaths.