Creating executable jar using Maven and System dependencies

I recently came across a project setup which required me to explore little more bout maven plug-ins. The situation is characterized with:
1. Project had dependencies on 3rd party library that doesn’t already have a pom.xml file defined.
2. Project need to be packaged as an executable jar with dependencies as library which also required manifest customization.

Maven’s System dependency scope can be used to our rescue from situation described in task #1, though using the System scope is discouraged. And here I’m also going to demonstrate how using System dependencies resulted in increased implications for task #2.

All you need is to add a dependency with scope system and path to that jar as depicted below, better if you can keep it under src/main/ so that it will be available with your code and not everyone who want to build your code have to search for those 3rd party jar. However this approach will not not solve dependency resolution problem if you are going to have this project as a dependency in any other project.

		<dependency>
			<groupId>anything you wish</groupId>
			<artifactId>anything you wish</artifactId>
			<version>anything you wish</version>
			<scope>system</scope>
			<systemPath>${basedir}/src/main/lib/sonic_XA.jar</systemPath>
                        <!-- ${basedir} signifies here the project base directory. -->
		</dependency>

Now let’s talk bout task #2, defining an executable JAR file involves the following steps:
a. Define a main class in your JAR’s MANIFEST.MF file that defines the executable class.
b. Find all of the libraries on which your project depends.
c. Include these libraries in your MANIFEST.MF file so that your application classes can find them.

The maven-jar-plugin can be used to modify the contents of a default MANIFEST.MF file, a sample configuration:

	<plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-jar-plugin</artifactId>
		<version>2.3.1</version>
		<configuration>
			<archive>
				<manifest>
					<!-- adds a Class-Path element to the MANIFEST.MF file, and includes 
						all dependencies in that Class-Path -->
					<addClasspath>true</addClasspath>
					<!-- location where all dependencies will be kept -->
					<classpathPrefix>lib/</classpathPrefix>
					<!-- executable class that contains main method -->
					<mainClass>com.sushantworld.core.Loader</mainClass>
				</manifest>
			</archive>
		</configuration>
	</plugin>

But as I was using System dependencies (those must be provided by env. and that was not a case for me), and hence we needed a more customized MANIFEST.MF and INDEX.LIST files so that we can include those 3rd party jar in the classpath, hence configured the maven-jar-plugin to use your custom MANIFEST.MF and INDEX.LISTfiles, with manually added system dependencies:

	<plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-jar-plugin</artifactId>
		<version>2.3.1</version>
		<configuration>
			<useDefaultManifestFile>true</useDefaultManifestFile>
			<archive>
				<index>false</index>
				<manifest>
					<addClasspath>true</addClasspath>
					<classpathPrefix>lib/</classpathPrefix>
					<mainClass>com.sushantworld.core.Loader</mainClass>
				</manifest>
			</archive>
		</configuration>
	</plugin>

Now all you need to do use copy-dependencies goal of maven-dependency-plugin to copy your dependencies to the directory of your choice (in our case, lib directory as configured to classpathPrefix in maven-jar-plugin) under the build directory.

	<plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-dependency-plugin</artifactId>
		<version>2.1</version>
		<executions>
			<execution>
				<id>copy</id>
				<phase>install</phase>
				<goals>
					<goal>copy-dependencies</goal>
				</goals>
				<configuration>
					<outputDirectory>
						${project.build.directory}/lib
					</outputDirectory>
				</configuration>
			</execution>
		</executions>
	</plugin>

Now after successful building, you can launch the application with a simple command:

java -jar jarfilename.jar

Links:
All about jar

Note: I do not suggest use of System scope for dependencies unless provided by the environment at runtime.

Advertisements