Showing posts with label maven. Show all posts
Showing posts with label maven. Show all posts

Tuesday, June 25, 2013

Resource Filtering with Gradle

My team has recently started a new Java web application project and we picked gradle as our build tool. Most of us were extremely familiar with maven, but decided to give gradle a try.
Today I had to figure out how to do resource filtering in gradle. And to be honest it wasn't as easy as I thought it should be; as least coming from a maven background. I eventually figured it out, but wanted to post my solution to make it easier for others.

What is Resource Filtering?
First, for those that may not know, what is resource filtering? It's basically a way to avoid hard coding values in files and make them more dynamic. For example, I may want to display the version of my application in my application. The version is usually defined in your build file and this value can be injected or replaced in your configuration file during assembly. So I could have a file called config.properties under src/main/resources with the following content:
application.version=${application.version}. With resource filtering the ${application.version} value gets replaced with 1.0.0 during assembly, then my application can load config.properties and display the application version.

It's an extremely valuable and powerful feature in build tools like maven and one that I took advantage of often.

Resource Filtering in Gradle
With this being my first gradle project, I needed to find the recommended way to enable resource filtering in gradle. My first problem I had to figure out was where to define the property. In maven this would typically be defined in the project's pom.xml file as a maven property:

<properties>
    <application.version>1.0.0</application>
</properties>

For gradle the appropriate place seemed to be the project's gradle.properties file. So you would add the following to your project's gradle.properties file (Note, I'm not suggesting you would hardcode the modules version in the gradle.properties file. Obviously the value would be derived from the version property in your project. I'm just using this for a simple example):

application.version=1.0.0

The next, and most difficult, problem I had to track down was how to actually enable resource filtering. I was hoping to just set some enableFiltering option and define the includes/excludes list, but that doesn't seem to be the case (extra tip: don't do filtering on binary files like images). I did find some resources online, but this one seemed to be the best approach. So you will need to add the following to your build.gradle file:

import org.apache.tools.ant.filters.*

processResources {
    filter ReplaceTokens, tokens: [
        "application.version": project.property("application.version")
    ]
}

Next you need to update your resource file. So put a config.properties file under src/main/resources and add this:

application.version=@application.version@

Note, the use of @ instead of ${}. This is because gradle is based on ant, and ant by default uses the @ character as the token identifier whereas maven uses ${}.

Finally, if you build your project you can look under build/resources/main and you should see a config.properties file with a value of 1.0.0. You can also open up your artifact and see the same result.

Dot notation
One thing to note is I typically use a period or dot to separate words for properties: application.version instead of applicationVersion. So you will notice the surrounding quotes around "application.version" in the build.gradle file. This is required as failing to surround the key by quotes will fail the build. Probably because groovy's dynamic nature thinks you are traversing an object.

Overriding
I also investigated the best approach to overriding properties in gradle, as this appeared to be slightly different then how it's done in maven. In maven, properties can be overridden by properties defined in the user's setting.xml file or on the command line with the -D option. To override application.version in gradle on the command line I had to run the following:

gradle assemble -Papplication.version=2.0.0

If you want to override it for all projects you can add the property in your gradle.properties file under /user_home/.gradle.

Also, if you are overriding the value via the command line and your property value contains special characters like a single quote, you can wrap the value with double quotes like the following to get it to work:

gradle assemble -Papplication.version="2.0.0'6589"

Summary
Well I hope this helps and if anyone from the gradle community sees a better way to perform resource filtering I'd love to hear about it. I'd also like to see something as important as resource filtering becoming easier to perform in gradle. I think it's crazy having to add an import statement to perform something so simple.

Sunday, July 25, 2010

Upgrading to Maven 3

I've been playing around with maven 3 lately on our legacy maven 2 multi-module project via mvnsh. Like advertised, maven 3 is backwards compatible with maven 2. In fact, most everything worked out of the box when switching to maven 3. In this post, I'm going to highlight the required and currently optional items I changed so you can start preparing to migrate your project to maven 3. But first, what's so special about maven 3 and why would you upgrade? Polyglot maven, mvnsh, and improved performance (50%-400% faster) are just a few. And since it's so easy to migrate to maven 3, you really don't have any excuses.

Currently, I build our project using maven 2.2.1. This article was tested with mvnsh 0.10 which includes maven 3.0-alpha-6. The current release of maven 3 is 3.0-beta-1, while maven 3.1 is due out Q1 of 2011.

Profiles.xml no longer supported
I haven't really figured out the reasoning, but it doesn't really matter; maven 3.0 no longer supports a profiles.xml. Instead you place your profiles in your ~/.m2/settings.xml. Some of our database processes and integration tests require properties from our profiles.xml. It was simple to solve by just moving my profiles to my settings.xml and everything worked.

Upgrade GMaven Plugin
We depend pretty heavily on the gmaven plugin for testing, simple groovy scripts, and some ant calls. In order to build some modules I had to upgrade gmaven. The current version we were using was 1.0-rc-3. Our projects built perfectly after changing it to org.codehaus.gmaven:gmaven-plugin:1.2.

${pom.version} changing to ${project.version}
Here maven 3 kindly warned me that uses of the maven property pom.version may no longer be supported in future versions and should be changed to project.version. My modules still built, but thought it was nice of maven to inform me of the potential change.

Version and Scope Issues
We had a few places where we needed to define a dependency version and another place where we shouldn't have defined a scope. Both instances prevented maven 3.0 from building our modules, but fixing them was easy. The first instance was we defined a version for a plugin in the pluginManagement section, but maven 3 required it also where it was used in the reporting plugin section. Not exactly sure about this one, ideally you would only define your plugin versions in the pluginManagement section but oh well.

We had some WAR projects using jetty. In the jetty plugin definition we had a dependency on geronimo and had defined a scope of provided. Maven 3 complained about it and since it's really not necessary, just removing it fixed the issue.

modelVersion
Maven 3.0 kept warning about using ${modelVersion} instead of ${project.modelVersion}. I was still able to build though, so my guess is the value for modelVersion, 4.0.0, most likely will change when maven 3.1 comes out.

