Useful Maven Plugins: Part 1

Exclusive offer: get 50% off this eBook here
Apache Maven 2 Effective Implementation

Apache Maven 2 Effective Implementation — Save 50%

Build and Manage Applications with Maven, Continuum, and Archiva

£14.99    £7.50
by Brett Porter Maria Odea Ching | September 2009 | Java Open Source

In this article by Brett Porter and Maria Odea Ching, we will take a closer look at some of the plugins from two locations: those hosted as part of the Apache Maven project (http://maven.apache.org/plugins/), and a number of plugins from the Codehaus Mojo project (http://mojo.codehaus.org/plugins.html), which is oriented directly towards Maven plugin development.

Where possible, we will apply the plugins to our example application to see how they can be used in practice, and then cover some of the other use cases and best practices for their use.

Nobody can tell exactly how many Maven plugins exist today—since, like dependencies they can be retrieved from any specified remote repository, there are likely hundreds to choose from, and likely even more that have been custom written for use within the infrastructure of particular organizations.

A common practice for frameworks and tools that require build integration is to publish a Maven plugin to accomplish the task—and it is becoming increasingly common to encounter this as a standard part of the getting started section of a project you might hope to use. However, there are also a number of plugins that would be considered general purpose and handle some extended build cases in a wider variety of projects.

While this won't come close to covering all the plugins you are likely to encounter, with these common tools in your arsenal it will cover many of your Maven build needs, reducing the need for you to write your own plugins.

The Remote Resources plugin

Most projects will use the Resources plugin at some point, even if it isn't configured directly—it is standard in the default life cycle for any packaging that produces some type of artifact, bundling the resources found in src/main/resources.

However, what if you wanted to share those resources among multiple projects? The best approach to doing that is to store the resources in the repository and retrieve them for use in multiple builds—and that is where the Remote Resources plugin comes in.

First, we should note that this is not the only alternative for handling the scenario. The Dependency plugin's unpack goal is also quite capable of unpacking an artifact full of resources directly into the location that will be packaged.

However, the Remote Resources plugin offers several advantages:

  1. Re-integration with the resources life cycle so that retrieved resources will automatically be processed in any goals in the process-resources phase.
  2. The ability to perform additional processing on the resources (including the optional use of Velocity templates to generate the resources) before inclusion.
  3. A specific bundle generation goal for creating the resource artifact in the first place.

These advantages can make the plugin very effective at dealing with some common scenarios. For example the inspiration for the creation of the plugin, and one of its more common uses, is to place aggregated license files within the final artifact.

There are other scenarios where the dependency:unpack goal remains more suitable. It is best to select the Remote Resources plugin when the files will be incorporated into the resources life cycle and the Dependency plugin when the files will be utilized independently.

Let's look at how to create a license file for our Centrepoint application. We will do this in two steps—the creation of the resource bundle that provides the generic resources for any project by the same organization, and the processing of the module resources.

Creating a Remote Resource bundle

Remote Resource bundles are regular JAR files packaged with additional information generated by the remote resource plugin's bundle goal. Creating a module follows the same process as with other JAR files.

In the example application, we will create the module outside of the Centrepoint multi-module hierarchy, so that it could (theoretically) be used by other projects from the same organization. This could be anywhere in source control, but we will assume it sits side-by-side with the effectivemaven-parent module in the workspace.

$ mvn archetype:generate -DartifactId=license-resources 
-DgroupId=com.effectivemaven

As this is not going to be a code project, the src/main/java and src/test directories can be removed from the generated content. We then continue to add the parent project to the POM, so the result looks like the following:

<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>com.effectivemaven</groupId>
<artifactId>effectivemaven-parent</artifactId>
<version>1-SNAPSHOT</version>
<relativePath>../effectivemaven-parent/pom.xml</relativePath>
</parent>
<artifactId>license-resources</artifactId>
<version>1.0-SNAPSHOT</version>
<name>License Resource Bundle</name>
</project>

We will add the Remote Resources plugin shortly, but first let's create the resources that will be bundled. These are added to the src/main/resources like regular resources.

Consider the following Velocity template file, src/main/resources/LICENSE.vm:

## License Generator
#macro(showUrl $url)
#if($url)
($url)
#end
#end
This software is distributed under the following license(s):
#foreach ($l in $project.licenses)
- $l.name #showUrl ($l.url)
#end
#if (!$projectsSortedByOrganization.isEmpty())
The software relies on a number of dependencies. The individual
licenses are outlined below.
#set ($keys = $projectsSortedByOrganization.keySet())
#foreach ($o in $keys)
From: '$o.name' #showUrl($o.url)
#set ($projects = $projectsSortedByOrganization.get($o))
#foreach ($p in $projects)
- $p.name #showUrl ($p.url)
$p.artifact
#foreach ($l in $p.licenses)
License: $l.name #showUrl ($l.url)
#end
#end
#end
#end

For those not familiar with Velocity, the purpose of this is to first iterate through the project's licenses and list them, then secondly iterate through the project's dependencies (grouped by the organization they are from) and list their license. The $projectsSortedByOrganization variable is a special one added by the Remote Resources plugin to assist in this task.

Before we can move on to use the bundle, we need to add the plugin to the bundle project like so:

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-remote-resources-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>bundle</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

This goal is required to generate a bundle manifest, the contents of which tell the plugin which resources to process when it is later called on to do so.

With this all in place, we can now install the bundle into the local repository, ready for use:


license-resources$ mvn install

If you were to inspect the contents of the generated JAR file, you would see both the LICENSE.vm file in the root, and the bundle manifest in META-INF/maven/remoteresources.xml. You would also find that the Velocity template is unmodified—the contents will be executed when the bundle is later processed in the target project, which we will proceed to look at now.

Processing Remote Resources in a project

Using the resource bundle we have created is now quite straightforward. We start by adding the folllowing to the build section of modules/pom.xml file of Centrepoint:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-remote-resources-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
<configuration>
<resourceBundles>
<resourceBundle>
com.effectivemaven:license-resources:1.0-SNAPSHOT
</resourceBundle>
</resourceBundles>
</configuration>
</plugin>


Here we have added a list of resource bundle artifacts to the configuration for the process goal, in the familiar shorthand artifact notation of groupId:artifactId:version. It has been added to the modules POM so that the license is included in the JAR files, but not included in the other non-code modules such as the documentation (which already generates a copy of the license from the reporting plugins).

Normally, you should use a released version of the license bundle, not a snapshot as we have here (as we have not yet covered the release process!). Since the bundle is configured directly and not through a dependency, the Release plugin will not detect this unresolved snapshot later.

Now, if we build a module such as store-api, we will see the license included in the root directory of the JAR file with the following content:

This software is distributed under the following license(s):
- The Apache Software License, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.txt)
The software relies on a number of dependencies. The individual
licenses are outlined below.
From: 'Apache Maven 2: Effective Implementations Book'
(http://www.effectivemaven.com/)
- Centrepoint Data Model
com.effectivemaven.centrepoint:model:jar:1.0-SNAPSHOT
License: The Apache Software License, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.txt)
From: 'Google'
(http://www.google.com/)
- Guice
(http://code.google.com/p/google-guice/)
com.google.code.guice:guice:pom:1.0
License: The Apache Software License, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.txt)

This is a good start, but we don't really need to include our own artifacts in the list, so we go back to the plugin declaration in modules/pom.xml and add another line of configuration:

<configuration>
<excludeGroupIds>${project.groupId}</excludeGroupIds>
<resourceBundles>
...

Regenerating the above artifact will alter the license to remove the dependencies from the project's group.

A different case is the final distribution. As this is not part of the modules hierarchy, first we need to include the plugin definition identical to the one added previously.

In the sample code for this article, you will notice that this has been taken a step further with the version and common configuration pushed into a pluginManagement section of the Centrepoint parent POM, and just the execution of the plugin goal remains in the modules and distribution POM files.

We can now build the assembly as usual:

distribution$ mvn clean install

Upon inspecting the generated assemblies, you will not see the license file included yet. This is because the Assembly plugin does not pick up Maven resources by default, as it does not participate in the normal life cycle.

To include the license file, we must alter the assembly descriptor distribution/src/main/assembly/bin.xml and add the following file set:

<fileSet>
<directory>target/maven-shared-archive-resources</directory>
<outputDirectory>/</outputDirectory>
</fileSet>

The directory given is the standard location in which the Remote Resources plugin stores the resources it has processed, so if you decide to configure that differently in your own projects you would need to change this to the corresponding location.

Upon building the assembly again we will see that the license has been generated, and that it includes the licenses of dependencies outside of the Centepoint application. As you can see, the distributed application depends on Jetty (also under the Apache License 2.0), which includes some portions of Glassfish (under the CDDL 1.0 License).

While the above technique can be very helpful in constructing some useful information about your project and its dependencies, it cannot be guaranteed to produce complete licensing information for a project. The method relies on accurate information in the POMs of your dependencies, and this can sometimes be inaccurate (particularly when using public repositories such as the Maven Central Repository). If you are redistributing your files, always confirm that you have correctly recorded any necessary licensing information that must accompany them!

The Remote Resources plugin is also capable of covering other scenarios that are particularly suited to license handling or more generally recording information about the project it is being processed for. These include:

  1. The supplementalDataModels configuration option that allows you to fill in incomplete or incorrect metadata for a project dependency before the resources are processed (to avoid particular problems as described above).
  2. The appendedResourcesDirectory, which allows you to store the above models in a separate file.
  3. The properties configuration, which allows the injection of other build properties into the Velocity templates.

However, with this in mind, remember that the Remote Resources plugin is often just as suitable for any type of reusable resource, even if it is a static file.

The Build Number plugin

In Maven Mojos, the goals within a plugin are always designed to be simple tasks. Their aim is to do one thing, and do it well. A good example of this is the Build Number plugin. This simple plugin has one goal (create), with one purpose—to obtain a suitable build number and expose it to the build through properties or a file.

While the plugin focuses on exposing the current Subversion revision, it is capable of generating an incremented build number (stored in a specified properties file), and a representation of the current system date and time. This feature can be very useful in identifying the exact heritage of a particular build. The build number generated by the plugin is different to that used by Maven to identify snapshots or artifact versions. While it is possible that you might mark your version using the information it generates, this plugin is typically used to record information about a particular build—whether it is a snapshot, or a release—within the artifact itself as a permanent record.

Using the plugin is straightforward. By adding the goal to the project, the Subversion revision and a timestamp property will be exposed from the point that it is run onwards.

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.0-beta-1</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
</plugin>

In this example, we execute the plugin in the generate-resources phase so that the properties are available to any resource processing. Note that the values could be used with the Remote Resources plugin that we have just seen.

There are two things to take into consideration with this configuration, however. Firstly, not all source builds will be Subversion checkouts, but the plugin does not verify that. To work around this potential problem, you can put the goal into a profile:

<profile>
<id>buildnumber</id>
<activation>
<file>
<exists>.svn</exists>
</file>
</activation>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
...

This particular activation check will cause the profile to be used within a Subversion checkout (that is, if .svn exists in the current directory), and to skip the plugin if not. In this case, the properties will not be set (or the file will not be created), so the code using these must take that into account.

Secondly, how the values will be accessed needs to be given careful consideration. For example, it is unlikely that you want to create a convoluted build processing step to filter the value into a particular JSP file to appear in a web application. For the sake of keeping the build simple (and speedy), it is best to write the values into a single file that the application can then load from its classpath.

This can be achieved by creating a filtered resource that contains references to the values. The advantage of this method is that it is automatic if you have already configured filtered resources, and automatically ends up in the classpath of the application code that can load the file as a resource.

Apache Maven 2 Effective Implementation Build and Manage Applications with Maven, Continuum, and Archiva
Published: September 2009
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

The plugin also supports a number of formatting options, in the event that you do not wish to use the raw build number and timestamp integers. These options are also used to trigger the manual build number increment feature of the plugin in the event that Subversion is not used.

As our example application is one such case, let's apply this technique ourselves. We will only need to include the build number plugin in one location—if it were to be run on every module, the number would end up being different for each. Some seemingly sensible options may not actually be appropriate here:

  • The parent project will cause the build number to be included in every module, and executing it just in the parent may result in it not being executed at all.
  • The final distribution might be the best place in some situations, but what if you needed to reference the build number from the application itself?

Considering these factors, the best location is the web application module, as this will be the code which will eventually display the build number. In another application where multiple modules were to require it, it might be best generated in a common dependency instead.

The modules/webapp/pom.xml file requires two modifications. First, to enable filtering, we must add both the new filtered resources location and the original resources location to the build section (as Maven inheritance overrides resources instead of adding to them). This will look like the following:

<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/filtered-resources</directory>
<filtering>true</filtering>
</resource>
</resources>

Depending on what resources you have in src/main/resources, you may choose to do away with it and include them in the filtered-resources directory instead. However, if some resources may be corrupted by filtering (such as images and other binary files), you will need to retain both.

Next, the addition of the build number plugin is needed with some particular configuration:

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.0-beta-1</version>
<configuration>
<format>Build: #{0} ({1,date})</format>
<items>
<item>buildNumberd*</item>
<item>timestamp</item>
</items>
</configuration>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
</plugin>

Note the formatting options being used. The first, format, specifies what the configured property (by default called buildNumber) will look like, using the Java MessageFormat syntax. The items configuration provides a list of variables to substitute into the message format for {0}, {1}, and so on. Here, the first configured variable is buildNumberd*, which is used to trigger the automatic number generation. The second is the integer timestamp that represents the current system date and time.

You may also configure the location of the file to be generated that contains the incremented build number, with the default being buildNumber.properties in the current directory. It is important to place this in a location that will not be purged over time—for example, in the target directory, as this is regularly cleaned!

The exposed property is referenced from the resource files using the normal filtering syntax, so we can create src/main/filtered-resources/build.properties like so:

build.message=${buildNumber}

When we build the application, we will see the build number generated and incremented each time:

[INFO] Storing buildNumber: Build: #1 (27/03/2009) 
at timestamp: 1238074038389

All that is left to do is to access the build number from the application, which you can see in the BuildNumber class:

ResourceBundle bundle = ResourceBundle.getBundle( "build" );
msg = bundle.getString( "build.message" );

As you can see, if you are using Subversion it is likely a simpler option to use the revision number instead of creating a new build number, or perhaps passing the build number in from your build server using its own build numbering scheme. Using an incremented number as we have above can be inconsistent depending on the build order and build successes, with the additional requirement of maintaining the separate tracking file.

The Shade plugin

Maven's dependency-based nature promotes the practice of proper componentization of a build and producing a set of discrete artifacts that can be aggregated into an application. Throughout a dependency tree, dependencies may appear multiple times and can be mediated to the correct version so that a single version can be used. However, circumstances will occur where, rather than the discrete list of dependencies, it is necessary to merge, hide, or alter the parts of a number of dependencies into a single artifact. This is the purpose for which the Shade plugin was developed, and in this section we will look at two use cases in more detail.

Building a standalone artifact

Distributing applications if they have dependencies on other JAR files has always been problematic. Java has a mechanism to run a JAR on its own through the Main-Class manifest attribute, providing us with a simple command such as the following:

client$ java -jar centrepoint-client-1.0-SNAPSHOT.jar

However, this will generally fail as the dependencies required are not present on the classpath. From here, you might add the Class-Path element to the manifest. However, this requires that the JARs be placed in a particular location relative to the other files, and we are back to distributing a complete archive.

To make this easier, the Assembly plugin contains a pre-built descriptor called jar-with-dependencies. This simply collapses the JAR and all it's dependencies into one large JAR so that the above type of execution can work. In some cases, this configuration will work correctly, and no more work needs to be done.

One of the issues with the naïve approach of the assembly plugin is that many of the dependencies will house fi les with identical names, and it is given little choice but to pick just the last fi le that it encounters rather than dealing with the overlap properly.

This is where the Shade plugin can be useful, by providing hooks to transform these resources into a single resource as appropriate.

This is similar in intent to the Uberjar plugin that you may have used in Maven 1, however the technique is improved. In the assembly plugin and the shade plugin, the resulting artifact collapses the dependency JARs and corrects references instead of having to unpack and construct classloaders, making it a much faster alternative.

For example, consider the following confi guration:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.
resource.ComponentsXmlResourceTransformer" />
</transformers>
<artifactSet>
<excludes>
<exclude>xerces:xercesImpl</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>

Here we have the shade goal bound to the package phase to produce the collapsed JAR file as we might expect. In the transformers section, a transformer is added that merges all encountered Plexus descriptor files under META-INF/plexus/components.xml. Finally, we have the ability to configure the transitive dependencies that are included and excluded from the final artifact using the artifactSet configuration.

As of Shade version 1.2, the following transformers are supplied with the plugin:

  • ApacheLicenseResourceTransformer and ApacheNoticeResourceTransformer: Specific to Apache license files such as those we created with the remote resources plugin earlier
  • AppendingTransformer: To concatenate plain text resources
  • ComponentsXmlResourceTransformer: For merging Plexus descriptors
  • ManifestResourceTransformer: For merging Java JAR file manifests
  • ServicesResourceTransformer: For merging Java services metadata in META-INF/services
  • XmlAppendingTransformer: To concatenate XML resource files with appropriate nesting

These transformers will cover many of your needs when using the Shade plugin. You may eventually need your own custom transformer if there are certain types of resources that must be merged between two artifacts that are not accommodated by the above. In that case, a transformer can be written in a separate artifact and added as a plugin dependency, then referenced directly from the configuration. This is similar to the technique illustrated for sharing other build resources such as the reporting configuration files.

Shading dependencies

Another interesting use case of the Shade plugin is to hide or alter the dependencies of an artifact. This can be helpful in several situations, such as:

  • Distributing classes from JARs that are difficult to access as dependencies (as long as such redistribution is allowed). For example, you may need to use a patched version of a common library and not want to cause conflict for projects that might depend on both your library and the original version.
  • Avoiding conflicts with other instances of the dependency within a classloader or to work around incompatible versions in a dependency tree.
  • Reducing the amount of code shipped by selectively including code from a particular dependency.

These situations are all similar and boil down to including some or all of the classes from a set of dependencies into the current artifact and altering the transitive dependency tree to compensate.

Let's consider this example within the context of the example application. In the example code for this article, we have extended the model to use commons-lang, which is used to construct the equals and hashCode methods. Now, if we were to share the API and model with third party developers to create their own store implementations, it would raise the same issues:

  • Requiring the dependency of commons-lang (about 260K in size) for just a couple of classes.
  • Introducing a dependency with a potentially stricter requirement than the rest of the application may later require (for example, if an incompatible, Java 5 enabled, commons-lang 3.0 is made available), again for very few classes.

In this situation, shading in just the portions of commons-lang that are needed may be a viable alternative. It is important to note that we can only do this if the license of the dependency allows such a combination, which in this case is true.

An important part of this scenario is that the dependency is being shared outside of the current application. Inside an application, where you have full control of the dependency tree, you are less likely to benefit from shading dependencies for this purpose.

To start with, we will add the Shade plugin configuration to the model/pom.xml file:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>commons-lang:commons-lang</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>

Running the package command with this in place will show the following:

[INFO] [jar:jar]
[INFO] Building jar: /Users/brett/code/06/centrepoint/modules/model/
target/model-1.0-SNAPSHOT.jar
[INFO] [shade:shade {execution: default}]
[INFO] Including commons-lang:commons-lang:jar:2.3 in the shaded jar.
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing /Users/brett/code/06/centrepoint/modules/model/target/
model-1.0-SNAPSHOT.jar with /Users/brett/code/06/centrepoint/modules/
model/target/model-1.0-SNAPSHOT-shaded.jar

Multiple things are happening here. First, we can see that the normal JAR is produced as usual, and then the shade goal runs (consistent with the execution we added to the project). The plugin next shows that because of the artifactSet that we gave, commons-lang will be included in the JAR. If there were other dependencies, they would remain a regular dependency and not be included. Finally, we see that using the Shade plugin's default configuration, the original artifact is replaced with the shaded artifact so that this version will be installed or used in the reactor instead.

However, we still have some work to do. Notice the size and contents of the JAR for the model now—so far, we have added all of commons-lang. We would only like to include the builder classes, so for that purpose we add artifact filters to the Shadeplugin configuration:

<filters>
<filter>
<artifact>commons-lang:commons-lang</artifact>
<includes>
<include>org/apache/commons/lang/builder/**</include>
</includes>
</filter>
</filters>

If we rebuild the project, we see that the JAR has reduced in size and upon inspection would find that only the files under the given path are included. This also ensures other files that might cause confusion (such as the pom.properties file under META-INF from commons-lang) are not included.

Of course, some caution is required here—these classes may well have required some of the now-excluded classes that are no longer present. In your own projects, ensure that your integration test cases adequately exercise the code being used; so that inadvertent runtime errors don't occur later.

Beware the catch here that unit tests won't exercise this change! Unit tests run before the shading process occurs, so the tests need to occur in the integration test phase or in a separate testing module.

The next thing that we might notice about the contents of the JAR file is that the classes are still in their original packages under org/apache/commons/lang. This poses a potential problem for other projects that use commons-lang and depend on this API. There will now be two copies of these classes on the classloader that contains both JARs, and depending on the order, either copy of the classes could be used. If the wrong version is silently picked up, confusion will ensue on the part of the developer that appears to be getting the right version of the dependency, but the wrong behavior.

To prevent this, the Shade plugin allows us to encapsulate the use of commons-lang entirely by using the relocations feature.

<relocations>
<relocation>
<pattern>org.apache.commons.lang</pattern>
</relocation>
</relocations>

Now when we build the artifact, we will see that the class names have changed, for example:

hidden/org/apache/commons/lang/builder/EqualsBuilder.class

This is more than a simple renaming—the Shade plugin has also adjusted the bytecode of these classes and the references in our own Java classes to rename the package to include hidden at the start. This will now avoid any conflict with other instances of commons-lang!

Though this conflict is removed, you may still need to consider the converse case, if a class depends on only being instantiated once to operate correctly, or is accessed by a string using Class.forName for example, shading may not work.

One slight adjustment should be made here, as the default pattern of prepending hidden is not always a good choice. This can be confusing to see in stack traces, and if two different dependencies shade the same classes in the same way then the likelihood of conflict occurs again. Instead, we can choose to include them within the namespace of our own package instead by a slight adjustment to the relocation configuration:

<relocation>
<pattern>org.apache.commons.lang</pattern>
<shadedPattern>
com.effectivemaven.centrepoint.model.shaded.lang
</shadedPattern>
</relocation>

A final thing is worth noting about the configuration we have used so far. Initially, the shaded artifact replaces the original artifact and is installed in the local repository. In addition to replacing the original artifact, you may notice that the POM file installed in the local repository is also replaced with a different version that removes the commons-lang dependency. This is what we would expect as we have completely incorporated our commons-lang needs within the classes of the JAR through the shading process. This is thanks to the createDependencyReducedPom configuration option being enabled by default.

In some scenarios, you may not wish to replace the artifact, but instead create an additional artifact, with a different classifier, that contains the shaded alternative. This can be a good middle ground to give users the choice between an all-in-one artifact, or the default artifact that allows Maven to manage the dependencies normally. In this case, the createDependencyReducedPom option should be set to false, and the shadedArtifactAttached option set to true. Bear in mind, however, that the dependency representation for the classified artifact in this instance will be incorrect as the main artifact POM is used for classified artifacts as well.

The Shade plugin configuration contains a number of other configuration options for altering inclusions and exclusions at each step of the process that we have seen here and in particular for altering the technique and naming of artifacts that are attached with a classifier. Refer to the Shade plugin documentation for more information: http://maven.apache.org/plugins/maven-shade-plugin/shade-mojo.html

Before we move on, you may have observed a potential side effect of this change; that there will now be two instances of each class in the application should another use commons-lang. If multiple dependencies shade in the same classes, there may be multiple copies so even though this is powerful it is worth being judicious about using the technique, particularly if you have good control over how your artifact will be used as a dependency. Maven's dependency mechanism and version management is there for a reason. If it is possible to continue using discreet artifacts as they are, then that may be the best and simplest alternative too!

>> Continue Reading Useful Maven Plugins: Part 2

[ 1 | 2 ]
Apache Maven 2 Effective Implementation Build and Manage Applications with Maven, Continuum, and Archiva
Published: September 2009
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

About the Author :


Brett Porter

Brett Porter is a software developer from Sydney, Australia with a passion for development tooling, and automation. Seeking a more standardized and reproducible solution to organize, build, and deploy a number of software projects across teams, he discovered an early beta of Maven 1.0 in 2003, and has been heavily involved in the development of the project since. He is a member of the Apache Maven Project Management Committee, and has conducted presentations and training on Maven and related tooling at several conferences and events. He founded the Archiva project in 2005. Brett is also a member of the Apache Software Foundation.

Brett is currently VP, Product Development at G2iX, in charge of the Maestro division. He and his team seek to make developers more efficient by offering support and services for development and automation tools including Apache Maven, Apache Continuum, Apache Archiva, and Selenium.

Brett was co-author of the book Better Builds with Maven, the first book to be written about the Maven 2.0 release in 2005, and has been involved in reviewing Maven: A Developer's Notebook and Java Power Tools.

Maria Odea Ching

Maria Ching grew up in a small town called Daet, then moved to Manila when she went to college. She took up Computer Studies at De La Salle University and graduated in 2005. She started using open source tools from her first job after graduating and from then on, got interested in everything open source. When she came to work for Exist, she got assigned in a project doing a lot of development work in open source projects, specifically Maven, Continuum, and Archiva. Back then, Continuum and Archiva (formerly named Maven Repository Manager) were still sub-projects of Maven. Eventually, she became a committer, then a PMC member of Maven. In 2008, Continuum and Archiva became top-level projects at the ASF and Deng was elected as PMC Chair of Archiva. She is still currently serving as PMC Chair of the project and as PMC members of Continuum and Maven.

Books From Packt

Drupal 6 Site Blueprints
Drupal 6 Site Blueprints

Solr 1.4 Enterprise Search Server
Solr 1.4 Enterprise Search Server

Papervision3D Essentials
Papervision3D Essentials

Pentaho Reporting 3.5 for Java Developers
Pentaho Reporting 3.5 for Java Developers

Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs
Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs

JasperReports 3.5 for Java Developers
JasperReports 3.5 for Java Developers

LWUIT 1.1 for Java ME Developers
LWUIT 1.1 for Java ME Developers

WebSphere Application Server 7.0 Administration Guide
WebSphere Application Server 7.0 Administration Guide

 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software