Posted Sun, 30 Nov 2003
I just had to suffer through packaging a Java application in a jar file and making it run. This doesn't sound like such a big deal, but it quickly turned into one. I've been working with JAR files for quite a while, but I've been dealing in the J2EE end of things, where you deploy your JAR into a server, not to a client as an application. It wasn't a big stretch to discover that all I had to do was add a Main-Class: tag to my manifest file, but the trouble came when I tried to run it.
Lets say I have a package, net.eskeeter.jartest that has a class in it called JarTest:
public class JarTest {
public final static void main( String[] args )
System.out.println( "Hello, Readers!" );
}
}
JarTest is the entry point to my program, so I create a manifest that looks roughly like so:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.5.3
Created-By: 1.4.1_01-14 ("Apple Computer, Inc.")
Main-Class: net.eskeeter.jartest.JarTest
Name: net/eskeeter/jartest/
Sealed: true
Implementation-Vendor: eskeeter.net
Fantastic -- so I make my jar, named conveniently enough jartest.jar, and try to execute it:
java -jar jartest.jar
Fantastic! That worked great. So lets add the twist that gave me this headache; lets say JarTest.java looks like this:
import org.apache.log4j.*;
public class JarTest {
private final static Logger logger = Logger.getLogger( JarTest.class );
public final static void main( String[] args ) {
logger.info( "Hello, Readers!" );
}
}
Simple enough, all I've done is make this a toy example on how to use Apache's Log4J utility. For the sake of argument, lets say I have a file called log4j.jar (which I do) that contains the Log4J implementation (which it does...), so I want to execute my toy example, so I jar it up same as before and type the following:
java -cp .:log4j.jar -jar jartest.jar
An imaginary donut for the first reader who can tell me what will go wrong... That's right! Congrats to the young lady in the 2nd row, I get a ClassNotFoundException: org.apache.log4j.Logger! Why!? I included it in my class path... everything looks fine... but it's not.
The reason is that when you execute a jar file, the jar files Manifest must describe any external dependencies the application has; in this case log4j. So I have to update my manifest:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.5.3
Created-By: 1.4.1_01-14 ("Apple Computer, Inc.")
Main-Class: net.eskeeter.jartest.JarTest
Class-Path: log4j.jar
Name: net/eskeeter/jartest/
Sealed: true
Implementation-Vendor: eskeeter.net
Note the crucial addition of the Class-Path: attribute. If I had multiple entries on this line, they would be white-space separated; and the paths are relative to the location of the jar file. So I repackage my jartest and execute it -- note that since my jar file knows about it's external dependencies, there's no reason to be explicit about my classpath:
java -jar jartest.jar
And everything works great!
So it all seems simple and innocent enough, but I don't like the idea of having to be explicit about my external dependencies in such a fixed maner. For instance, when you download Log4J from apache, you get a jar with a name like log4j-1.2.8.jar, because by convention libraries include version numbers. I don't necessarily want to tie myself down to version 1.2.8, so I rename the library and strip it of its versioning information, log4j.jar, this is what I add to my classpath. Now, when a new version comes out, all I do is download it, strip it of it's version information, and replace my old log4j.jar with my new log4j.jar. The downside to this? I'll never know by looking in my lib/ directory which version of log4j I'm currently using.
The other downside? Lets say I want to distribute my JarTest application, to you for instance. You're a Java savvy user, and you already have Log4J installed in a location of your choice... but I don't know where that is and I can't tell my Manifest where that is. So I dictate that there will be a ./lib/ directory relative to the location of my jar (or, perhaps, bundled in my jar), and that directory will contain log4j.jar. My distribution bundle just increased in size by the size of Log4J, and has been complicated by Sun's distribution policy! That's awful! No, you being as savvy as you are, are pefectly able to delete my log4j file, and symlink to your own version of it, but lets say for instance that your less-than-savvy friend, Joe, wanted to try it out... see where I'm going with this?
It's limiting... I don't know how to fix it, but I'd like to see a solution. Perhaps Java needs a package management system similar to Perl... Perl knows what modules it has installed, and all I need to do is tell Perl I'd like to use it. It then knows where to find it, and how to load it. It would be fantastic if I could say "Java, I'd like to use Apache's Log4J... I don't care what version, but it should be greater than, say, 1.2.5", and Java would know where that library was located and go get it! Then I wouldn't have to hard code classpaths into my application's JAR bundle...
Then again, there are those who'd say limits are good -- I might NEED you to use Log4J-1.2.8 (bad example, but you know what I mean...). Still though, I find this to be too much.
Oh well, just a passing thought after hours of fighting with it. Has this been solved already? Any other ideas on how to make it better? Any better conventions for making JAR's less "fixed"? Comments welcome!
add to del.icio.us




Comments
On June 22, 2008 at 07:06 PM Dinesh Patil wrote:
Very useful things !!! Thks bro
On June 22, 2008 at 07:06 PM Dinesh Patil wrote:
Very usefule things !!! Thks bro