The CTP JarClassLoader
This article describes the startup mechanism used by CTP. Nobody needs to read this article in order to use, or even to extend, CTP. The purpose is simply to capture the thought process in case changes are contemplated in the future. This article contains highly technical, secret information which will make you extremely attractive to women, which as all programmers know, is a heavy burden to bear. Proceed at your own risk.
CTP is a pipeline processor which is designed to be extensible. The idea is that programmers can implement new PipelineStages or database interfaces and couple them into the program without having to modify CTP itself. See Extending CTP for more information. Once an extension has been created, it must be recognized by CTP in order to be available for loading. The recommended way to do that is to build the extensions into one or more JAR files and place those files in the libraries directory of the CTP installation. To support this mechanism, CTP must have a way to load classes that are not on the classpath.
A brief diversion on ClassLoaders
ClassLoaders load classes and resources. There are three standard ClassLoaders in Java:
- The primordial ClassLoader is responsible for loading the classes of the Java language. This ClassLoader is part of the JVM, and it is written in native code for the platform.
- The extension ClassLoader is responsible for loading Java extensions. These are typically classes in JARs in the JVM/lib/ext directory, although it is possible to specify additional extensions directories when starting a Java program.
- The application ClassLoader is responsible for loading classes found on the classpath.
The classpath is defined by an environment variable or an attribute in the manifest of a JAR file. It can also be specified on the command line when starting a Java program.
A ClassLoader (except for the primordial ClassLoader) is a Java class, and it must be loaded by another ClassLoader, which becomes its parent. ClassLoaders typically obey delegation rules along these lines:
- When asked to load a class (by a call to its loadClass method), the ClassLoader first checks to see if it has already loaded the class. If so, it must return exactly the same Class object for it that it returned before.
- If the requested class has not been previously loaded, the ClassLoader calls the loadClass method of its parent. If the parent returns a Class object for the requested class, the ClassLoader returns it.
- If the parent failed to load the requested class (indicated by its receiving a ClassNotFoundException from the parent), the ClassLoader searches its domain to find the class. If it finds the class, it returns it. If not, it throws a ClassNotFoundException.
The effect of the delegation mechanism is that the highest level ClassLoader which can load a class is the one that loads it. Every Class object knows the ClassLoader which loaded it. When an object attempts to instantiate another object using the new instruction, the instantiating object's ClassLoader is used to load the Class of the instantiated class, but the delegation mechanism may result in a higher level ClassLoader being the one that does the actual loading (and being the one that the loaded class knows as its ClassLoader). This little bit of arcana will be significant later.
Parenthetic note: For two Class objects to be equal, they must be the same class and they must have been loaded by the same ClassLoader.
The java.net package has a URLClassLoader which loads classes from an array of URLs pointing to JARs. (A URL pointing to a file can be obtained by calling (new File(filename)).toURL(). Like all standard ClassLoaders, it obeys the standard delegation rules.
... more to come ...