Weird Surefire Output
This wasn't necessarily an issue with the surefire plugin, but I wanted to comment about it's output when tests failed as I thought it might have been a maven 3 issue. Below is a screenshot of the output when you have failed tests. At first I thought it was a maven 3 issue, but I built the same project using the same commands with maven 2.2.1 and got the same test failures. Hopefully, they can clean this type of thing up, because I could image lots of people getting confused.

Failed test output

That's essentially it. Happily, there really wasn't much required to change, which goes to show the great lengths the maven team has gone through to ensure backwards compatibility. Finally, here is a Compatibility Notes maven has provided on the subject of migrating maven 2 projects to maven 3.

Tuesday, July 13, 2010

Avatar Maven

Today I gave a quick presentation to some coworkers about maven. It's a broad topic, so I kept it fairly limited. Most of my audience was very familiar with maven, so I tried not boring them with stuff they already knew. I tried making it a little engaging by comparing the Avatar, master of all 4 elements, to Maven, master of the build (it's a stretch I know). It's a quick presentation (15 slides) providing some helpful maven tips, what's coming in maven 3, and mvnsh. Hope you like it.

Friday, May 14, 2010

How to create a release without the maven2 release plugin

One of the most referenced articles I have written is "How to create a release using the maven release plugin". But what if you can't get the maven release plugin to work with your project? Perhaps like our team, you've got a legacy maven2 multi-module project that's been nigh impossible to use with the release plugin. Our project has a mix of WAR modules combined with some Flex modules. I believe our last issue was some googlecode flex mojo wasn't working with the release plugin. Consequently, for the past year or so, we've been manually creating our releases. This actually hasn't been that much of a pain since we really only do it once a sprint at the end. Combined with my favorite perl script it doesn't really take that long. However, it does have the disadvantage of requiring some knowledge of what and now to do it. Ideally, it would be a job in Hudson, anyone on the team could run as many times as they like.

In an effort to try and automate as much as possible, I decided to try and automate releasing our legacy multi-module project using bash. This has several benefits: create a release faster, done consistently each time, turn-key solution anyone on the team can run that doesn't require stale documentation on how to do it.

It took my several hours to essentially duplicate the maven release plugin process. Thanks to our new intern Scott Rogers and linux master Ron Alleva, I was eventually able to get it finished. It's my first "official" bash script so pardon the mess. If you've never attempted to automate your release project, first consider reading my article on How to effectively use SNAPSHOT.

Here is the script available as a gist on github: project-release.sh. Here is what it does:

  1. Copies the current working branch (i.e. trunk) into another branch. It uses the pom.xml value to get the current working branch.
  2. Updates all the pom.xml version sections of the current working branch
  3. Commits the pom.xml changes
  4. Checks out the release branch
  5. Updates all the pom.xml version sections of the release branch (basically stripping off -SNAPSHOT)
  6. Commits the pom.xml changes
To run this script all you have to do is run: project-release.sh 2 false. The first parameter (2) is the increment position that the current working branch needs to be next. For example, if trunk was on 1.2.0-SNAPSHOT and the position passed in was 2, then trunk gets updated to 1.3.0-SNAPSHOT. If the position was 3 then trunk would be updated to 1.2.1-SNAPSHOT. The second parameter is used when testing. It's like the dryRun option in the maven release plugin. When set to true, nothing gets copied or committed.

A few notes about the script:
  • The base branch URL is hardcoded but could easily be passed in as another parameter or placed and read from some external file.
  • It uses the cmd xpath to extract out the pom version, project name, and scm url. I'm on ubuntu 9.10 and according to synaptic I have libxml-xpath-perl version 1.13-6 installed.
  • It doesn't run any maven commands like mvn deploy. Other jobs in CI can accomplish that or you can easily add them into the script.
  • To run from Hudson:
    • Create a New Job
    • In the Build section Add a Execute Shell Step
    • Update the Command text with: $WORKSPACE/trunk/project-release.sh 2 false
Overall, I'm pretty happy with the outcome. And as we start to perform more releases among multiple projects I think it's going to really come in handy. I think ideally you should try and release your project using the maven release plugin, but if that isn't possible then don't give up. Just clone.

Wednesday, March 17, 2010

How to Speed up Maven

How many times do you invoke maven in a day? How much time could you save if you shaved 15 seconds off each execution? That doesn't really sound like a lot but when executed a 100 times a day it adds up quickly; and 15 seconds is probably conservative. Now stop being selfish and think collectively as team how often maven is executed. 15 seconds for 50-100 builds a day across a team of 10-15 programmers adds up even quicker. This was my experience experimenting with the maven-cli-plugin with a simple sub-module running mvn clean install with unit tests.

I had never actually heard of maven providing this ability. I had seen and used it for other technologies like grails and scala, but I never considered if maven did as well. It wasn't until James Strachen recently tweeted about attempting to use a new feature provided by Sonatype called mvnsh: "a CLI Interface that enables you to speed up your builds because project information and Maven plugins are loaded into a single, always-ready JVM instance". From the FAQ, "The Maven engine is only started up (bootstrapped) one time and then held in a "ready" state waiting for user input of Maven or other shell command". Since I spend most of my day basically running maven, I was very intrigued.

Unfortunately, it appears mvnsh only supports maven 3 projects and our projects are still using maven 2. Even though I've read maven 3 is backwards compatible, I'm still waiting for a more official stable release (maven 3 is currently at 3.0-alpha-7). On the bright side, it appears mvnsh was set to improve on the maven-cli-plugin that supports maven 2 projects.

So I just wanted to share my experience getting the maven-cli-plugin set up and get the word out to fellow maven users to improve their productivity. Overall it seems to work as expected and does improve my local build times. I'm on Ubuntu 9.10, maven 2.0.10, and JDK 1.5. As I mentioned in the beginning, using cli:execute-phase decreased my build times approximately 15 seconds. The biggest disadvantage so far is I don't have access to my alias's, which I depend on heavily for just about everything I do with maven.

Getting started with the maven-cli-plugin
I basically followed the Common Setup instructions on the github wiki User Guide.

I added the pluginGroup to my settings.xml file:

<pluginGroups>
    <pluginGroup>org.twdata.maven</pluginGroup>
