SQL*Magic : Super*SQL
|SQL*MagicSuper*SQLCase StudiesResourcesTechnology ChoicesSQL StandardLinks

JAR files, the CLASSPATH, and the main-class Specification

Author: Davis Swan

Date: June 2, 2004

I have read several articles that cover these topics, but I still felt that a concise summary of the issues was missing. Hopefully, the following document clarifies the use of jar files, particularly those with packages and those including a main-class. It is assumed that the reader is familiar with compiling Java files and may have already created jar files.


The fundamental concept that must be understood is that a CLASSPATH is not a search directory, but an explicit list of classes that will be used by the class loader. In a very simple case where you have a single class file in the current directory, you might think that you dont need a CLASSPATH at all. Consider the following example.

 * The HelloWorld class implements an application that
 * simply displays "Hello World!" to the standard output.
class HelloWorld { 
    public static void main(String[] args) { 

        //  Display the string
        System.out.println("Hello World!"); 

Once you have compiled this source file to create the class file HelloWorld.class in the current directory, you might expect that you could type java HelloWorld and the program would execute. It will not. Instead, you will get the ever-popular NoClassDefFoundError.

That is because the class loader will prefix the class HelloWorld with NULL and add the extension .class to arrive at the resulting fully qualified class HelloWorld.class. It will then attempt to open that file as an absolute path using the OS and will not find it, because there is no assumed default path to the current directory.

Typing the command SET CLASSPATH=. (or, in Linux/Unix SET CLASSPATH . ) changes the behaviour of the class loader. It will now prefix the class name with .\  (or ./ in Linux/Unix) and add the extension to get the fully qualified class name .\HelloWorld.class   (or ./HelloWorld.class in Linux/Unix) and the OS will be able to find the file when you type java HelloWorld.

The explicit nature of the CLASSPATH explains why the following simple procedure also fails. Assume you create a jar file with the command:

jar cvf HelloWorld.jar HelloWorld.class

You then delete the HelloWorld.class file as being redundant. Now, even with your CLASSPATH set to . (the current working directory), the command java HelloWorld fails. That is because the class loader will still be trying to find the exact file .\HelloWorld.class  (./HelloWorld.class in Linux/Unix) which no longer exists. The class loader will NOT do a search of any existing jar file unless it is explicitly told to do so. You can force a search of a particular jar file by explicitly including the file in the CLASSPATH.

For example, if you now use the command SET CLASSPATH=HelloWorld.jar  (SET CLASSPATH HelloWorld.jar in Linux/Unix), the class loader will scan all the entries in the jar file. It will add the .class extension to any classes referred to by the Java application, and will look for exactly that entry in the jar file.

Note: If you used the command jar cvf HelloWorld.jar .\*.class  (jar cvf HelloWorld.jar ./*.class in Linux/Unix) you would end up with exactly the same jar file because the assumed base path and explicitly named path are identical. You can prove that to yourself by typing jar tvf HelloWorld.jar to list the contents of the jar file.


Moving Class Files to a Subdirectory

Lets now separate the location of the class file from the directory where the jar file is located.

Create a new directory classDir (pay attention to the case), recompile HelloWorld.java and move the HelloWorld.class file to classDir.

Create a jar file using the command jar cvf HelloWorld.jar classDir\*.class  (jar cvf HelloWorld.jar classDir/*.class in Linux/Unix).

Delete the file HelloWorld.class from classDir since it is now redundant.

You have now created a situation where there is no possible way to get HelloWorld to execute from any directory.

The command java HelloWorld will cause the class loader to add the current directory and extension to create the fully qualified class file .\HelloWorld.class   (./HelloWorld.class in Linux/Unix), which no longer exists.

Typing the command java classDir/HelloWorld will also fail, which may seem strange. Doing a jar tvf HelloWorld.jar will produce a listing which includes an entry for classDir/HelloWorld.class, which is exactly what you are trying to execute.

The problem here is that the class loader does not view the class as classDir/HelloWorld, but only as HelloWorld. Therefore the class loader fails because there is a discrepancy between the physical contents of the jar file (classDir/HelloWorld) and the logical name of the class (HelloWorld). This is because the simple act of moving the class file to a subdirectory has created an implicit Java package.

There is a lot of documentation on package creation available at java.sun.com. Packages are used to uniquely identify classes which otherwise might be in conflict. The most confusing thing about packages is the automatic relationship that exists between class file directories and package names.

As already mentioned, by moving the file HelloWorld.class to a subdirectory an implicit Java package was created. Therefore, the source code file HelloWorld.java must be changed by adding the package declaration in order to make it execute.

 * The HelloWorld class implements an application that
 * simply displays "Hello World!" to the standard output.
package classDir;
class HelloWorld { 
    public static void main(String[] args) { 

        //  Display the string
        System.out.println("Hello World!"); 

We can now recompile the java file, move the resultant HelloWorld.class file to the classDir subdirectory, create the jar file with the command jar cvf HelloWorld.jar classDir\*.class (jar cvf HelloWorld.jar classDir/*.class in Linux/Unix), and delete the HelloWorld.class file from the classDir subdirectory.

With the CLASSPATH still set to HelloWorld.jar, the command java classDir/HelloWorld will now work properly. This is because the jar file entry (which did not change at all when the package declaration was added) now matches the logical name of the class.

Note that if the package declaration inside HelloWorld.java was package classdir the command java HelloWorld would fail because the package declaration and the directory tree are both case sensitive, even though the Windows Command prompt is not. Note also that the creating of the implicit package depends upon your location in the directory tree when you issue the jar command. In the example above, if you moved to the classDir subdirectory, and typed jar cvf HelloWorld.jar *.class, then the jar file would be created as per the original configuration.

The bottom line is this. If you are having trouble with a jar file, use the command jar tvf someJarFile.jar to list the contents of the jar file. If there are subdirectories in the listing you get you have to make sure that the package declarations in the source files exactly match these entries, and that the correct command is used to execute the class.


A More Complex Example

The following example lists a more complex directory structure for HelloWorld.class together with the package and execution commands.

If the directory structure is com\myOrg\myApplication\ (com/myOrg/myApplication in Linux/Unix) then the jar file must be created from the parent directory of this structure, using the following command:

jar cvf HelloWorld.jar com\myOrg\myApplication\*.class Windows
jar cvf HelloWorld.jar com/myOrg/myApplication/*.class Linux/Unix

The following package declaration must be included in all the java source files:
package com.myOrg.myApplication.

Assuming that the CLASSPATH is set to HelloWorld.jar, the application will be executed using the command
java com/myOrg/myApplication/HelloWorld


Avoiding CLASSPATH Problems by Using a MANIFEST

There is a way to completely avoid all of these CLASSPATH issues if you are dealing with a single jar file. The command java jar HelloWorld.jar will always work, even with no CLASSPATH set at all, provided that the jar file contains a MANIFEST with an entry for the main-class. The MANIFEST entry must match the logical class name. Therefore, in the original example discussed previously, the file MANIFEST.MF would have the following lines.

Manifest-Version: 1.0
Created-By: 1.3.1_08 (Sun Microsystems Inc.)
Main-Class: classDir/HelloWorld

Assuming that this file is placed in the classDir subdirectory, the correct command to create the jar file would be;

jar cvfm HelloWorld.jar classDir\MANIFEST.MF classDir\*.class Windows
jar cvfm HelloWorld.jar classDir/MANIFEST.MF classDir/*.class Linux/Unix

The command java jar HelloWorld.jar will now work from any location, regardless of the current setting for CLASSPATH.

For the more complicated example described above the MANIFEST.MF would have the following entries.

Created-By: 1.3.1_08 (Sun Microsystems Inc.)
Main-Class: com/myOrg/myApplication/HelloWorld


Adding References to External Files in the MANIFEST

In some cases a Java application needs access to external files such as JDBC driver files or user preference files. It is worth noting that these external files can be referenced explicitly in the jar file, so that the application will work properly without any setting for the CLASSPATH.

Assume that the application makes a connection to an Oracle database, requiring the JDBC driver classes12.zip, and reads user preferences from a file named user.pref. The correct MANIFEST entry would be as follows;

Class-Path: . ./classes12.zip

As long as the files classes12.zip and user.pref are copied to the directory where the jar file exists then the application will work perfectly without having to use an explicit CLASSPATH. That is, a command such as java -jar myApp.jar is all that is required.

Within the Java application, the following code could be used to read the file user.pref.

InputStream userPrefStream;
BufferedReader userPrefReader;

String userPrefLine;
userPrefStream = ClassLoader.getSystemResourceAsStream("user.pref");
if ( userPrefStream != (InputStream)null )
   userPrefReader = new BufferedReader( new InputStreamReader(userPrefStream));
      while ((userPrefLine = userPrefReader.readLine()) != null )
//       More processing code.