Sunday, October 31, 2010

Classpath setting in an executable jar file


Recently while working for a customer project, I found out that its quite handy to package the product (application) jar as a single executable jar file. The jar file has a MANIFEST file and all the related application class files. In the manifest file I could add the location of all third party libraries and the main class as follows :

Manifest File :

Main-Class : com.x.y.z.MainServer
Class-Path : . ../config ../lib/activation.jar ../lib/activemq-core.jar ../lib/ojdbc.jar ......

This was perfect and I could start the application by firing the following command :

Command : "java -jar application.jar"

The application has classpath dependency on the config folder (property and other configuration files for the application) and the third party jars it used. This all was now specified in the Manifest and I had the cleanest deployment.

Now, this being a databse independent product, there was a requirement not to add the driver jars (default was the ojdbc.jar) in the Manifest and hence not ship it with the product. The customers wanted to specifiy there own tested drivers for their specific databases.

Solution 1 :

The obvious solution which comes into mind is removing driver jar (ojdbc.jar in this case) from the manifest and specifying it in the class path like :

Manifest File :

Main-Class : com.x.y.z.MainServer
Class-Path : . ../config ../lib/activation.jar ../lib/activemq-core.jar ......

Command : "java -cp ../lib/ojdbc.jar -jar application.jar"
or
Command : "java -classpath ../lib/ojdbc.jar -jar application.jar"

The ojdbc.jar could be easily replaced by a custom customer specific driver.

But this is not possible and you would end up with a error such as Driver's ClassNotFoundException . Because when you execute a jar file you can not override or append the classpath present in the manifest. In a nutshell '-cp' and '-classpath' options are ignored by the vm with '-jar' option.

Solution 2 :

You remember the basics of classloaders then you still have access to the bootstrap classloader and extension classloader. These are searched when the classes are missing, through your application classloader (the others being its parent).

1. To add this additional jar to extension classpath, you have to place it in the /ext folder under your java installation. This is not an ideal behaviour for a production setup.

2. To add a jar file to the bootstrap classloader, you have to execute the application with the following command :

Command : "java --Xbootclasspath/a:../lib/ojdbc.jar -jar application.jar"

This will take into account the application classpath from the manifest inside the application.jar and also add the ojdbc.jar into the bootstrap classloader's classpath.

But again, this could potentially conflict with some third party libraries you use which could potentially in turn instrument your application classes at runtime. Personally, I would not recommend this as a healthy approach.

Solution 3 :

The Manifest file would not have a Main-Class defined but just the Class-Path as follows :

Manifest File :

Class-Path : . ../config ../lib/activation.jar ../lib/activemq-core.jar ......

The command used to run application would be :

Command : "java -cp ../lib/application.jar:../ojdbc.jar com.x.y.z.MainServer"

This works and this is what I used. Not the cleanest of all but possibly the best solution available.

The third party jars defined in the application.jar file's Manifest, are still picked up by the VM. The application.jar is in the class path and resolution of dependencies of application classes is also searched through the classpath defined in its manifest.

In a nutshell, the classpath defined in Manifests of the jar files in the application classpath is appended to the application classpath.

6 comments:

javarevisited said...

Hi,

Its worth noting that if you have two classes with same name in classpath in that case one which comes earlier in classpath will get picked up. this concept is very useful to test patch releases where you update only few classes to quickly test patch release or have added some debug print statement to troubleshoot any issue. to read more about How classpath works in Java

Thanks
Javin
Why String is immutable in Java

javarevisited said...

Hi,

Thanks for this post, I think its important for new Java developer to understand How classpath works in Java and how to use it on different scenario.

Thanks
Javin

javarevisited said...

Hi Anshuiitk,

Very informative post. you are right with -jar option both -cp and -classpath are ignored and classpath defined in manifest file gets used. I have also shared my experience in How classpath works in Java .

having said providing a start up script would have been nice idea.

Thanks
Javin
How get() method of HashMap works in JAVA

Олег said...

Thanks. Fucking class path...

NANDKISHOR WAGH said...

awsome piece of information, I had come to know about your website from my friend vinod, indore,i have read atleast seven posts of yours by now, and let me tell you, your blog gives the best and the most interesting information. This is just the kind of information that i had been looking for, i'm already your rss reader now and i would regularly watch out for the new posts, once again hats off to you! Thanks a ton once again, Regards, Difference Between Classpath and Path

Henry Nathan said...
This comment has been removed by the author.