</pluginGroups>
I then added a new profile to my settings.xml file:
<profile>
    <id>cli</id>
    <pluginRepositories>
        <pluginRepository>
            <id>twdata-m2-repository</id>
            <name>twdata.org Maven 2 Repository</name>
            <url>http://twdata-m2-repository.googlecode.com/svn/</url>
        </pluginRepository>
    </pluginRepositories>
</profile>
And finally I enabled the new profile in settings.xml:
<activeProfiles>
    <activeProfile>cli</activeProfile>
</activeProfiles>
Using the maven-cli-plugin
The maven-cli-plugin basically supports 2 goals: execute and execute-phase. Use execute when you want to run goals and use execute-phase when you want to run phases. It's unfortunate the plugin forces you to pre-commit to one or the other; hopefully mvnsh has improved this. I typically run clean install which are phases so I ran: mvn cli:execute-phase. This puts you in an interactive shell where you can run clean install. Once finished you continue to stay in the shell allowing you to repeatedly execute mvn phases without having to bootstrap maven each time.

Summary
Since I spend a majority of my time executing maven and this simple experiment decreased my build times, I plan on using the cli plugin for every day use when I don't want to use my alias's. Also, this plugin should work on windows as well so don't think you are left out. Let me know how your mileage varies. Do you have any other tips that improve maven execution times?

On a side note, I'm sure the django/python crowd cringe at how much time java developers waste compiling and deploying. Execution times are something I believe they don't have to worry about.

Monday, March 1, 2010

Free Maven Repository Hosting for Open Source projects by Sonatype

I'm very excited to see Sonatype support maven repositories for Open Source projects that use maven. In all honesty, they didn't have to do this. Unfortunately, the java.net repo was harming the maven reputation. I've had direct experience using the java.net maven repo and can say it was an unpleasant experience. When we open sourced our 4 JBI (Java Business Integration) Components their home existed on java.net and we used the their maven repo. It was difficult to upload anything and it seemed to be constantly down.

Before this announcement, open source java projects using maven didn't really have an option as to where they could publish their artifacts. To my knowledge neither Google Code or Sourceforge offered this capability. Apache and Codehaus did and obviously you still have the Maven Central (http://repo1.maven.org/maven2/), but I never went through the process of what it took to use them. Now it doesn't matter where your project is hosted. Hopefully the next thing to come is free Continuous Integration services in the cloud using Hudson. I think this is the next step for project hosting sites like Google Code and github.

Providing free maven repos I think has a lot of benefits and not just for maven users. For example, this should benefit all dependency management tools that are built on top of maven repos. I'm not 100% sure tools like Ivy, Grape, Gradle, and Buildr use maven repo's, but my guess is they do and this will benefit those users. Another benefit is being able to standardize on maven repositories, hopefully preventing users from searching where they can find your artifacts. I've wasted a lot of time in the past trying to find valid repositories where I could find artifacts for a project I was wanting to use.

I'm also very impressed with the features Sonatype is providing. Not only will they support release artifacts but SNAPSHOT's as well which could consume a lot of space. You'll also be able to easily sync with Central. Finally, they will support a staging repo in order to test things out before officially releasing.

So go sign up and thanks Sonatype. Also, read this post to learn how to release your project using the maven release plugin.

Tuesday, February 16, 2010

Rapid REST Development with maven jetty plugin

Ever wonder how much time is wasted by Java developers rebuilding and redeploying web applications? For me alone I can't imagine it. In fact, I'd be embarrassed if Google Buzz made it public knowledge without my consent. Two years ago I wrote an article "No more J2EE Apps" and I received a lot of great feedback. Let me first say that I think, IMHO, Java Developers are at a disadvantage when it comes to rapidly developing web applications with a static language. Developers using python, php, rails, or grails really don't even have to spend a second trying to solve this problem. On the other hand, Java Developers have to figure out how to best accomplish this, and every situation seems to be different: JBoss, Weblogic, Eclipse, Idea, Netbeans, Jetty, JRebel. Of all the solutions I think JRebel provides the best chance for success that solves any environment no matter the web container or developers IDE of choice.

I haven't really done much hardcore java development in awhile, but in order to improve my team's productivity, I am going to be exploring best practices the next couple of months concerning this area. First up, I am going to explain how I got the maven jetty plugin to work with our REST Services WAR and the steps necessary to redeploy changes. The nice thing about jetty is it's easy to use from maven and we could use it in our CI environment to possibly reduce our build times and provide quicker feedback. The downside is each change requires jetty to hotdeploy the new WAR. End the end I think the best solution will be a combination of JBoss+JRebel. But I won't get to for awhile.

Maven Jetty Plugin
My first prototype uses the maven-jetty-plugin version 6. The application we are testing is a WAR containing REST Services built with Jersey (JAX-RS). Here is a good posting by my co-worker Jeff Black "Jersey...Jetty and Maven style!". This example didn't work for me because for some insane reason the init-param, com.sun.ws.rest.config.property.packages, does not work in WebSphere. So I had to do some slight modifications to get it to work without that being declared. My pom.xml and jetty.xml files are below. Most "normal" non-Jersey applications don't need all of this, but it was necessary to get our legacy WAR working with jetty.

Here are the steps involved to start jetty and redeploy changes:

  1. mvn install jetty:run - this will first build the WAR and then start jetty while also deploying the WAR. Running install was necessary because I reference the exploded WAR directory under target in the jetty configuration.
  2. Make changes to source code
  3. Run "mvn compile war:exploded" in a separate terminal to compile your changes and copy the new class files to the location Jersey expects to find them. Which in my case is /target/myapp/WEB-INF/classes
  4. Click back to the terminal running jetty and hit the ENTER key. This causes jetty to reload the WAR. This is because by default I set the scan interval to 0 and jetty.reload to manual, so I can batch up multiple changes before reloading.
Overall I am happy with the results so far. Previously, it took 1-2 minutes and sometimes more to rebuild the war and hotdeploy to JBoss. Using the jetty plugin this now takes around 30 seconds. Again, I think this could be further improved by using JRebel.

Tips
I did have to update my MAVEN_OPTS environment variable to increase Java's PermGenSpace since jetty reloads the WAR each time and you'll quickly run out of memory. This was something I was already doing in JBoss. Here is what it is set to:

export MAVEN_OPTS="-Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m"

