15.2. GENERIC INTERCEPTIONOne of the main challenges in the design of JMangler was to hook into the class loading system in a way that ensures interception of all application class files without compromising portability. In this section, we first review related approaches in order to explain why this is an issue. Then we present the solution used by JMangler. The class loading architecture and the resulting interception options are illustrated in Figure 15-1. Figure 15-1. Four ways of intercepting classes at load time: Use of a custom classloader, extension of the class java.lang.ClassLoader, use of a custom JVM implementation, and use of a custom I/O DLL. Only the second option provides portable generic interception.15.2.1. Java's Class Loader ArchitectureJava's class loading mechanism [20, 21, 36] is implemented partly in the JVM, which contains the native implementation of the bootstrap class loader, and partly in the Java APIs, which contain the pure Java class java.lang.ClassLoader and subclasses thereof (see Figure 15-1). The bootstrap class loader is responsible for loading system classes (that is, all classes that are part of the Java Development Kit and of standard extensions). The class ClassLoader is the common superclass of all classloaders for application-specific classes. Programmers can customize the class loading system by writing their own subclasses of ClassLoader. Custom class loaders are used for two reasons:
15.2.2. Class-Loader Dependent InterceptionThe common way of intercepting class files at load time is by writing a custom subclass of ClassLoader. For instance, BCEL, Javassist, and JOIE include custom classloaders. The applicability of this approach is limited by the namespace mechanism. Loading a class with one class loader automatically excludes processing the same copy of that class with another classloader. Therefore, classloader-dependent interception is not possible in applications that have their own custom class loader(s). Common uses of custom classloaders include application servers, distributed applications, and mobile code. 15.2.3. JVM-Dependent InterceptionClassloader-independent interception is achieved by Keller and Hölzle's Binary Component Adaptation (BCA) [27, 28, 29]. This was the first approach that enabled modifying compiled Java classes at load time. However, BCA is implemented as a customized version of the Java Virtual Machine of JDK 1.1 for Solaris. It is therefore JVM-dependent. 15.2.4. Platform-Dependent InterceptionDuncan and Hölzle [18] propose a non-intrusive alternative to BCA, library-based load time adaptation. They avoid having to customize the JVM by supplying a modified version of the dynamically linked standard C library. To make their approach work with all JVMs on a given platform, they modify each file routine to check for class files. Unfortunately, library-based adaptation requires a custom DLL to be provided for every operating system. Therefore, it is platform-specific. 15.2.5. Portable Generic InterceptionIn contrast to the previous approaches, JMangler provides a portable and generic interception facility. JMangler achieves this by providing a modified version of the final method defineClass() in the class java.lang.ClassLoader. Because the modified behavior is enforced for every subclass of ClassLoader, JMangler is activated whenever an application-specific class is loaded. This way, interception of all loaded application classes is possible, independently of a custom JVM or a platform-specific DLL. The limitation of this approach compared to BCA and DLL-based load-time adaptation is that JMangler cannot transform system classes. Every version of JMangler (1 through 3) is an alternative implementation scheme for this same basic idea. Version 1 provides the modification statically and enforces its utilization by prepending it to Java's BootstrapClassPath when starting the JVM. Version 2 does not modify java.lang.ClassLoader. However, it achieves the same effect by intercepting all instantiations of concrete subclasses of java.lang.ClassLoader and by replacing them with instantiations of adapted versions these classes. Analysis and replacement of calls as well as creation of adapted version of classloaders is done at load time. Versions 3 and higher use the "HotSwap" API for Java[3] [15, 16] to replace java.lang.ClassLoader with a version created at runtime. Discussion of further implementation issues, including the tradeoff between the different schemes, is outside the scope of this paper.
|