Leveraging Java "1.6.0_24" on Windows XP, I performed some quick tests to determine if a JAR's manifest (META-INF/MANIFEST.MF) Class-Path attribute could reference a directory, thereby automatically picking up any contained classes/jars within that directory.
The result... INTERESTING..
My directory tree contents were as follows:
/test
/test/test.jar
/test/lib
/test/lib/abc.jar
the "test.jar" found in the top level "test" directory contained a single file entry:
META-INF/MANIFEST.MF
the "abc.jar" found in the "lib" directory contained a single file entry; a class named "Testing" :-
public class Testing
{
public static void main(String args[])
{
System.out.println("found me");
}
}
To prove our Testing class can be located, we set test.jar's MANIFEST.MF contents initially to:
Manifest-Version: 1.0
Class-Path: lib/abc.jar
Created-By: 1.6.0_24 (Sun Microsystems Inc.)
(Note, following the Created-By: ... line, there are two newlines.)
Invoking the following java command line, we see the Testing class was successfully triggered:
C:\test>java -cp test.jar Testing
found me
I did some additional testing with relative and absolute paths in the MANIFEST.MF, the results of which were:
works: Class-Path: ./lib/abc.jar
works: Class-Path: /C:/test/lib/abc.jar
works: Class-Path: \C:\test\lib\abc.jar
fails: Class-Path: C:\test\lib\abc.jar
Next, I altered the MANIFEST.MF contents to:
Manifest-Version: 1.0
Class-Path: lib/
Created-By: 1.6.0_24 (Sun Microsystems Inc.)
and re-issued the java command:
C:\test>java -cp test.jar Testing
Exception in thread "main" java.lang.NoClassDefFoundError: Testing
Caused by: java.lang.ClassNotFoundException: Testing
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: Testing. Program will exit.
Thus, it appeared jar files in the lib directory would not be automatically included in the classpath.
To be overly thorough, I decided to run some additional tests, the results of which were:
fails: Class-Path: lib
fails: Class-Path: lib/
fails: Class-Path: ./lib
fails: Class-Path: ./lib/
fails: Class-Path: \C:\test\lib\
fails: Class-Path: \C:\test\lib
fails: Class-Path: /C:/test/lib/
fails: Class-Path: /C:/test/lib
At this point, I was of the opinion that a directory specified as part of a manifest Class-Path attribute/directive would simply be ignored.
WRONG!
I decided to extract (and subsequently delete) abc.jar. The contents of my directory tree were thus:
/test
/test/test.jar
/test/lib
/test/lib/Testing.class
I set the MANIFEST.MF contents to:
Manifest-Version: 1.0
Class-Path: lib/
Created-By: 1.6.0_24 (Sun Microsystems Inc.)
and re-issued the java command:
C:\test>java -cp test.jar Testing
found me
SUCCESS. It had located the class. What was even more interesting came out in the subsequent tests I performed:
fails: Class-Path: lib
fails: Class-Path: ./lib
fails: Class-Path: \C:\test\lib
fails: Class-Path: /C:/test/lib
works: Class-Path: lib/
works: Class-Path: ./lib/
works: Class-Path: \C:\test\lib\
works: Class-Path: /C:/test/lib/
Hence, if an explicit directory name is provided as part of the Class-Path attribute, it must have a trailing slash in order to be recognized!
If a directory entry ends with "." or ".." no trailing slash is required. For example, if test.jar was moved to /test/lib, and had its MANIFEST.MF Class-Path set to ../.. , then Testing.class would be found if it resided in "/".
As a final test, and just to be doubly certain that relative paths specified in a MANIFEST.MF are in no way influenced by the java invoking end-user's working directory, I ran the following:
(with MANIFEST.MF Class-Path set to lib/ and lib containing Testing.class)
C:\>java -cp test\test.jar Testing
found me
Thanks ... This was helpful. I ran into this issue from another angle.
ReplyDeleteI had in my MANIFEST.MF:
Class-Path: ./somejar.jar ./some_directory
and this loaded somejar.jar as well as classes found under some_directory. Note that some_directory does not need the traling slash.
but when I changed to:
Class-Path: ./some_directory
The classes under some_directory no longer loaded.
Finally, with help from your blog post:
Class-Path: ./some_directory/
Works like a charm! So, it seems the trailing slash is definitely needed when a directory is specified by itself.
Thanks,
-- Craig
Thanks, I have been struggling with this all morning. Big help!
ReplyDeleteThis is very useful.
ReplyDeleteThanks!