Sample Files
Here is my pom.xml
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.22</version>
<configuration>
<jettyConfig>${project.build.testOutputDirectory}/jetty.xml</jettyConfig>
<scanIntervalSeconds>${jetty.scan.sec}</scanIntervalSeconds>
<useTestClasspath>true</useTestClasspath>
<webAppConfig>
<baseResource implementation="org.mortbay.resource.ResourceCollection">
<resourcesAsCSV>${project.build.directory}/myapp</resourcesAsCSV>
</baseResource>
</webAppConfig>
<systemProperties>
<systemProperty>
<name>jetty.port</name>
<value>${jetty.port}</value>
</systemProperty>
</systemProperties>
<systemProperties>
<systemProperty>
<name>jetty.reload</name>
<value>${jetty.reload}</value>
</systemProperty>
</systemProperties>
</configuration>
<dependencies>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0</version>
</dependency>
</dependencies>
</plugin>
.....
<properties>
<jetty.port>8080</jetty.port>
<jetty.scan.sec>10</jetty.scan.sec>
<jetty.reload>manual</jetty.reload>
</properties>

Here is my jetty.xml located under /src/test/resources used to define the Datasource.
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<Configure id="Server" class="org.mortbay.jetty.Server">

<New id="MYAPP-DS" class="org.mortbay.jetty.plus.naming.Resource">
<Arg>jdbc/MYAPP-DS</Arg>
<Arg>
<New class="org.apache.commons.dbcp.BasicDataSource">
<Set name="driverClassName">oracle.jdbc.driver.OracleDriver</Set>
<Set name="url">jdbc:oracle:thin:@localhost:1521:XE</Set>
<Set name="username">user</Set>
<Set name="password">password</Set>
</New>
</Arg>
</New>

</Configure>

Thursday, January 28, 2010

Precompiling JSPs for WebSphere 6.1

Don't envy me..........Your about to witness my efforts from the past 2-3 weeks.

For the past week I have been trying to get precompiled JSPs working for WebSphere 6.1, because our target environment does not include the Java Development Kit (JDK) for security reasons. The following is a brief explanation on how to precompile JSPs for WebSphere 6.1 using maven2. And as an added bonus, I'll also explain how to use the maven-was6-plugin to automate deployment of a WAR using Jython.

Precompiling JSPs for WebSphere
First off, let me say, this was not fun. I spent a ton of time trying to figure out why the precompiled JSPs worked on JBoss 4.2.1, but not WebSphere 6.1. Both maven jspc plugins (jspc-maven-plugin and maven-jetty-jspc-plugin) produced precompiled JSPs that worked on JBoss, but not WebSphere. From what I could tell it all boiled down to the fact that JBoss 4.2.1 includes the 2.1 version of jsp-api while WebSphere 6.1.0.27 includes the 2.0 version. Just so you believe me, the 2 conflicting jars in WebSphere were: $WAS_HOME/lib/j2ee.jar and $WAS_HOME/plugins/com.ibm.ws.webcontainer_2.0.0.jar. On the plus side, I did find a great Java decompiler for linux called jd-gui. I'd highly recommend it for any operation system. It uses jad, but provides a nice GUI interface that even lets you open a jar and explore any .class file.

In summary, based on my experience, the 2 existing maven jspc plugins do not create compatible precompiled JSPs with WebSphere 6.1.0.27. Surprisingly, the solution that ended up working was using the JspBatchCompiler.sh script that comes with WebSphere.

The JspBatchCompiler.sh was exactly what we needed. Thankfully, you can give this script a WAR or EAR file and it will explode it, precompile all the JSPs, and repackage it back up again. This script is located under $WAS_HOME/bin. Once I verified it by manually running the script, the next step was to automate it using maven. Since I wasted so much time figuring everything else out, I didn't spend a ton of time improving the maven portion. Instead I decided to go with what I knew and that was using the exec-maven-plugin to run the script. The following is the profile I used to precompile the JSPs in the WARs located in the EAR.



<profile>
<id>precompile</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>precompile-was-ear-jsps</id>
<phase>validate</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${wasHome}/bin/JspBatchCompiler.sh</executable>
<arguments>
<argument>-ear.path</argument>
<argument>${pom.basedir}/target/${project.artifactId}-${project.version}.${project.packaging}</argument>
<argument>-jdkSourceLevel</argument>
<argument>15</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>rename-replace-was-ear</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<move file="${java.io.tmpdir}/${project.artifactId}-${project.version}.${project.packaging}"
tofile="${pom.basedir}/target/${project.artifactId}_jspc-${project.version}.${project.packaging}"/>
<delete file="${pom.basedir}/target/${project.artifactId}-${project.version}.${project.packaging}"/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
The first plugin section uses the exec-maven-plugin to run the JspBatchCompiler.sh and takes the EAR as input. The second plugin section uses the maven-antrun-plugin to basically rename the EAR to include a keyword (_jspc) in the EAR filename so everyone knows when they have an EAR with precompiled JSPs. It then moves the new EAR from it's tmp location back to the modules target directory where everything expects it to be. Once that is done, it removes the old EAR to avoid confusion.

About the only improvement that could be made is making it portable to other Operating Systems like Windows. This "could" be accomplished by using the JspC ant task WebSphere provides, but I couldn't find any good examples of how to do that via maven, so I took a rain check.

Deploy WAR to WebSphere 6.1
This Jython code snippet literally saved our sprint. I am not sure how I would have automated deploying and undeploying a WAR via maven without it as the maven-was6-plugin really only works with EARs. This is because when deploying a WAR you need to provide the WARs contextroot, which the plugin currently doesn't support (MWAS-59). I was however able to call the Jython script from the maven-was6-plugin to undeploy and deploy a WAR.

The following profiles and Jython scripts show how to use maven and Jython to undeploy and deploy a WAR. The Jython scripts exist in files under the same directory as the pom.



