Announcing Desktop Notifier for Maven 1.0

We all know that builds are supposed to be fast. Otherwise, we just end up swordfighting. But sometimes the build just takes too long, either because you have an empty ~/.m2/repository, or the integration tests take a lot of time to run, or … . But you get the point.

For this very reason I ( as the saying goes, with a little help from my friends ) built a Maven extension which shows desktop notifications whenever a build is complete. It’s smart enough to notify you at the end of the reactor build for multi-module projects and it distinguishes between success and failure.

The notifier support Linux, OS X and Windows with specific notification senders, with a fallback on the AWT APIs whenever we do not find a preferred sender.

The 1.0 version is available for download in the Github downloads area. To install it, just drop the jar file under lib/ext in your Maven installation.

Advertisement

Maven Recipe : GWT development profile

It’s no secret that I am a fan of GWT for web development. I believe it to be a superior solution for large-scale Javascript development. However, it does come with some downsides. Verbosity is one of them, but we have a solution for that – see how to cut down GWT’s verbosity. Another one is the long compilation time.  Since GWT generates permutations for multiple browser targets – 6 in version 2.1 – you will wait more than needed if you just need to compile for one browser.

It often is the case that a single-permutation application is deployed for smoke testing, and then a application with all permutations is deployed to Q&A. This is usually achieved by creating a development module which reduces the number of permutations to one. I will show you how to integrate this in a natural way into your Maven build.

I will start with the simplest possible Maven/GWT project, as shown below:

I will not insist over the contents of the project, as it is not important. The project only has one client-side class, and is based on the archetype from the excellent gwt-maven plugin from Codehaus.

In the pom.xml, the module to compile is configured to be the production one:

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>gwt-maven-plugin</artifactId>
 <version>2.1.0-1</version>
 <!-- contents omitted -->
 <configuration>
  <!-- contents omitted -->
  <module>ro.lmn.maven.recipe.gwtdev.ProfileDemo</module>
 </configuration>
</plugin>

A compiler run currently generates six permutations and takes 23 seconds on my machine. I am lucky to have four cores at my disposal, but usually build server CPU time is scarce, so the effect is amplified.

robert@neghvar:~/git-repos/gwtdev-profile (master)> mvn clean package
...
[INFO] --- gwt-maven-plugin:2.1.0-1:compile (default) @ gwtdev-profile ---
[INFO] Compiling module ro.lmn.maven.recipe.gwtdev.ProfileDemo
[INFO]    Compiling 6 permutations
[INFO]       Compiling permutation 0...
[INFO]       Process output
[INFO]          Compiling
[INFO]             Compiling permutation 1...
[INFO]       Compiling permutation 2...
[INFO]       Compiling permutation 3...
[INFO]       Compiling permutation 4...
[INFO]          Compiling
[INFO]             Compiling permutation 5...
[INFO]    Compile of permutations succeeded
[INFO] Linking into /home/robert/git-repos/gwtdev-profile/target/gwtdev-profile-1.0-SNAPSHOT/ProfileDemo
[INFO]    Link succeeded
[INFO]    Compilation succeeded -- 22.702s
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 29.101s
[INFO] Finished at: Sat Mar 05 23:59:24 EET 2011
[INFO] Final Memory: 12M/142M
[INFO] ------------------------------------------------------------------------

To cut down on compilation time I will use a Dev module which compiles just for recent versions of FireFox:

<?xml version="1.0" encoding="UTF-8"?>
<module rename-to="ProfileDemo">
 <inherits name="ro.lmn.maven.recipe.gwtdev.ProfileDemo" />
 <set-property name="user.agent" value="gecko1_8" />
</module>

To select it in my pom.xml I will create a dev profile, which overwrites some of the configuration entries of the gwt-maven-plugin:

<profiles>
 <profile>
 <id>dev</id>
  <build>
   <plugins>
    <plugin>
     <groupId>org.codehaus.mojo</groupId>
     <artifactId>gwt-maven-plugin</artifactId>
     <configuration>
      <module>ro.lmn.maven.recipe.gwtdev.ProfileDemoDev</module>
      <draftCompile>true</draftCompile>
     </configuration>
    </plugin>
   </plugins>
  </build>
 </profile>
