Name: jl125535 Date: 03/06/2002
FULL PRODUCT VERSION :
java version "1.4.0-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-rc-b91)
Java HotSpot(TM) Client VM (build 1.4.0-rc-b91, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]
ADDITIONAL OPERATING SYSTEMS :
Any O/S that supports the Java platform.
A DESCRIPTION OF THE PROBLEM :
Executable JAR files are a good and relatively simple way
of deploying Java applications. When the JAR file contains
a "Main-Class:" attribute, it becomes possible to launch
the application by double-clicking on the file's icon (if
supported by the client OS) or from the command line, as
java -jar some_app.jar
or, where there are multiple entry-points (not just one
java -cp some_app.jar com.mycompany.MyMainClass
However, there is a limitation with this approach. If the
application contained within the JAR file depends upon
other external JAR files, there is no straightforward way
of bundling these files within the "main" JAR file.
According to the JAR specifications, there is a Class-Path:
attribute that can be specified in JAR manifest files.
However, such paths can only be resolved relative to the
URL from which the JAR file was loaded.
For example, if the JAR file was loaded
from "C:\java\apps\appli.jar", and the Class-Path:
references "lib/other.jar", the class loader will look
in "C:\java\apps\lib\" for "other.jar". It won't look at
the JAR file entry "lib/other.jar". This example
uses "file://" URLs, but the same sort of approach also
applies to HTTP URLs, etc.
As far as I aware, and based on my own attempts, there's no
way to specify a Class-Path that makes the class loader
look in embedded jar files. See the preceeding paragraph
for a description.
I suggest perhaps allow the Class-Path attribute to
recognise by some path prefix or special URL protocol
("jar://", "self://"?) that the path refers to the inside
of the JAR file. Alternatively, a new attribute could be
added, such as "Jar-Internal-Class-Path:", specifically for
finding classes (and, for that matter, any other resources
that can be retrieved via getResource()). I prefer the
second approach personally, as it avoids protocol
conflicts, and is easier to understand and resolve. This
should be recursive (embedded JARs should be recursively
search for other embedded JARs).
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a JAR file, "test.jar", containing some classes.
2. Create another class that refers to classes contained
within "test.jar". This should ideally be an executable
class, i.e.: it should contain a "main()" method.
3. Create another JAR file, "main.jar", including the class
created in step 2 and the JAR file from step 1. Set the
Main-Class: attribute to refer to the class from step 2.
4. Try running the "main.jar", from a location that does
not contain the other jar file (from step 1) or class file
(from step 2); if these files are in the same folder
as "main.jar" when you run it, then classes can be loaded
from the current folder -- it won't need to look in the jar
file (you may therefore wish to place "main.jar" in some
other arbitrary folder before running it). It should
complain that the class from step 2 cannot be found.
5. Try step 3 with various different settings for the Class-
Path: attribute in the META-INF/MANIFEST.MF file.
EXPECTED VERSUS ACTUAL BEHAVIOR :
I would like the classes from the embedded JAR file to
become accessible. Instead, I get error messages as shown
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.NoClassDefFoundError: B
This bug can be reproduced always.
CUSTOMER WORKAROUND :
Ask the end-user to manually install the required external
libraries himself, either as an "extension" for the JVM or
in a specific location. This is not desirable, as it makes
the deployment process more complicated; depending on the
type of end user, this may be quite offputting (non-
Deploy the application as an archive (such as a ZIP file),
and ask the user to decompress it. The main class is
decompressed into a user-specified folder, and the external
libraries are placed in the same folder or some subfolder.
This allows Class-Path: to resolve, but is an additional
step that could be eliminated by enhancing Java.
Furthermore, this requires that the user has the
appropriate decompression software.
Possibly writing a custom classloader... if possible, this
is a relatively time-consuming and complicated approach for
the developer (not so much writing the classloader, as in
understanding class visibility etc between different
classloaders, which can often be confusing -- and it would
be nice to maintain a high-level relatively abstract
approach to software development in Java, instead of
forcing developers to worry about "plumbing").
Additionally, this may require granting certain security
permissions (not sure about that though). This may be one
means of implementing the enhancement, but I would prefer
that this is integrated into the standard APIs and class-
loading behaviour, and is transparent for the developer.
Decompress all external JAR, then merge the contents of
these JAR files into the main JAR file. If the "reverse
domain name" convention for classes is respected, this
should work for class files from different JAR files, but
may cause conflicts for resources such
as "conf/gui.conf", "i18n/text_en.properties", "images/splas
h.png" in some cases where common names are used.
Furthermore, there may be conflicts between different
manifest files and it becomes difficult to include signed
or sealed code correctly.
(Review ID: 139046)