ava's class loading framework is powerful and flexible. It allows applications to access class libraries without linking to static "include" files. Instead, it loads archive files containing library classes and resources from designated locations, such as directories and network locations defined by the CLASSPATH
environment variable. The system resolves run-time references to classes and resources dynamically, which simplifies updates and version releases. Nevertheless, each library has its own set of dependenciesand it is up to developers and deployment personnel to make sure that their applications properly reference the correct versions. Sadly, the combination of the default class-loading system and specific dependencies can and does lead to bugs, system crashes, and worse.
This article proposes a container framework for class loading intended to resolve these issues.
The Java Classpath
Java relies on the environment property/variable, CLASSPATH
, to designate the path that the runtime uses to search for classes and other resources, as they are needed. You define the CLASSPATH
property by setting the CLASSPATH
environment variable or using the Java command-line option, -classpath
A Java runtime typically finds and loads classes in the following order:
Archives and the Classpath
- Classes in the list of bootstrap classesThese are classes that embody the Java platform, such as the classes in rt.jar.
- Classes that appear in the list of extension classesThese classes use the Extension Mechanism Framework to extend the Java platform, with archive files (.jar, .zip, etc.) located in the /lib/ext directory of the runtime environment.
- User classesThese are classes that do not use the extension mechanism architecture identified using the -classpath command-line option or the CLASSPATH environment variable.
An archive .jar or.zip
file can include a manifest file containing entries that can be used to provide archive information, set archive properties, etc. The manifest can also extend the classpath by including an entry named Class-Path
, which contains a list of archives and directories. JDK 1.3 introduced the Class-Path
manifest entry for specifying optional jars and directories that load if needed. Here's an example Class-Path
Java provides an extensible model for designating the list of locations and files from which to load classes. However, some problems can arise, such as when a different version of a library exists on the classpath than an executing class expects.
Classpath Version Conflicts
The runtime identity of a class in Java is defined by its fully-qualified name (the package name prepended to the class name, sometimes known as the FQN), all appended to the ID of the classloader that loaded the class. Thus, each instance of a class loaded by multiple classloaders is regarded as a separate entity by the Java runtime. This means that the runtime can load multiple versions of the same class at any given time. This is a very powerful and flexible feature; however, the side effects can be confusing to a developer if not used intelligently.
Imagine, if you will, that you're developing an enterprise application that accesses data from multiple sources with similar semantics, such a file system and a database. Many systems of this type expose a data-access layer with data access objects (DAOs) that abstract the similar data sources. Now, imagine that you load a new version of a database DAO with a slightly different API to meet the demands of a new feature of a DAO clientbut you still need the old DAO for other clients not ready for the new API. In typical runtime environments, the new DAO will simply replace the old version and all new instances will be created from the new version. However, if the update takes place without stopping the runtime environment (hot-loading) any already-existing instances of the old DAO will reside in memory alongside any instances of the new DAO as they are created. This is confusing at best. Even worse is the danger of a DAO client expecting to create an instance of the old version of the DAO, but actually getting
an instance of the new version with the altered API. As you can see, this can present some interesting challenges.
To ensure stability and safety, calling code must be able to designate the exact version of a class that it intends to use. You can address this by creating a class-loading, component-container model and using some simple class-loading techniques.