The Artima Developer Community
Sponsored Link

Java Seminars by Bill Venners
Dynamic Extension in Java
Lecture Handout

Agenda


Dynamic Extension


Who Cares?


Class loaders




Why Customize Loading?


Name Spaces


Duplicate Names


Names and Definitions


Why Name Spaces?


How Do Name Spaces Work?

// In file dynaext/ex1/Rodent.java
public class Rodent {
    public void scurry() {
        System.out.println("Rodent scurrying");
    }
}
// In file dynaext/ex1/Mouse.java
public class Mouse extends Rodent {
    public void scurry() {
        System.out.println("Mouse scurrying");
    }
}
// In file dynaext/ex1/Cat.java
public class Cat {
    public static void main(String[] args) {
        Rodent myToy = new Mouse();
        myToy.scurry();
    }
}

The Rule in Action

// In file dynaext/ex1/Cat.java
public class Cat {
    public static void main(String[] args) {
        Rodent myToy = new Mouse();
        myToy.scurry();
    }
}



// In file dynaext/ex1/Rodent.java
public class Rodent {
    public void scurry() {
        System.out.println("Rodent scurrying");
    }
}

// In file dynaext/ex1/Mouse.java
public class Mouse extends Rodent {
    public void scurry() {
        System.out.println("Mouse scurrying");
    }
}

java.lang.Class


forName() Follows the Rule

// In file dynaext/ex2/Cat.java
public class Cat {
    public static void main(String[] args)
        throws ClassNotFoundException, IllegalAccessException,
               InstantiationException {
        Class c = Class.forName(args[0]);
        Rodent myToy = (Rodent) c.newInstance();
        myToy.scurry();
    }
}

// In file dynaext/ex2/Rodent.java
public class Rodent {
    public void scurry() {
        System.out.println("Rodent scurrying");
    }
}

// In file dynaext/ex2/Mouse.java
public class Mouse extends Rodent {
    public void scurry() {
        System.out.println("Mouse scurrying");
    }
}

loadClass() Breaks the Rule

To load a type into a different name space, invoke loadClass() on a class loader object. 



// In file dynaext/ex3/Cat.java
public class Cat {
    public static void main(String[] args)
        throws ClassNotFoundException, IllegalAccessException,
               InstantiationException {
        RodentClassLoader rcl = new RodentClassLoader();
        Class c = rcl.loadClass(args[0]);
        Rodent myToy = (Rodent) c.newInstance();
        myToy.scurry();
    }
}
// In file dynaext/ex2/Rodent.java
public class Rodent {
    public void scurry() {
        System.out.println("Rodent scurrying");
    }
}
// In file dynaext/ex2/Mouse.java
public class Mouse extends Rodent {
    public void scurry() {
        System.out.println("Mouse scurrying");
    }
}
// In file RodentClassLoader.java
import java.io.*;
import java.util.Hashtable;
public class RodentClassLoader extends ClassLoader {
    public synchronized Class loadClass(String typeName,
        boolean resolveIt) throws ClassNotFoundException {

        // See if type as already been loaded by
        // this class loader
        Class result = findLoadedClass(typeName);
        if (result != null) {
            // Return an already-loaded class
            return result;
        }

        // Check with the primordial class loader
        try {
            result = super.findSystemClass(typeName);
            // Return a system class
            return result;
        }
        catch (ClassNotFoundException e) {
        }

        // Don't attempt to load a system file except
        // through the primordial class loader
        if (typeName.startsWith("java.")) {
            throw new ClassNotFoundException();
        }

        // Try to load it from subdirectory hole.
        byte typeData[] = getTypeFromHole(typeName);
        if (typeData == null) {
            throw new ClassNotFoundException();
        }

        // Parse it
        result = defineClass(typeName, typeData, 0,
            typeData.length);
        if (result == null) {
            throw new ClassFormatError();
        }

        if (resolveIt) {
            resolveClass(result);
        }

        // Return class from hole
        return result;
    }

    private byte[] getTypeFromHole(String typeName) {

        FileInputStream fis;
        String fileName = "hole" + File.separatorChar +
            typeName.replace('.', File.separatorChar)
            + ".class";

        try {
            fis = new FileInputStream(fileName);
        }
        catch (FileNotFoundException e) {
            return null;
        }

        BufferedInputStream bis =
            new BufferedInputStream(fis);
        ByteArrayOutputStream out =
            new ByteArrayOutputStream();

        try {
            int c = bis.read();
            while (c != -1) {
                out.write(c);
                c = bis.read();
            }
        }
        catch (IOException e) {
            return null;
        }
        return out.toByteArray();
    }
}

