Search this blog ...

Tuesday, March 29, 2011

JAR : MANIFEST.MF Class-Path referencing a directory

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

3 comments:

  1. Thanks ... This was helpful. I ran into this issue from another angle.

    I 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

    ReplyDelete
  2. Thanks, I have been struggling with this all morning. Big help!

    ReplyDelete