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.