<profile>
<id>undeploy-war</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>was6-maven-plugin</artifactId>
<executions>
<execution>
<id>undeploy</id>
<phase>validate</phase>
<goals>
<goal>wsAdmin</goal>
</goals>
</execution>
</executions>
<configuration>
<wasHome>${wasHome}</wasHome>
<profileName>AppSrv01</profileName>
<conntype>SOAP</conntype>
<applicationName>petstore_war</applicationName>
<earFile>${pom.basedir}/target/petstore.war</earFile>
<updateExisting>false</updateExisting>
<language>jython</language>
<script>uninstallApp.py</script>
<host>localhost</host>
</configuration>
</plugin>
</plugins>
</build>
</profile>

# File: uninstallApp.py
# Jython script to undeploy WAR
# FYI, the was6 plugin does support the ability to pass in params to the jython script
cellName = 'testbed01Node01Cell'
nodeName = 'testbed01Node01'
serverName = 'server1'

#Install the app
print "Installing App: "
AdminApp.install("../petstore.war", "-contextroot /petstore -defaultbinding.virtual.host default_host -usedefaultbindings");
AdminConfig.save();

#Start the app
apps = AdminApp.list().split("\n");
theApp = ""
for iApp in apps:
if str(iApp).find("petstore") >= 0:
theApp = iApp;
print "Starting App: ", theApp
appManager = AdminControl.queryNames('cell='+cellName+',node='+nodeName+',type=ApplicationManager,process='+serverName+',*')
AdminControl.invoke(appManager, 'startApplication', theApp)
print "Application installed and started successfuly!"

Here is the profile and Jython script I used to Deploy a WAR:


<profile>
<id>deploy-war</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>was6-maven-plugin</artifactId>
<executions>
<execution>
<id>deploy</id>
<phase>validate</phase>
<goals>
<goal>wsAdmin</goal>
</goals>
</execution>
</executions>
<configuration>
<wasHome>${wasHome}</wasHome>
<profileName>AppSrv01</profileName>
<conntype>SOAP</conntype>
<applicationName>petstore_war</applicationName>
<earFile>${pom.basedir}/target/petstore.war</earFile>
<updateExisting>false</updateExisting>
<language>jython</language>
<script>installApp.py</script>
<host>localhost</host>
</configuration>
</plugin>
</plugins>
</build>
</profile>
# File: installApp.py
# Jython script to deploy WAR
# FYI, the was6 plugin does support the ability to pass in params to the jython script
cellName = 'testbed01Node01Cell'
nodeName = 'testbed01Node01'
serverName = 'server1'

#Install the app
print "Installing App: "
AdminApp.install("../petstore.war", "-contextroot /petstore -defaultbinding.virtual.host default_host -usedefaultbindings");
AdminConfig.save();

#Start the app
apps = AdminApp.list().split("\n");
theApp = ""
for iApp in apps:
if str(iApp).find("petstore") >= 0:
theApp = iApp;
print "Starting App: ", theApp
appManager = AdminControl.queryNames('cell='+cellName+',node='+nodeName+',type=ApplicationManager,process='+serverName+',*')
AdminControl.invoke(appManager, 'startApplication', theApp)
print "Application installed and started successfuly!"

That's it. The Jython scripts could be improved by making the cell, node, and server names configurable instead of hardcoded and it "appears" the maven-was6-plugin supports passing in properties, but I just didn't have the time to figure it out at the moment.

By the way, I hope maven 3 has solved the XML verbosity when it comes to doing simple things like creating profiles. That's a lot of XML to do very little.

Monday, April 13, 2009

Better Offline Capabilities with Maven 2.0.10

This week while traveling on business, I had hoped to get a lot of work done, but was quickly disappointed when I wasn't able to build because maven couldn't download the latest SNAPSHOTs. Even though I had fresh local SNAPSHOT versions that would suffice.

Fortunately, maven 2.0.10 was recently released and it promised fixes for this exact situation (see release notes). Currently I am happily using version 2.0.9.

So now that I am at the hotel I decided to see if an upgrade would help me. I first built the submodule again to make sure I got the dreaded unable to download dependency. Downloaded and installed 2.0.10 and rebuilt again. And I am very excited to report that it worked

To stay tuned into everything Maven, subscribe to Brian's Enterprise Blog at Sonatype. Brian Fox is one of the head developers for Maven and according to Google Reader I read 100% of his posts.

Wednesday, October 22, 2008

More tips on using the maven2 release plugin

This blog's most frequently visited post was the one I did over a year ago titled "How to create a release using the maven2 release plugin". Automating this portion of our frequent release process without a doubt has saved my team hundreds of hours over the past year. For that reason I would like to provide some new tips I discovered yesterday when I needed to change the subversion comment used by the release plugin when committing any changes.

For teams not using the maven-release-plugin yet, this handy plugin helps automate the complete process when releasing your software. It helps save time by doing the following and more:

  • First it will build your project running any tests you specify
  • If successful, it will then commit your project into a tag
  • Update the tag pom versions from say 1.0.0-SNAPSHOT to 1.0.0
  • Change the tag pom SCM URL to correctly point to the tag instead of HEAD
  • Most importantly, it will build the release (1.0.0) and upload the artifacts to your companies maven2 repository (archiva now in our case; was artifactory).
  • Finally, it will increment the pom versions in HEAD to be the next release (1.0.1-SNAPSHOT).

Tip #1 - Change Commmit Comment
By default the maven-release-plugin uses a comment like, [maven-release-plugin] prepare release project-1.0.0, when doing any type of commit such as creating a tag/branch or incrementing the pom versions. Which works wonderful until your team implements a Subversion pre-commit hook requiring all commits to start with certain keywords (Story: ZZZ, Jira: ZZ-N). Luckily the maven-release-plugin has an option to override the comment.

There are two simple ways to provide the plugin with the comment. The first sets the system property, scmCommentPrefix, to the predefined prefix. The second, provides the prefix comment in the pom.

mvn release:clean release:prepare -DscmCommentPrefix=Jira: AC-100 [maven-release-plugin]

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<scmCommentPrefix>Jira: AC-100 [maven-release-plugin]</scmCommentPrefix>
</configuration>
</plugin>
<plugins>

