Tracking Exceptions – Part 6 – Building an Executable Jar
If you’ve read the previous five blogs in this series, you’ll know that I’ve been building a Spring application that runs periodically to check a whole bunch of error logs for exceptions and then email you the results.
Having written the code and the tests, and being fairly certain it’ll work the next and final step is to package the whole thing up and deploy it to a production machine. The actual deployment and packaging methods will depend upon your own organisation’s processes and procedures. In this example, however, I’m going to choose the simplest way possible to create and deploy an executable JAR file. The first step was completed several weeks ago, and that’s defining our output as a JAR file in the Maven POM file, which, as you’ll probably already know, is done using the packaging element:
<packaging>jar</packaging>
It’s okay having a JAR file, but in this case there’s a further step involved: making it executable. To make a JAR file executable you need to add a MANIFEST.MF
file and place it in a directory called META-INF
. The manifest file is a file that describes the JAR file to both the JVM and human readers.
As usual, there are a couple of ways of doing this, for example if you wanted to make life difficult for yourself, you could hand-craft your own file and place it in the META-INF
directory inside the project’s src/main/resources directory. On the other hand, you could use the maven-jar
plug-in and do it automatically. To do that, you need to to add the following to your POM file.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.4</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>com.captaindebug.errortrack.Main</mainClass> <classpathPrefix>lib/</classpathPrefix> </manifest> </archive> </configuration> </plugin>
The interesting point here is the <archive><manifest> configuration element. It contains three sub-elements:
addClasspath
: this means that the plug-in will add the classpath to theMANIFEST.MF
file so that the JVM can find all the support jars when running the app.mainClass
: this tells the plug-in to add aMain-Class
attribute to theMANIFEST.MF
file, so that the JVM knows where to find the the entry point to the application. In this case it’scom.captaindebug.errortrack.Main
classpathPrefix
: this is really useful. It allows you to locate all the support jars in a different directory to the main part of the application. In this case I’ve chosen the very simple and short name oflib
.
If you run the build and then open up the resulting JAR file and extract and examine the /META-INF/MANIFEST.MF
file, you’ll find something rather like this:
Manifest-Version: 1.0 Built-By: Roger Build-Jdk: 1.7.0_09 Class-Path: lib/spring-context-3.2.7.RELEASE.jar lib/spring-aop-3.2.7.RELEASE.jar lib/aopalliance-1.0.jar lib/spring-beans-3.2.7.RELEASE.jar lib/spring-core-3.2.7.RELEASE.jar lib/spring-expression-3.2.7.RELEASE.jar lib/slf4j-api-1.6.6.jar lib/slf4j-log4j12-1.6.6.jar lib/log4j-1.2.16.jar lib/guava-13.0.1.jar lib/commons-lang3-3.1.jar lib/commons-logging-1.1.3.jar lib/spring-context-support-3.2.7.RELEASE.jar lib/spring-tx-3.2.7.RELEASE.jar lib/quartz-1.8.6.jar lib/mail-1.4.jar lib/activation-1.1.jar Created-By: Apache Maven 3.0.4 Main-Class: com.captaindebug.errortrack.Main Archiver-Version: Plexus Archiver
The last step is to marshall all the support jars into one directory, in this case the lib
directory, so that the JVM can find them when you run the application. Again, there are two ways of approaching this: the easy way and the hard way. The hard way involves manually collecting together all the JAR files as defined by the POM (both direct and transient dependencies) and copying them to an output directory. The easy way involves getting the maven-dependency-plugin
to do it for you. This involves adding the following to your POM file:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.5.1</version> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory> ${project.build.directory}/lib/ </outputDirectory> </configuration> </execution> </executions> </plugin>
In this case you’re using the copy-dependencies
goal executed in the package phase to copy all the project dependencies to the ${project.build.directory}/lib/
directory – note that the final part of the directory path, lib
, matches the classpathPrefix
setting from the previous step.
In order to make life easier, I’ve also created a small run script: runme.sh
:
#!/bin/bash echo Running Error Tracking... java -jar error-track-1.0-SNAPSHOT.jar com.captaindebug.errortrack.Main
And that’s about it. The application is just about complete. I’ve copied it to my build machine where it now monitors the Captain Debug Github sample apps and build.
I could, and indeed may, add a few more features to the app. There are a few rough edges that need knocking off the code: for example is it best to run it as a separate app, or would it be a better idea to turn it into a web app? Furthermore, wouldn’t it be a good idea to ensure that the same errors aren’t reported twice?
I may get around to thart soon… or maybe I’ll talk about something else; so much to blog about so little time…
The code for this blog is available on Github at: https://github.com/roghughe/captaindebug/tree/master/error-track. If you want to look at other blogs in this series take a look here…
- Tracking Application Exceptions With Spring
- Tracking Exceptions With Spring – Part 2 – Delegate Pattern
- Error Tracking Reports – Part 3 – Strategy and Package Private
- Tracking Exceptions – Part 4 – Spring’s Mail Sender
- Tracking Exceptions – Part 5 – Scheduling With Spring
Reference: | Tracking Exceptions – Part 6 – Building an Executable Jar from our JCG partner Roger Hughes at the Captain Debug’s Blog blog. |