</profiles>

For added speed, I’ve enabled draft compilation, which means that the GWT compiler will spend less effort on trying to optimise the resulting javascript.

Rerunning the build with the -Pdev argument results in just one permutation being generated:

robert@neghvar:~/git-repos/gwtdev-profile (master)> mvn -Pdev clean package
...
[INFO] --- gwt-maven-plugin:2.1.0-1:compile (default) @ gwtdev-profile ---
[INFO] Compiling module ro.lmn.maven.recipe.gwtdev.ProfileDemoDev
[INFO]    Compiling 1 permutation
[INFO]       Compiling permutation 0...
[INFO]    Compile of permutations succeeded
[INFO] Linking into /home/robert/git-repos/gwtdev-profile/target/gwtdev-profile-1.0-SNAPSHOT/ProfileDemo
[INFO]    Link succeeded
[INFO]    Compilation succeeded -- 14.091s
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 19.939s
[INFO] Finished at: Sun Mar 06 00:08:55 EET 2011
[INFO] Final Memory: 11M/104M
[INFO] ------------------------------------------------------------------------

The speed saving is of 35% and will likely increase as the GWT code increases in size and complexity.

We have reached our goal of creating a GWT development profile. By using a Maven profile we can make minimal modifications to our configuration and even apply different compiler options.

The complete source code for this article is available at https://github.com/rombert/Maven-Recipe—GWT-development-profile. If you have any suggestions or corrections, please comment. Or better yet, fork me.

Maven Recipe: Delivering applications as RPMs

In the Java world, the standard unit of delivery is the jar/war/(x)ar file. For applications which don’t fit the ‘single file’ delivery model, there is no standard alternative.  To provide a solution, I will show you how to package your application with Maven as an RPM.

As we are talking about Maven, the solution starts with finding the right plugin – in our situation the rpm-maven-plugin. We include the plugin as follows:

 <groupId>org.codehaus.mojo</groupId>
 <artifactId>rpm-maven-plugin</artifactId>
 <version>2.0.1</version>

The goal we need to execute is rpm – and it attaches itself to the package phase by default – so far so good.

The configuration that is usually done in a RPM spec file is done inside the plugin’s configuration block. First, we list some required descriptive elements:

<configuration>
  <copyright>2010, NoBody</copyright>
  <group>Development</group>
  <description>Maven Recipe: RPM Package.</description>

Now we get to the core of the RPM – listing the files to deploy. As we want to deploy our application’s classes and the ones from our dependencies, we create a mapping:

<mappings>
  <mapping>
    <directory>${app.home}/lib/</directory>
    <dependency/>
  <artifact/>
</mapping>

This is a very compact way of declaring that we want all dependencies – the dependency tag – and our primary build artifact – the artifact tag – to be deployed to the lib directory of our application.

The next step is to take the app.properties file which is checked into source control and deploy it as a sample file in the conf directory, so that we have a reference at hand when configuring the application:

<mapping>
  <directory>${app.home}/conf</directory>
  <configuration>true</configuration>
  <sources>
    <source>
      <location>${project.build.outputDirectory}/app.properties</location>
      <destination>app.sample.properties</destination>
    </source>
  </sources>
</mapping>

Using location and destination we have absolute control over what is included and where it is placed. Also note the usage of the configuration tag, which translates in to the %config RPM macro, which means that changes to the file are preserved when updating or removing RPM  ( details on the RPM %config macro ).

Last, we need to generate an empty logs directory for our application. This is done simply by declaring a mapping with no sources.

<mapping>
  <directory>${app.home}/logs</directory>
</mapping>

Right now we can build a RPM out of our application. I’ve added hibernate 3.3.2.GA as a dependency, since it brings in a few transitive dependencies.