Tip #2 - Configuration Options via -D
Disappointed that I didn't catch this earlier, especially since I have been using -DpreparationGoals for awhile now, but all of the optional parameters for the prepare goal can be defined with -D rather than in the POM (as above). So if you wanted to change the default tagBase to use /branches instead of the default /tags do the following. However, I would still put this static value in the pom.
mvn release:clean release:prepare -DtagBase=http://project.svn.org/svn/project/branches
Tip #3 - Allow SNAPSHOT dependencies
This new feature is going to be huge for legacy projects who weren't raised with the release plugin. As mentioned in my previous post, previous versions of the release plugin didn't allow you to have SNAPSHOT dependencies; rightfully so too since that wouldn't be a very good practice to have a release depend on a changing dependency. However, I think this feature prevented many projects from using the release plugin because their snapshot depenencies were too many to change and it was just easier to continue doing same old.

Even though I have not used this feature, it would seem now one could release a project using this plugin and still have SNAPSHOT dependencies. However, after creating the release it would still be a good idea to go back in the release and change any SNAPSHOT dependencies to releases.

I believe this is a new feature so you might also want to upgrade to the latest version.

Thursday, July 3, 2008

Maven not downloading latest snapshots or releases

Ever had issues with maven not downloading the latest snapshots when you know for a fact new snapshots are available? Or your CI environment just deployed a new release (2.0), but when another Hudson job builds, maven does not download the latest 2.0 release artifact. Want an automated solution so you don't have to manually delete the artifacts from your local repository just so maven will download the latest?

Force maven to download latest snapshots
Our company uses Hudson for our automated CI environments. Our project basically has two jobs. The first job checks out and builds HEAD when modified and deploys SNAPSHOT WARs to our companies maven2 repository (artifactory). The second job, which builds nightly, uses maven to download the SNAPSHOT WARs from artifactory, creates an EAR, deploys it to JBoss, and runs integration tests. By default, maven will check once a day for changes to snapshots, so when our second job was triggered, maven inside hudson was not downloading the latest SNAPSHOT WARs.

The solution was to append the -U in the maven goals (run mvn -help). It stands for update-snapshots and tells maven to update all snapshots no matter what.


Force maven to download latest releases
Our next problem was when we created a branch and started creating release artifacts such as 2.0. Unfortunately the description given by maven for the -U option is incorrect (or at least in v 2.0.9), "Forces a check for updated releases and snapshots on remote repositories". As much as I tried, the -U option wouldn't work in our hudson job to force maven to download the latest non-snapshot releases.

The only current solution I know of is to use the maven-dependency-plugin and its goal purge-local-repository. So in your maven goals at some point execute mvn dependency:purge-local-repository and maven will physically delete your projects artifacts from the local repository (/home/user/.m2/repistory) and its transitive dependencies (I think). I tried setting the actTransitively to false and it didn't work for us so I just removed it. I also set verbose to true so I could see what maven deleted in Hudson's console output.



The pipes are used to separate out different goals to isolate its classpath or properties. That way we can skip tests in one run, and then run them in the next all in the same goals section.

Thursday, May 1, 2008

Pimp my Linux: Maven2 Bash Completion

Inspired by this bash completion for ssh, I wanted to see what it would take to do something similar for maven2. Thanks to google it's already been done.

It's not perfect, but it's a start. For example, the choices are hard coded in the script. And when I tab after typing mvn assem it outputs mvn assembly\:assembly. Shouldn't be too hard to find that extra slash.

Here are some thoughts on how one could improve it. First, maybe it could be improved for at least the core plugins by searching the local repo in /m2/repository/org/apache/maven/plugins. Or searching the local repo for any pom that contains a packing value of maven-plugin (maven-plugin). Combine that with the help plugin to get the possible goals and all their parameters and one could probably create a pretty slick and useful bash completion for maven2.

For example, you can get everything you need for any plugin if you know the groupId and artifactid by running this:
mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-war-plugin -Dfull=true

I say all this, but I use a lot of alias's for my maven commands. Here are a few of my favorites:

alias mci='mvn clean install'

alias build='mvn clean install -Dmaven.test.skip=true -Dpmd.skip=true -Dcheckstyle.skip=true'

Using Maven War Overlays to extend Hudson

I just recently found out about one of the neatest features of the maven-war-plugin called WAR Overlays. Basically it provides a very simple way to merge multiple WARs together to create an Uber WAR. You simply add a WAR as a dependency in your POM verses adding a JAR, and the maven-war-plugin will take care of the rest. My team uses this ability to extend the JSPWiki WAR to add in our wiki pages. The result is an Uber WAR including the JSPWiki stuff and our wiki pages. Then when a new JSPWiki WAR is available we just update the dependency version in our POM.

So for an example, I am going to demonstrate extending my favorite CI tool Hudson since it's freaking awesome and is downloaded as a WAR. Don't try this at home since hudson already provides the ability to extend it using plugins (which also rocks by the way).

Create a Simple WAR Project
First, create a war project using maven-archetypes. Execute mvn archetype:generate and select #18. Run mvn clean install to ensure it builds correctly (I am using maven v2.0.9).

Install hudson into local repository
Next we need to be able to consume the hudson war in our pom and since I am unware of the hudson war being available on any external repository we are going to just install it manually into our local m2 repository.

  • Download the latest hudson war
  • Install hudson.war into your local repository using the mvn install plugin by running: mvn install:install-file -Dfile=hudson.war -DgroupId=hudson -DartifactId=hudson -Dversion=1.0 -Dpackaging=war
Consume the hudson.war in your pom
Open up your war's parent pom and add the hudson war as a dependency.
<dependency>
<groupId>hudson</groupId>
<artifactId>hudson</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
Next, since we want to run hudson in embedded mode without a container we need to add to the generated manifest file the Main class since I am too lazy to figure out how to include the original hudson manifest file (even though I am sure its possible). Include the following in your build section:
<build>
<finalName>mywar</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Main-Class>Main</Main-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
Build and Run it
Now build the war again: mvn clean install. And your WAR should be merged with the hudson war. In fact for verification, your simple war contained an index.jsp under src/main/webapp and if you extract the war under target you will see your index.jsp.

Run: java -jar target/mywar.war
and go to: http://localhost:8080/index.jsp

If you are doing this on a serious level, you might want to look into the maven-cargo-plugins ability to create Uber wars.

Thursday, April 10, 2008

Update: Always specify a version for Maven2 plugins