How the Name Spaces Look



// In file dynaext/ex3/Cat.java
public class Cat {
    public static void main(String[] args)
        throws ClassNotFoundException, IllegalAccessException,
               InstantiationException {
        RodentClassLoader rcl = new RodentClassLoader();
        Class c = rcl.loadClass(args[0]);
        Rodent myToy = (Rodent) c.newInstance();
        myToy.scurry();
    }
}

Talking Between Namespaces


The Greet Application

// In file dynaext/ex4/com/artima/greeter/Greeter.java
package com.artima.greeter;

public interface Greeter {
    void greet();
}

// In file dynaext/ex4/greeters/Hello.java
import com.artima.greeter.Greeter;

public class Hello implements Greeter {
    public void greet() {
        System.out.println("Hello, world!");
    }
}

// In file dynaext/ex4/Greet.java
import com.artima.greeter.*;

public class Greet {

    // Arguments to this application:
    //   args[0] - path name of directory in which class
    //             files for greeters are stored
    //   args[1], args[2], ... - class names of greeters
    //             on which to load and invoke greet().
    //
    // All greeters must implement the
    // com.artima.greeter.Greeter interface.
    //
    static public void main(String[] args) {

        if (args.length <= 1) {
            System.out.println(
                "Enter base path and greeter class names.");
            return;
        }

        GreeterClassLoader gcl =
            new GreeterClassLoader(args[0]);

        for (int i = 1; i < args.length; ++i) {
            try {
                // Load the greeter specified on the
                // command line
                Class c = gcl.loadClass(args[i]);


                // Instantiate it into a greeter object
                Object o = c.newInstance();


                // Cast the Object ref to the Greeter
                // interface type so greet() can be
                // invoked on it
                Greeter greeter = (Greeter) o;

                // Greet the world in this greeter's
                // special way
                greeter.greet();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

// In file dynaext/ex4/greeters/Salutations.java
import com.artima.greeter.Greeter;

public class Salutations implements Greeter {
    public void greet() {
        System.out.println("Salutations, orb!");
    }
}

// In file dynaext/ex4/greeters/Greetings.java
import com.artima.greeter.Greeter;

public class Greetings implements Greeter {
    public void greet() {
        System.out.println("Greetings, planet!");
    }
}

// In file dynaext/ex4/greeters/HowDoYouDo.java
import com.artima.greeter.Greeter;
public class HowDoYouDo implements Greeter {
    public void greet() {
        System.out.println("How do you do, globe!");
    }
}

// In file dynaext/ex4/greeters/Surprise.java
import com.artima.greeter.Greeter;
public class Surprise implements Greeter {
    public void greet() {

        // Choose one of four greeters pseudo-randomly and
        // invoke its greet() method.
        int choice = (int) (Math.random() * 3.99);

        Greeter g;
        switch(choice) {

        case 0:
            g = new Hello();
            g.greet();
            break;

        case 1:
            g = new Greetings();
            g.greet();
            break;

        case 2:
            g = new Salutations();
            g.greet();
            break;

        case 3:
            g = new HowDoYouDo();
            g.greet();
            break;
        }
    }
}

// In file dynaext/ex4/greeters/HiTime.java
import com.artima.greeter.Greeter;
import java.util.Date;
public class HiTime implements Greeter {
    public void greet() {
        // Date's no-arg constructor initializes itself
        // to the current date and time
        Date date = new Date();
        int hours = date.getHours();

        // Some hours: midnight, 0; noon, 12; 11PM, 23;
        if (hours >= 4 && hours <= 11) {
            System.out.println("Good morning, world!");
        }
        else if (hours >= 12 && hours <= 16) {
            System.out.println("Good afternoon, world!");
        }
        else if (hours >= 17 && hours <= 21) {
            System.out.println("Good evening, world!");
        }
        else if (hours >= 22 || hours <= 3) {
            System.out.println("Good night, world!");
        }
        else {
            // This should never happen.
            System.out.println("Hello, world!");
        }
    }
}

GreeterClassLoader

// In file dynaext/ex4/com/artima/greeter/GreeterClassLoader.java
package com.artima.greeter;
import java.io.*;
import java.util.Hashtable;

public class GreeterClassLoader extends ClassLoader {

    // basePath gives the path to which this class
    // loader appends "/.class" to get the
    // full path name of the class file to load
    private String basePath;

    public GreeterClassLoader(String basePath) {
        this.basePath = basePath;
    }

    public synchronized Class loadClass(String typeName,
        boolean resolveIt) throws ClassNotFoundException {

        // See if type has already been loaded by this
        // class loader
        Class result = findLoadedClass(typeName);

        if (result != null) {
            // Return an already-loaded class
            return result;
        }

        // Check with the primordial class loader
        try {
            result = findSystemClass(typeName);

            // Return a system class
            return result;
        }
        catch (ClassNotFoundException e) {
        }

        // Don't attempt to load a system file except
        // through the primordial class loader
        if (typeName.startsWith("java.")) {
            throw new ClassNotFoundException();
        }

        // Try to load it from the basePath directory.
        byte typeData[] = getTypeFromBasePath(typeName);
        if (typeData == null) {
            throw new ClassNotFoundException();
        }

        // Parse it
        result = defineClass(typeName, typeData, 0,
            typeData.length);

        if (result == null) {
            throw new ClassFormatError();
        }

        if (resolveIt) {
            resolveClass(result);
        }

        // Return class from basePath directory
        return result;
    }

    private byte[] getTypeFromBasePath(String typeName) {

        FileInputStream fis;
        String fileName = basePath + File.separatorChar
            + typeName.replace('.', File.separatorChar)
            + ".class";

        try {
            fis = new FileInputStream(fileName);
        }
        catch (FileNotFoundException e) {
            return null;
        }

        BufferedInputStream bis =
            new BufferedInputStream(fis);
        ByteArrayOutputStream out =
            new ByteArrayOutputStream();

        try {
            int c = bis.read();
            while (c != -1) {
                out.write(c);
                c = bis.read();
            }
        }
        catch (IOException e) {
            return null;
        }
        return out.toByteArray();
    }
}

Changes in 1.2


The Parent-Delegation Model


A 1.2 GreeterClassLoader

// On CD-ROM in file
// linking/ex7/com/artima/greeter/GreeterClassLoader.java
package com.artima.greeter;

import java.io.*;

public class GreeterClassLoader extends ClassLoader {

    // basePath gives the path to which this class
    // loader appends "/.class" to get the
    // full path name of the class file to load
    private String basePath;

    public GreeterClassLoader(String basePath) {

        this.basePath = basePath;
    }

    public GreeterClassLoader(ClassLoader parent, String basePath) {

        super(parent);
        this.basePath = basePath;
    }

    protected Class findClass(String className)
        throws ClassNotFoundException {

        byte classData[];

        // Try to load it from the basePath directory.
        classData = getTypeFromBasePath(className);
        if (classData == null) {
            throw new ClassNotFoundException();
        }

        // Parse it
        return defineClass(className, classData, 0,
            classData.length);
    }

    private byte[] getTypeFromBasePath(String typeName) {

        FileInputStream fis;
        String fileName = basePath + File.separatorChar
            + typeName.replace('.', File.separatorChar)
            + ".class";

        try {
            fis = new FileInputStream(fileName);
        }
        catch (FileNotFoundException e) {
            return null;
        }

        BufferedInputStream bis = new BufferedInputStream(fis);

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        try {
            int c = bis.read();
            while (c != -1) {
                out.write(c);
                c = bis.read();
            }
        }
        catch (IOException e) {
            return null;
        }

        return out.toByteArray();
    }
}

Unloading Types

// On CD-ROM in file linking/ex7/GreetAndForget.java
import com.artima.greeter.*;

public class GreetAndForget {

    // Arguments to this application:
    //     args[0] - path name of directory in which class files
    //               for greeters are stored
    //     args[1], args[2], ... - class names of greeters to load
    //               and invoke the greet() method on.
    //
    // All greeters must implement the com.artima.greeter.Greeter
    // interface.
    //
    static public void main(String[] args) {

        if (args.length <= 1) {
            System.out.println(
                "Enter base path and greeter class names as args.");
            return;
        }

        for (int i = 1; i < args.length; ++i) {
            try {

                GreeterClassLoader gcl =
                    new GreeterClassLoader(args[0]);

                // Load the greeter specified on the command line
                Class c = gcl.loadClass(args[i]);

                // Instantiate it into a greeter object
                Object o = c.newInstance();

                // Cast the Object ref to the Greeter interface type
                // so greet() can be invoked on it
                Greeter greeter = (Greeter) o;

                // Greet the world in this greeter's special way
                greeter.greet();

                // Forget the class loader object, Class
                // instance, and greeter object
                gcl = null;
                c = null;
                o = null;
                greeter = null;

                // At this point, the types loaded through the
                // GreeterClassLoader object created at the top of
                // this for loop are unreferenced and can be unloaded
                // by the virtual machine.
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
The reachability of the Class instances for Surprise and HowDoYouDo.


Design Insights


Sponsored Links

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use