
In Chapter 7, Multi-module Enterprise Project, we showed how many pieces of Maven come together to produce a fully functional multi-module build. While the example from that chapter suggests a real application - one which interacts with a database, a web service, and which, itself, presents two interfaces: one in a web application, and one on the command-line; the example project presented in that chapter is still contrived. To present the complexity of a real project would require a book far larger than the one you are now reading. Real life applications evolve over years and are often maintained by large diverse groups of developers each with a different focus. In a real world project, you are often evaluating decisions and designs made and created by others. In this chapter, we take a step back from the examples you've seen in Part I, “Maven by Example”, and we ask ourselves if there are any optimizations that might make more sense given what we now know of Maven. Maven is a capable tool which can be as simple or as complex as you need it to be. Because of this, there are often a million ways to accomplish the same task, and there is often no one, "right" way to configure your Maven project.
Don't misinterpret that last sentence as a license to go off and ask
Maven to do something it wasn't designed for. While Maven allows for a
diversity of approach, there is certainly "A Maven Way", and you'll be
more productive using Maven as it was designed to be used. All this
chapter is trying to do is communicate some of the optimizations you can
perform on an existing Maven project. Why didn't we just introduce an
optimized POM in the first place? Designing
POMs for pedagogy is a very different requirement from
designing POMs for efficiency. While it is certainly
much easier to define a certain setting in your
~/.m2/settings.xml than to declare a profile in a
pom.xml, writing a book, and reading a book is mostly
about pacing and making sure we're not introducing concepts before you are
ready. In Part I, “Maven by Example”, we've made an effort not to
overwhelm the reader with too much information, and, in doing so, we've
skipped some core concepts like the
dependencyManagement element introduced in this
chapter.
There are often instances in Part I, “Maven by Example” when the authors of this book took a short-cut or glossed over an important detail to shuffle you along to the main point of a specific chapter. You learned how to create a Maven project and you compiled and installed it without having to wade through hundreds of pages introducing every last switch and dial available to you. We've done this because we believe it is important to deliver the new Maven user to a result faster rather than meandering our way through a very long, seemingly interminable story. Once you've started to use Maven, you should know how to analyze your own projects and POMs. In this chapter, we take a step back, and take a look at what we're left with after the example from Chapter 7, Multi-module Enterprise Project.
Optimizing a Multi-Module project’s POM is best done in several passes as there are many areas to focus on. In general, we are looking for repetition within a POM and across the sibling POMs. When you are starting out, or when a project is still evolving very rapidly, it is acceptable to duplicate some dependencies and plugin configurations here and there, but as the project matures and as the number of modules increases, you will want to take some time to refactor common dependencies and configuration points. Making your POMs more efficient will go a long way to helping you manage complexity as your project grows. Whenever there is duplication of some piece of information, there is usually a better way.
If you take a look through the various POMs
created in Chapter 7, Multi-module Enterprise Project, note several
patterns of replication. The first pattern we can see is that some
dependencies such as spring and
hibernate-annotations are declared in several modules.
The hibernate dependency also has the exclusion on
javax.transaction replicated in each definition. The
second pattern of duplication to note is that sometimes several
dependencies are related and share the same version. This is often the
case when a project's release consists of several closely coupled
components. For example, look at the dependencies on
hibernate-annotations and
hibernate-commons-annotations, both are listed as
version 3.3.0.ga, and we can expect the version of both
these dependencies to change together going forward. Both the
hibernate-annotations and
hibernate-commons-annotations are components of the
same project released by JBoss, when there is a new project release, both
of these dependencies will change. The last pattern of duplication is the
duplication of sibling module dependencies and sibling module versions.
Maven provides simple mechanisms that let you factor all of this
duplication into a parent POM.
Just like in your project's source code, any time you have duplication in your POMs, you open the door a bit for trouble down the road. Duplicated dependency declarations make it difficult to ensure consistent versions across a large project. When you've only got two or three modules, this might not be a primary issue, but when your organization is using a large, multi-module Maven build to manage hundreds of components across multiple departments, one single mismatch between dependencies can cause chaos and confusion. A simple version mismatch in a project's dependency on a bytecode manipulation package called ASM three levels deep in the project hierarchy could throw a wrench into a web application maintained by a completely different group of developers which depend on that particular module. Unit tests could pass because they are being run with one version of a dependency, but they could fail disastrously in production where the bundle (war in this case) was packaged up with a different version. If you have tens of projects using something like Hibernate Annotations each repeating and duplicating the dependencies and exclusions, the mean time between someone screwing up a build is going to be very short. As your Maven projects become more complex, your dependency lists are going to grow, and you are going to want to consolidate versions and dependency declarations in parent POMs.
The duplication of the sibling module versions can introduce a
particularly nasty problem that is not directly caused by Maven and is
only learned after you've been bitten by this bug a few times. If you use
the Maven Release plugin to perform your releases, all these sibling
dependency versions will be updated automatically for you, so maintaining
them is not the concern. If simple-web version
1.3-SNAPSHOT depends on
simple-persist version 1.3-SNAPSHOT,
and you are performing a release of the 1.3 version of
both projects, the Maven Release plugin is smart enough to change the
versions throughout your multi-module project's POMs
automatically. Running the release with the Release plugin will
automatically increment all of the version in your build to
1.4-SNAPSHOT, and the release plugin will commit the
code change to the repository. Releasing a huge multi-module project
couldn't be more easy, until...
Problems occur when developers merge changes to the
POM and interferes with a release which is in progress.
Often a developer merges and occasionally mishandles the conflict on the
sibling dependency, inadvertently reverting that version to a previous
release. Since the consecutive versions of the dependency are often
compatible, it does not show up when the developer builds and won’t show
up in any continuous integration build system as a failed build. Imagine a
very complex build where the trunk is full of components at
1.4-SNAPSHOT, and now imagine that developer A has
updated a component A deep within the project's hierarchy to depend on
version 1.3-SNAPSHOT of component B. Even though most
developers are going to have 1.4-SNAPSHOT, the build is
going to succeed if version 1.3-SNAPSHOT and
1.4-SNAPSHOT of component B are compatible. Maven will
continue to build the project using the 1.3-SNAPSHOT
version of component B from developer's local repositories. Everything
seems to be going quite smoothly, the project builds, the continuous
integration build works fine, someone might have had a mystifying bug
related to component B, but they marked it up to malevolent gremlins and
moved on. Meanwhile, there is a pump in the reactor room steadily building
up pressure until something blows...
Someone, let's call them Mr. Inadvertent, had a merge conflict in
component A, and mistakenly pegged component A's dependency on component B
to 1.3-SNAPSHOT while the rest of the project marches
on. A bunch of developers have been trying to fix a bug in component B all
this time and they've been mystified as to why they can't seem to fix the
bug in production. Eventually someone looks at component A and realizes
that the dependency is pointing to the wrong version. Hopefully, the bug
wasn't large enough to cost money or lives, but Mr. Inadvertent feels
stupid and people tend to trust him a little less than they did before the
whole sibling dependency screw-up. (Hopefully, Mr. Inadvertent realizes
that this was user error and not Maven's fault, but more than likely he
starts an awful blog and complains about Maven endlessly to make himself
feel better.)
Fortunately, dependency duplication and sibling dependency mismatch
are easily preventable if you make some small changes. The first thing
we’re going to do is find all the dependencies used in more than one
project and move them up to the parent POM’s
dependencyManagement section. We’ll leave out the
sibling dependencies for now. The simple-parent pom now
contains the following:
<project>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.5.ga</version>
<exclusions>
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
...
</project>Once these are moved up, we need to remove the versions for these
dependencies from each of the POMs, otherwise they will
override the dependencyManagement defined in the parent
project. I will only show simple-model for
brevity:
<project>
...
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
</dependency>
</dependencies>
...
</project>The next thing we should do is fix the replication of the
hibernate-annotations and
hibernate-commons-annotations version since these
should match. We’ll do this by creating a property called
hibernate-annotations-version. The resulting
simple-parent section looks like this:
<project>
...
<properties>
<hibernate.annotations.version>3.3.0.ga</hibernate.annotations.version>
</properties>
<dependencyManagement>
...
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>${hibernate.annotations.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>${hibernate.annotations.version}</version>
</dependency>
...
</dependencyManagement>
...
</projectThe last issue we have to resolve is with the sibling dependencies.
One technique we could use is to also move these up to the
dependencyManagement section just like all the others
and define the versions of sibling projects in the top-level parent
project. This is certainly a valid approach but we can also solve the
version problem just by using two built-in
properties-${project.groupId} and
${project.version}. Since they are sibling
dependencies, there is not much value to be gained by enumerating them in
the parent, so we’ll rely on the built-in
${project.version} property. Since they all share the
same group, we can further future-proof these declarations by referring to
the current POM’s group using the built-in
${project.groupId} property. The
simple-command dependency section now looks like
this:
<project>
...
<dependencies>
...
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>simple-weather</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>simple-persist</artifactId>
<version>${project.version}</version>
</dependency>
...
</dependencies>
...
</project>To summarize the two optimizations we completed that reduce duplication of dependencies:
dependencyManagementIf more than one project depends on a specific dependency, you
can list the dependency in dependencyManagement.
The parent POM can contain a version and a set of
exclusions, all the child POM needs to do to
reference this dependency is use the groupId and
artifactId. Child projects can omit the version
and exclusions if the dependency is listed in
dependencyManagement.
Use ${project.version} and
${project.groupId} when referring to a sibling
project. Sibling projects almost always share the same groupId and
they almost always share the same release version. Using
${project.version} will help you avoid the
sibling version mismatch problem discussed above.
If we take a look at the various plugin configurations, we can see
the HSQLDB dependencies duplicated in several places.
Unfortunately, dependencyManagement doesn’t apply to
plugin dependencies, but we can still use a property to consolidate the
versions. Most complex Maven multi-module projects declare all versions in
the top-level POM making the top-level POM a focal
point for changes that affect the entire project. Think of version numbers
as string literals in a Java class, if you are constantly repeating a
literal, you'll likely want to make it a variable so that when it needs to
be change, you only have to change it in one place. Rolling up the version
of HSQLDB into a property in the top-level
POM yields the following properties element:
<project>
...
<properties>
<hibernate.annotations.version>3.3.0.ga</hibernate.annotations.version>
<hsqldb.version>1.8.0.7</hsqldb.version>
</properties>
...
</project>The next thing we notice is that the
hibernate3-maven-plugin configuration is duplicated in
the simple-webapp and simple-command
modules. We can manage the plugin configuration in the top-level
POM just like we managed the dependencies in the
top-level POM with the
dependencyManagement section. To do this, we use the
pluginManagement element the top-level
POM's build element.
<project>
...
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>hibernate3-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<components>
<component>
<name>hbm2ddl</name>
<implementation>annotationconfiguration</implementation>
</component>
</components>
</configuration>
<dependencies>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>${hsqldb.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
</build>
...
</project>
On larger projects, additional dependencies often tend to creep into a POM as the number of dependencies grow. As dependencies change, you are often left with dependencies which are not being used, and just as often, you may forget to declare explicit dependencies for libraries you require. Because Maven 2.x includes transitive dependencies in the compile scope, your project may compile properly, but fail to run in production. Consider a case where a project uses classes from a widely used project like Jakarta Commons Beanutils. Instead of declaring an explicit dependency on Beanutils, your project simply relies on a project like Hibernate which references Beanutils as a transitive dependency. You project may compile successfully and run just fine, but if you upgrade to a new version of Hibernate which doesn't depend on Beanutils, you'll start to have compile and runtime errors, and it won't be immediately obvious why your project stopped compiling. Also, because you haven't explicitly listed a dependency version, Maven cannot resolve any version conflicts which may arise.
A good rule of thumb in Maven is to always declare explicit dependencies for classes referenced in your code. If you are going to be importing Commons Beanutils classes, you should also be declaring a direct dependency on Commons Beanutils. Fortunately, via bytecode analysis, the Maven Dependency Plugin is able to assist you in uncovering direct references to dependencies. Using the updated POMs we previously optimized, lets look to see if any errors pop up.
$ mvn dependency:analyze
[INFO] Scanning for projects...
[INFO] Reactor build order:
[INFO] Chapter 8 Simple Parent Project
[INFO] Chapter 8 Simple Object Model
[INFO] Chapter 8 Simple Weather API
[INFO] Chapter 8 Simple Persistence API
[INFO] Chapter 8 Simple Command Line Tool
[INFO] Chapter 8 Simple Web Application
[INFO] Chapter 8 Parent Project
[INFO] Searching repository for plugin with prefix: 'dependency'.
...
[INFO] ------------------------------------------------------------------------
[INFO] Building Chapter 8 Simple Object Model
[INFO] task-segment: [dependency:analyze]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing dependency:analyze
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [dependency:analyze]
[WARNING] Used undeclared dependencies found:
[WARNING] javax.persistence:persistence-api:jar:1.0:compile
[WARNING] Unused declared dependencies found:
[WARNING] org.hibernate:hibernate-annotations:jar:3.3.0.ga:compile
[WARNING] org.hibernate:hibernate:jar:3.2.5.ga:compile
[WARNING] junit:junit:jar:3.8.1:test
...
[INFO] ------------------------------------------------------------------------
[INFO] Building Chapter 8 Simple Web Application
[INFO] task-segment: [dependency:analyze]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing dependency:analyze
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] No sources to compile
[INFO] [dependency:analyze]
[WARNING] Used undeclared dependencies found:
[WARNING] org.sonatype.mavenbook.ch08:simple-model:jar:1.0:compile
[WARNING] Unused declared dependencies found:
[WARNING] org.apache.velocity:velocity:jar:1.5:compile
[WARNING] javax.servlet:jstl:jar:1.1.2:compile
[WARNING] taglibs:standard:jar:1.1.2:compile
[WARNING] junit:junit:jar:3.8.1:testIn the truncated output shown above, you can see the output of the
dependency:analyze goal. This goal analyzes the project
to see if there are any indirect dependencies, or dependencies which are
being referenced but are not directly declared. In the
simple-model project, the dependency plugin indicates a
"used undeclared dependency" on
javax.persistence:persistence-api. To investigate
further, go to the simple-model directory and run the
dependency:tree goal which will list all of the
project's direct and transitive dependencies.
$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'dependency'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Chapter 8 Simple Object Model
[INFO] task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree]
[INFO] org.sonatype.mavenbook.ch08:simple-model:jar:1.0
[INFO] +- org.hibernate:hibernate-annotations:jar:3.3.0.ga:compile
[INFO] | \- javax.persistence:persistence-api:jar:1.0:compile
[INFO] +- org.hibernate:hibernate:jar:3.2.5.ga:compile
[INFO] | +- net.sf.ehcache:ehcache:jar:1.2.3:compile
[INFO] | +- commons-logging:commons-logging:jar:1.0.4:compile
[INFO] | +- asm:asm-attrs:jar:1.5.3:compile
[INFO] | +- dom4j:dom4j:jar:1.6.1:compile
[INFO] | +- antlr:antlr:jar:2.7.6:compile
[INFO] | +- cglib:cglib:jar:2.1_3:compile
[INFO] | +- asm:asm:jar:1.5.3:compile
[INFO] | \- commons-collections:commons-collections:jar:2.1.1:compile
[INFO] \- junit:junit:jar:3.8.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
From this output, we can see above that the
persistence-api dependency is coming from
hibernate. A cursory scan of the source in this module
will reveal many javax.persistence import statements
confirming that we are, indeed, directly referencing this dependency. The
simple fix is to add a direct reference to the dependency. In this
example, we put the dependency version in
simple-parent's dependencyManagement
section because the dependency is linked to Hibernate, and the Hibernate
version is declared here. Eventually you are going to want to upgrade your
project's version of Hibernate, listing the
persistence-api dependency version near the Hibernate
dependency version will make it more obvious later when your team modifies
the parent POM to upgrade the Hibernate version.
If you look at the dependency:analyze output from
the simple-web module, you will see that we also need
to add a direct reference to the simple-model
dependency. The code in simple-webapp directly
references the model objects in simple-model, and the
simple-model is exposed to
simple-webapp as a transitive dependency via
simple-persist. Since this is a sibling dependency
which shares both the version and
groupId, the dependency can be defined in
simple-webapp's pom.xml using the
${project.groupId} and
${project.version}.
How did the Maven Dependency plugin uncover these issues? How does
dependency:analyze know what classes and dependencies
are directly referenced by your project's bytecode? The Dependency plugin
uses the ObjectWeb ASM
toolkit to analyze the raw bytecode. The Dependency plugin uses ASM to
walk through all the classes in the current project and it builds a list
of every other class referenced. It then walks through all the
dependencies, direct and transitive, and marks off the classes discovered
in the direct dependencies. Any classes not located in the direct
dependencies are discovered in the transitive dependencies, and the list
of "used, undeclared dependencies" is produced.
In contrast, the list of Unused, Declared dependencies is a little trickier to validate, and less useful than the "Used, Undeclared Dependencies". For one, some dependencies are used only at runtime or for tests and they won't be found in the bytecode. These are pretty obvious when you see them in the output; for example, JUnit appears in this list, but this is expected as it is only used for unit tests. You'll also notice that the Velocity and Servlet API dependencies are listed in this list for the simple-web module. This is also expected because, while the project doesn't have any direct references to the classes of these artifacts, they are still essential during runtime.
Be careful when removing any Unused, Declared Dependencies unless you have very good test coverage, or you might introduce a runtime error. A more sinister issue pops up with bytecode optimization. For example, it is legal for a compiler to substitute the value of a constant and optimize away the reference. Removing this dependency will cause the compile to fail, yet the tool shows it as unused. Future versions of the Maven Dependency Plugin will provide better techniques for detecting and/or ignoring these types of issues.
You should use the dependency:analyze tool
periodically to detect these common errors in your projects. It can be
configured to fail the build if certain conditions are found, and it is
also available as a report.
As a final overview, the final POM files are
listed as a reference for this chapter. Here is the top-level
POM for simple-parent:
Example 8.1. Final POM for simple-parent
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.sonatype.mavenbook.ch08</groupId> <artifactId>simple-parent</artifactId> <packaging>pom</packaging> <version>1.0</version> <name>Chapter 8 Simple Parent Project</name> <modules> <module>simple-command</module> <module>simple-model</module> <module>simple-weather</module> <module>simple-persist</module> <module>simple-webapp</module> </modules> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>hibernate3-maven-plugin</artifactId> <version>2.1</version> <configuration> <components> <component> <name>hbm2ddl</name> <implementation>annotationconfiguration</implementation> </component> </components> </configuration> <dependencies> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb.version}</version> </dependency> </dependencies> </plugin> </plugins> </pluginManagement> </build> <properties> <hibernate.annotations.version>3.3.0.ga</hibernate.annotations.version> <hsqldb.version>1.8.0.7</hsqldb.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> <version>2.0.7</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.5</version> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>${hibernate.annotations.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-commons-annotations</artifactId> <version>${hibernate.annotations.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>3.2.5.ga</version> <exclusions> <exclusion> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> </exclusion> </exclusions> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
The following POM captures the
POM for simple-command, the
command-line version of the tool.
Example 8.2. Final POM for simple-command
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.sonatype.mavenbook.ch08</groupId> <artifactId>simple-parent</artifactId> <version>1.0</version> </parent> <artifactId>simple-command</artifactId> <packaging>jar</packaging> <name>Chapter 8 Simple Command Line Tool</name> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>org.sonatype.mavenbook.weather.Main</mainClass> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <testFailureIgnore>true</testFailureIgnore> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins> </pluginManagement> </build> <dependencies> <dependency> <groupId>${project.groupId}</groupId> <artifactId>simple-weather</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>simple-persist</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> </dependency> </dependencies> </project>
This next POM is the
simple-model project's POM. The
simple-model project contains all of the model objects
used throughout the application.
Example 8.3. Final POM for simple-model
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.sonatype.mavenbook.ch08</groupId> <artifactId>simple-parent</artifactId> <version>1.0</version> </parent> <artifactId>simple-model</artifactId> <packaging>jar</packaging> <name>Chapter 8 Simple Object Model</name> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> </dependency> </dependencies> </project>
This next POM is the
simple-persist project's POM. The
simple-persist project contains all of the persistence
logic which is implemented using Hibernate.
Example 8.4. Final POM for simple-persist
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.mavenbook.ch08</groupId>
<artifactId>simple-parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>simple-persist</artifactId>
<packaging>jar</packaging>
<name>Chapter 8 Simple Persistence API</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>simple-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jta_1.1_spec</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</dependency>
</dependencies>
</project>
The simple-weather project is the project which
contains all of the logic to parse the Yahoo! Weather
RSS feed. This project depends on the
simple-model project.
Example 8.5. Final POM for simple-weather
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.sonatype.mavenbook.ch08</groupId> <artifactId>simple-parent</artifactId> <version>1.0</version> </parent> <artifactId>simple-weather</artifactId> <packaging>jar</packaging> <name>Chapter 8 Simple Weather API</name> <dependencies> <dependency> <groupId>${project.groupId}</groupId> <artifactId>simple-model</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.14</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> <scope>test</scope> </dependency> </dependencies> </project>
Finally, the simple-webapp project contains a web
application which stores retrieved weather forecasts in an
HSQLDB database and which also interacts with the
libraries generated by the simple-weather
project.
Example 8.6. Final POM for simple-webapp
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.sonatype.mavenbook.ch08</groupId> <artifactId>simple-parent</artifactId> <version>1.0</version> </parent> <artifactId>simple-webapp</artifactId> <packaging>war</packaging> <name>Chapter 8 Simple Web Application</name> <dependencies> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-servlet_2.4_spec</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>simple-model</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>simple-weather</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>simple-persist</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> </dependency> </dependencies> <build> <finalName>simple-webapp</finalName> <plugins> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <version>6.1.9</version> <dependencies> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
This chapter has shown you several techniques to improve the control of your dependencies and plugins to ease future maintenance of your builds. We recommend periodically reviewing your builds in this way to ensure that duplication and thus potential trouble spots are minimized. As a project matures, new dependencies are inevitably introduced and you may find that a dependency previously used in one place is now used in 10 and should be moved up. The used and unused dependencies list changes over time and can easily be cleaned up with the Maven Dependency Plugin