I previously wrote about specifying a release version for maven plugins in your POM files to have a more stable build and stop depending on SNAPSHOT plugin dependencies. It now seems with the latest version of maven2, version 2.0.9, that maven now comes with release versions for all the commons plugins (see the release notes).

"MNG-3395 - Starting in 2.0.9, we have provided defaults in the super pom for the plugins bound by default to the lifecycle and a few other often used plugins. This will introduce a bit of stability to your builds because core plugins will not change magically on you when they are released. We still recommend taking control of your plugin versions via pluginManagement declarations as this is the most robust way to future proof your builds. Defaulting the plugins in the superpom was a step towards introducing stability for small builds and new users. A full table of the versions used is shown in the next section."

Note how they still recommend specifying release versions in the pluginManagement section of your POM.

Saturday, September 22, 2007

How to effectively use SNAPSHOT

Recently I showed how you can use the maven-release-plugin to create a release for your project. Now I would like to explain how to use maven to effectively use SNAPSHOT.

First what do I mean by SNAPSHOT? SNAPSHOT is a special version in maven that indicates the latest code; typically TRUNK or HEAD in your source control. With this version, maven will automatically grab the latest SNAPSHOT every time you build. On the other hand, when you are using 2.0, once maven has downloaded this artifact, it never tries to get a new 2.0.

Why should you use SNAPSHOT? You should use SNAPSHOT for rapidly moving code where bug fixes and enhancements are coming fast. Another reason is you should never require users of your project to checkout and build your code. EVER! I would venture to say that 50% of the open source projects I have had to checkout and build, have not initially built.

How to create a SNAPSHOT?
First you have to find a webserver to host the artifacts. I have typically used apache running on linux. I first create a folder under / called snapshots (owner:group equal to repoman:repoman) and then create a softlink (ln -s) under /var/www/html called snapshots (owner:group being root:root). So now you should have /var/www/html/snapshots --> /snapshots. For some examples of some public SNAPSHOT repositories check out Apache's and Codehaus.

Second you need to update your parent POM in order to properly use the maven-deploy-plugin. First make sure the version you are using includes -SNAPSHOT. For example something like 0.9.2-SNAPSHOT.

Third add the distributionManagement section to your parent POM to look something like this:

<distributionManagement>
<snapshotRepository>
<id>maven2-snapshot-repository</id>
<name>Maven2 Snapshot Repository</name>
<url>scp://host/snapshots/</url>
<uniqueVersion>false</uniqueVersion>
</snapshotRepository>
</distributionManagement>
This will use scp to upload the artifacts to the url //host/snapshots. The element that needs to receive the most of your attention is <uniqueVersion>. This value can either be true or false. When it is false the artifacts created will not be unique and will look something like this each time: myproject-0.9.2-SNAPSHOT. Use this to reduce disk usage. When it is set to true a unique version is created everytime. For example myproject-0.9.2-20070922.011633-1.jar and then the next build that day would be myproject-0.9.2-20070922.011633-2.jar.

Finally, the last thing is to set the username and password of the repository in your $USER_HOME/.m2/settings.xml file. This will be used by scp for authentication. To do this add the following in your settings.xml file:
<servers>
<server>
<id>maven2-snapshot-repository</id>
<username>repoman</username>
<password>mypassword</password>
</server>
<servers>
That is it. Now you are ready to run mvn deploy.

Friday, September 14, 2007

How to create a release using the maven2 release plugin

If you haven't figured out how to simplify creating a release for your maven2 projects then you need to look into the maven-release-plugin. This powerful plugin helps shorten the time it takes when creating a release and we have used it extensively on our open source projects: RSS BC, SIP BC, UDDI BC, and the XMPP BC.

Kicking It Old School
Back when we were using maven1 we actually used a sed script to update the versions in the project.xml and project.properties files. Boy those where the days; how I don't miss them. However, I was still impressed with it so here you go. Create a script named swap:
#!/bin/sh
cp $1 $1.backup
sed -e s/"$2"/"#3"/g $1 > temp
cat temp $1

Browse to your project and run:
find -name 'project.properties' -exec ~/swap {} 1.1.0-SNAPSHOT 1.1.0 \;
find -name 'project.xml' -exec ~/swap {} 1.1.0-SNAPSHOT 1.1.0 \;

It was effective and did the job (in fact it's easier to use a perl script), but you still had to create the tag/branch and commit. Also with maven2 POMs you have other values that have to be updated like the <scm> information.

How to use the maven-release-plugin
First, you need to clean up your POMs. Here are three basic guidelines that will make your life easier when managing them:

  1. Avoid declaring groupIds and versions where it is not required. For example in child POMs, if the groupId and version are the same as the parent, you don't have to define your own groupId or version. By default it inherits from the parent.
  2. If you do have to declare versions, use the section of your parent POM. This helps define your downstream dependencies and prevents the child POMs from having to declare the versions that are shared among the project.
  3. Use properties to define other common versions.
For a real example, view the RSS BCs parent pom, and subsequent child poms: extensions, jar, and zip. Notice the use of properties and dependencyManagement in the super pom. Also note how the child POMs don't have to declare versions for inner project dependencies that are defined in the parent POM under dependencyManagement.

Now that you have your POMs cleaned up it's time to use the maven-release-plugin. It doesn't actually require these changes, but I am anal so sorry.

Step1
Make sure your parent POM has the <scm> section describing the projects source control location.

Step2
Get a clean copy of your project. The maven-release-plugin will not run if you have locally modified files; including IDE files or the target folder.

Step3
Run mvn release:prepare -DdryRun=true
This will walk you through a dry run and you can view the temporary POMs it creates to verify you did everything correctly.

Step4
Run mvn release:clean release:prepare -Dusername=johndoe -Dpassword=passwd
This will actually commit a tag with non-snapshot versions of your project and also increment all your versions to the next release. So for example, if your current version is 0.9.1-SNAPSHOT, it will commit a tag with 0.9.1 version, and update the trunk POMs with 0.9.2-SNAPSHOT. Of course when using the prepare goal you are prompted for the release numbers and next release numbers.

Step5
Run mvn release:perform
The perform goal basically runs deploy and site-deploy. This step is optional and I typically don't run it.

Gotcha
One gotcha is if your project has a SNAPSHOT dependency on an artifact outside your project, the release plugin doesn't handle that (or at least I haven't figured it out). The prompts seem to ask questions about what version they need to be, but never actually does anything with the information. Hopefully this will be fixed in future versions. In the mean time, use properties, and when running mvn release:prepare use the -D option to specify the version and then manually update the tag and trunk pom to reflect the real version. So something like mvn release:prepare -DdryRun=true -Dcommon.jbi.runtime.version=0.9.2.