robert@neghvar:~/workspace/rpm-package> mvn clean package
[INFO] Scanning for projects...                          
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Recipe: RPM Package                                      
[INFO]    task-segment: [clean, package]                                       
[INFO] ------------------------------------------------------------------------
.........
[INFO] Wrote: /home/robert/workspace/rpm-package/target/rpm/rpm-package/RPMS/rpm-package-0.0.1-SNAPSHOT20100520205409.noarch.rpm   
...........
[INFO] ------------------------------------------------------------------------                                                     
[INFO] BUILD SUCCESSFUL                                                                                                             
[INFO] ------------------------------------------------------------------------                                                     
[INFO] Total time: 5 seconds                                                                                                        
[INFO] Finished at: Thu May 20 23:54:11 EEST 2010                                                                                   
[INFO] Final Memory: 15M/174M                                                                                                       
[INFO] ------------------------------------------------------------------------

Let’s verify that the RPM file indeed contains all that we asked it to:

robert@neghvar:~/workspace/rpm-package> rpm -qlp /home/robert/workspace/rpm-package/target/rpm/rpm-package/RPMS/rpm-package-0.0.1-SNAPSHOT20100520205409.noarch.rpm
/opt/app/conf                                                                                                                                                      
/opt/app/conf/app.sample.properties                                                                                                                                
/opt/app/lib                                                                                                                                                       
/opt/app/lib/antlr-2.7.6.jar                                                                                                                                       
/opt/app/lib/commons-collections-3.1.jar                                                                                                                           
/opt/app/lib/dom4j-1.6.1.jar                                                                                                                                       
/opt/app/lib/hibernate-core-3.3.2.GA.jar                                                                                                                           
/opt/app/lib/jta-1.1.jar                                                                                                                                           
/opt/app/lib/rpm-package-0.0.1-SNAPSHOT.jar                                                                                                                        
/opt/app/lib/slf4j-api-1.5.8.jar                                                                                                                                   
/opt/app/lib/xml-apis-1.0.b2.jar                                                                                                                                   
/opt/app/logs

We have reached our goal of building an RPM with our applications dependencies, classes and configuration files. By using the rpm-maven-plugin we have managed to keep using Maven as our build tool interface, and also reused the information available in the POM file for declaring dependencies.

The complete source code for this article is available at http://github.com/rombert/Maven-Recipe–RPM-Package . If you have any suggestions or corrections, please comment. Or better yet, fork me.

Maven Recipe: Building an aggregate jar

Maven builds follow the “one project, one artifact” rule. This means that although it’s possible to build more than one artifact from a Maven project,  it’s not a good idea. It also means that while it’s also possible to build one artifact from multiple projects, it’s not entirely straightforward, and that’s what this post is about.

When refactoring a monolithic build into a modular one, often the downstream consumers are not prepared to consume multiple artifacts, so the build still needs to create a single jar with all the classes. This type of change is what I like to call build refactoring, similar to code refactoring. If code refactoring deals in restructuring an existing body of code, build refactoring aims to restructure an existing build.

Assuming that we have split a large project into multiple modules:

This high-complexity project used to contain two classes, but since they were unrelated we decided to split each into its own module.

When running mvn package, each module creates its individual jar:

robert@neghvar:~/workspace/aggregate-jar> mvn clean package                                                             
[INFO] Scanning for projects...  
(snip...)
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] Maven Recipe: Aggregate jar ........................... SUCCESS [1.641s]
[INFO] Maven Recipe: Aggregate jar - first module ............ SUCCESS [2.066s]
[INFO] Maven Recipe: Aggregate jar - second module ........... SUCCESS [0.697s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 seconds
[INFO] Finished at: Sat May 15 00:59:01 EEST 2010
[INFO] Final Memory: 23M/251M
[INFO] ------------------------------------------------------------------------

Inspecting the resulting jar files confirms this assertion:

robert@neghvar:~/workspace/aggregate-jar> find -name \*.jar
./aggregate-first-module/target/aggregate-first-module-1.0.0-SNAPSHOT.jar
./aggregate-second-module/target/aggregate-second-module-1.0.0-SNAPSHOT.jar

Of course, we can always use a custom shell command, or an ant file, or even the maven-antrun-plugin to combine the two jars. While easy to do correctly and even in a cross-platform manner,  the downside is that we lose the simplicity and modularity of a pure Maven build.

Following the “one project, one artifact” rule, it becomes clear that the solution is to add another project, which generates the distribution jar. We will add a new aggregate-dist module to the build, which will use the maven-assembly-plugin to combine the all resulting classes into a single jar. The module section therefore becomes

While the assembly plugin is well known and used, there are a few considerations which must be observed when aggregating the results of a multi-module build.

The distribution module should list the modules to assemble as dependencies:

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>aggregate-first-module</artifactId>
<version>${project.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>aggregate-second-module</artifactId>
<version>${project.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
 </dependencies>

The assembly plugin should be bound to the package lifecycle phase, and invoke the ‘single’ goal:

<plugin>

 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-assembly-plugin</artifactId>
 <version>2.2-beta-5</version>
 <executions>
 <execution>
 <id>package-all</id>
 <phase>package</phase>
 <goals>
 <goal>single</goal>
 </goals>
 <configuration>
 <descriptors>
 <descriptor>src/main/assembly/all-jar.xml</descriptor>
 </descriptors>
 </configuration>
 </execution>
 </executions>
 </plugin>

The assembly descriptor should include the unpacked non-transitive dependencies:


<assembly
 xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
 <id>all-jar</id>
 <formats>
 <format>jar</format> <!-- the result is a jar file -->
 </formats>

 <includeBaseDirectory>false</includeBaseDirectory> <!-- strip the module prefixes -->

 <dependencySets>
 <dependencySet>
 <unpack>true</unpack> <!-- unpack , then repack the jars -->
 <useTransitiveDependencies>false</useTransitiveDependencies> <!-- do not pull in any transitive dependencies -->
 </dependencySet>
 </dependencySets>
</assembly>

With this setup, we can invoke maven again:

robert@neghvar:~/workspace/aggregate-jar> mvn clean package                                                             
[INFO] Scanning for projects...  
(snip...)
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] Maven Recipe: Aggregate jar ........................... SUCCESS [1.562s]
[INFO] Maven Recipe: Aggregate jar - first module ............ SUCCESS [1.315s]
[INFO] Maven Recipe: Aggregate jar - second module ........... SUCCESS [0.286s]
[INFO] Maven Recipe: Aggregate jar - distribution ............ SUCCESS [1.312s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 seconds
[INFO] Finished at: Sat May 15 01:26:47 EEST 2010
[INFO] Final Memory: 23M/301M
[INFO] ------------------------------------------------------------------------

And indeed a third jar has been created

robert@neghvar:~/workspace/aggregate-jar> find -name \*.jar
./aggregate-first-module/target/aggregate-first-module-1.0.0-SNAPSHOT.jar
./aggregate-second-module/target/aggregate-second-module-1.0.0-SNAPSHOT.jar
./aggregate-dist/target/aggregate-dist-1.0.0-SNAPSHOT-all-jar.jar

We verify that it contains the two classes

robert@neghvar:~/workspace/aggregate-jar> jar tf aggregate-dist/target/aggregate-dist-1.0.0-SNAPSHOT-all-jar.jar | grep \.class
ro/lmn/maven/recipe/ClassOne.class
ro/lmn/maven/recipe/ClassTwo.class

At this point, we have achieved or goal. By creating a dedicated distribution module, we have isolated the packaging logic from the rest of the project, and used the maven-assembly-plugin to package the non-transitive dependencies, which we declared to be the modules to be packaged.

One indirect conclusion of this article is that, although Maven is convention-based and seemingly rigid ( or opinionated , to meet the buzzword quota per posting ), there are often idiomatic Maven solutions which solve unsual problems in an elegant way.

The complete source code for this article is available at http://github.com/rombert/Maven-Recipe—Aggregate-Jar . If you have any suggestions or corrections, please comment. Or better yet, fork me.