There are a number of ways that Java supports finding and opening resources:
One-Jar supports all of these mechanisms within the context of a One-Jar file, which may
contain Jar files that have duplicate resources.
- URL findResource(String resource): returns a resource, or null if not found.
- Enumeration findResources(String resource): returns an enumeration of resource URL's which
match the given string
- URL.openConnection(): opens a connection to a resource by its URL.
One-Jar defines a predictable order for classloading:
Note: this assumes that the <one-jar> Ant task is used to construct the One-Jar. Otherwise, embedded Jar resources are loaded
in the order they appear in the "jar -tvf" listing.
- The one-jar file
Every Jar file has a required
META-INF/MANIFEST file. With more than one Jar
inside a One-Jar executable there will therefore be duplicates. Fortunately, this file is not a problem,
since a normal Java application usually has many Jar files (or other codebases) on its classpath,
meaning that it must use scanning to locate its own
MANIFEST resources. One-Jar will
have no impact in this situation.
An Example: Log4j
When multiple Jar files contain a resource with the
same name, e.g. a
log4j.properties configurator for the Log4J logging subsystem,
there may be problems. Log4j is designed to process only the first log4j.properties file which it finds on the
classpath, and ignores all others.
Consider a One-Jar which is composed of main.jar, library-a.jar, and library-b.jar. Suppose
that each of these Jar files contains a log4j.properties file. Whichever Jar file initializes
the Log4j subsystem by invoking Logger.getLogger(Class) will trigger the loading of log4j.properties.
Which one will be chosen depends on the order that the JarClassLoader encountered them
during the One-Jar bootstrap process. Fortunately this is well defined, the One-Jar order is:
main/main.jar, lib/library-a.jar, lib/library-b.jar
assuming that library-a.jar was ahead of library-b.jar in the Jar entry list (also the Ant <lib> list)
Consequently, to ensure proper initialization, just make sure that there is a
file in the
main/main.jar. Or, if main.jar is already assembled and you don't
wish to rebuild it, simply put a log4j.properties file in the top-level One-Jar like so:
Since One-Jar delegates to the parent classloader which loads the One-Jar file, any top-level resources
(or classes) will take precedence over those in the bundled Jar files.