Windows Users
The other issue you might have is if you are using Windows. Since I am using Linux the svn commands are already installed and I use them frequently. And since the release plugin uses the svn commands to do its work, you will need to install the svn client commands in order to it in DOS. To do that go here, go down to the Windows section, and click Win32 packages built against Apache 2.2. In there download and install the svn-1.4.6-setup.exe. After installing it, open up a new DOS window and run svn help.

For further tips and tricks read the Gestalt Developers Guidelines for creating a release using the maven-release-plugin.

Related Articles

Wednesday, August 8, 2007

Setting up a simple maven2 project in less than 1 minute

Ever need to create a new maven2 project? Was your next step to copy and paste an existing maven2 project and rename all the directories, package folders, and oh yeah don't forget to remove those SCM (cvs/svn) hidden directories. That's not all, don't forget to update the pom's groupId, artifactId, and everything else under the POM sun.

Using the maven-archetype-plugin you can create simple projects in a matter of seconds. "Archetypes are a simple and useful way to bootstrap new development across your organization, and urge your developers to follow a similar project pattern. Archetypes are a template of a Maven project used to generate skeleton layout for projects of any desired type in a consistent way" (Maven: The Definitive Guide).

The default archetype is quickstart, and generates a simple Java project with some source and a unit test. Here is how you can use the quickstart archetype to create a maven2 project in seconds saving you from the copy/paste world:

mvn archetype:create -DgroupId=com.gestalt.jbi.rss.binding -DartifactId=rss-binding-installer

Thats it! Next you can run mvn clean install and you now have a simple Java project created.

If a simple J2SE project isn't what you need then check out these other 10 archetypes. Most notably are:

  1. maven-archetype-archetype - used to create a simple project that can be used to build other archetypes
  2. maven-archetype-j2ee-simple - build a simple j2ee project
  3. maven-archetype-mojo - in maven2, mojos comprise a maven plugin, so this archetype creates a project that gets you set to build your own maven plugin
Plus there are many many more available in the open source community. For example when working with Apache ServiceMix and maven2, use their maven archetypes to build Service Engines or Binding Components. They also have archetypes to help simplify creating Service Units/Service Assemblies for their various components. Sun also has support for archetypes to create components as well.

Tuesday, July 24, 2007

How to capture logging output in unit tests

When writing unit tests for java, its extremely valuable to view the log statements produced by your unit tests and java code. The following is a brief introduction on how to configure maven1 and maven2 projects to capture logs.

Maven1
The maven-test-plugin is used for running unit tests in maven1 projects. It is best to configure this plugin at a global level in your build.properties file located under your $USER_HOME.

First add the following to your $USER_HOME/build.properties file:

maven.junit.sysproperties=log4j.configuration
log4j.configuration=file:/c:/Documents and Settings/jlorenzen/log4j.properties

Second add a log4j.properties file under your $USER_HOME directory. Mostly likely this will include a ConsoleAppender. By using a ConsoleAppender the maven-test-plugin will capture the logs and write them to the console. However its cleaner if you set the property maven.junit.usefile=true in your build.properties file. This will put all your logs in the .txt test report file.

Maven2
Unfortunately things are a bit different for maven2 projects. You no longer use the maven-test-plugin but rather the maven-surefire-plugin. The following example will use java logging.

To get started open up your project's parent pom.xml and add the following for the maven-surefire-plugin.

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<property>
<name>java.util.logging.config.file</name>
<value>${logging.location}</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
Next you need to define the value for the variable ${logging.location}. You do this in your settings.xml file which is located under $USER_HOME/.m2. If you do not already have a settings.xml file you can get an empty one under your maven2 install in the directory $M2_HOME/conf. To define the value for ${logging.location} create a profile in your $USER_HOME/.m2/settings.xml file like the following:
<profiles>
<profile>
<id>configure.logging.location.profile</id>
<properties>
<logging.location>${user.home}/.m2/logging.properties</logging.location>
</properties>
</profile>
</profiles>

Next you need to activate the profile by adding the following in your settings.xml:

<activeprofiles>
<activeprofile>configure.logging.location.profile</activeprofile>
</activeprofiles>
Since the surefire plugin does not put log statements in the output file when using the redirectTestOutputToFile, viewing the log statements in the console can be a bit overwhelming. To make this easier consider using the test single goal: mvn -Dtest=ClassName test

Sunday, July 15, 2007

Pluging

What is pluging: knowledge gained while learning/using maven2 plugins? I call it this because my fingers always put an extra 'g' on the end when I type the word plugin. To me pluging is fun. It's like reading a keyboard shortcut list for an IDE; you can just feel yourself being more productive. So while reading "Maven: The Definitive Guide" I naturally felt excited to learn more about maven2.

Currently I am only on the second chapter and the author points out an uncommon plugin (uncommon to me anyways) that seems pretty handy. Check out the help plugin and run this goal: mvn help:describe -Dplugin=jar -Dfull. The describe goal prints out the plugin's goals and properties. The output is the same information you can get by browsing the plugin's home page. If the information is too much, the help plugin also supports an output property: mvn help:describe -Dplugin=jar -Dfull -Doutput=jar.goals. Nothing super fancy but saves you the step of googling for the plugins site and also gives you the opportunity to explore possible options.

I would also highly recommend viewing the other maven2 plugins. Some of the plugins I would like to get to know are:


  1. maven-pmd-plugin - code analysis tool like findbugs.

  2. maven-assembly-plugin - creates source and binary distributions and much more.

  3. maven-enforcer-plugin - validates the user's environment. Comes in handy when you require that your project builds with a certain version of maven or jdk.

  4. maven-one-plugin - bridges maven1 and maven2. Would come in handy since I work on maven1 and maven2 projects.

  5. maven-release-plugin - helps in releasing a project: tagging, branching, updating the POM, etc.