8.3 Making Your Own Class Loader

You can't create instances of the ClassLoader class, since it is abstract and does not implement the loadClass method. You can make your own class loader by creating a class that subclasses ClassLoader and implements loadClass.

Here's a template for writing class loaders using the Java 1.0 platform:

 class TemplateClassLoader extends ClassLoader {    /** Provide a place to store the classes */    Hashtable cache = new Hashtable();    /** Override loadClass to load classes from some special       place */    protected Class loadClass(String name, boolean resolve)         throws ClassNotFoundException {         // First, look for it in the cache.         Class c = (Class) cache.get(name);         if(c == null)         {              // Second, look for it in the special place.              // If you find it there, define those bytes to be the              // class. If not, look for it as a system class.              byte bytes[] = findClass(name);              if(bytes == null)                   c = findSystemClass(name);              else                   c = defineClass(bytes, 0, bytes.length);         }         if(resolve)              resolveClass(c);         return c;    }    /** Find the class with the given name. This is entirely up     * to you. If you can't find one, return null.     */    protected byte[] findClass(String name)    {       return null;    } } 

Under the Java 1.1 and later platforms, the call to defineClass is replaced with

 c = defineClass(name, c, 0, c.length); 

The first example is acceptable under Java 1.1 but deprecated (that is, it's considered bad style, and one day it may no longer be acceptable). If the name doesn't match the name of the class found in the bytes, then a ClassFormatError is thrown.

If the class is found neither in the cache nor wherever findClass looks for it, the class loader calls findSystemClass to see whether the system can locate a definition for the class. If findSystemClass doesn't find it, it throws a ClassNotFoundException, since that was the last chance to find the class.

8.3.1 Caching Classes

It's important that a class loader return the same Class object each time it's given a particular name. If the same class were loaded more than once, it would be confusing to users who might find that two classes with identical names aren't identical. Class static constructors might be invoked multiple times, causing problems for classes that were designed to expect them to be called only once.

Under Java 1.0, it was the responsibility of the class loader to cache classes itself. This is usually done with a Hashtable, as shown in the template. However, this still leaves the possibility of confusion, since two different class loaders might each load a class into the system with the same name. Java 1.1 resolves this problem by handling the caching itself. It makes this cache available to the class loader developer through a method called findLoadedClass:

 Class findLoadedClass(String name); 

A call to findLoadedClass replaces the cache lookup. When defineClass is called, it maps the name of the class to the Class that is returned. After that, findLoadedClass always returns that Class whenever it's given the same name, no matter which class loader invokes it.

When implementing your class loader, you will have to decide whether to use the Java 1.0 interface or the 1.1 interface. The 1.0 interface is supported on virtual machines supporting Java 1.1 but not vice versa. However, using the 1.0 interface will have different results on a JVM 1.1 if the class loader tries to define a class more than once. On a JVM 1.0, it would actually load the classes multiple times, and the system would have two different classes with the same name. These classes wouldn't share static fields or use private fields or methods on the other. On 1.1 and later JVMs, however, defineClass throws an exception when it's asked to define the class a second time anywhere in the virtual machine, even if the bytes are identical.

8.3.2 Examples

Many web browsers have class loaders in them to load Java applets. An applet loader based on the TemplateClassLoader might look like this:

 import java.net.URL; class AppletLoader extends TemplateClassLoader {    /** Where the code should come from */    private URL codebase;    AppletLoader(URL codebase)    {         this.codebase = codebase;    }    public byte[] findClass(String name)    {         try {             URL code_url = new URL(codebase, name + ".class");             URLConnection connection =                code_url.openConnection();             connection.connect();             int length = connection.getContentLength();             byte[] bytes = new byte[length];             connection.getInputStream().read(bytes);             return bytes;         }         catch(IOException e) {              // Something went wrong              return null;         }    } } 

The findClass method uses the java.net.URL class to create a URL for the applet based on the code base. If the code base is http://appletsource.com/applets/page.html, then it will see the applet package/MyTestApplet at

http://appletsource.com/applets/package/MyTestApplet.class

It reads in the entire text of the applet and returns it as an array of bytes. Later, when MyTestApplet references the class package/GreenButton, the system uses the same class loader to load it.

This is only a trivial example; more sophisticated browsers have the ability to download a collection of related classes simultaneously by loading a file in Java Archive (JAR) format. A JAR file may contain many classes, and the class loader has to search the JAR file for the class it's looking for.

Another kind of class loader is exemplified by ByteArrayClassloader, which is used in conjunction with dynamic compilation. New classes are created while programs are running and given to the class loader for later use with the method store. This is the code for it:

 import java.util.*; public class ByteArrayClassLoader extends TemplateClassLoader {    /** Used for storing dynamically created classes */    private Hashtable dynamicCache = new Hashtable();    public byte[] findClass(String name)    {         return (byte[]) dynamicCache.get(name);    }    /** Associate the name with the bytecodes in data in the hash       table     * dynamicCache, for use later.     */    public void store(String name, byte[] data)    {         dynamicCache.put(name, data);    } } 

This class is used by a program that dynamically creates bytecodes. After it creates the bytecodes for a class, it calls store with those bytecodes and the name of the class.

Later, when the program wants to create an instance of the class, it calls loadClass, passing it the name of the class and true for resolve. If this is one of the classes stored with store and it hasn't been seen before, the class will get the class definition from dynamicCache.



Programming for the Java Virtual Machine
Programming for the Javaв„ў Virtual Machine
ISBN: 0201309726
EAN: 2147483647
Year: 1998
Pages: 158
Authors: Joshua Engel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net