Sunday, 8 January 2012

Java Custom ClassLoader Example

Let us discuss how to load multiple versions of a class into a single JVM using custom classloader.

Once a class is loaded into a JVM, the same class (I repeat, the same class) will not be loaded again. This leads to the question of what is meant by "the same class." Similar to the condition that an object has a specific state, an identity, and that an object is always associated with its code (class), a class loaded into a JVM also has a specific identity, which we'll look at now.

In Java, a class is identified by its fully qualified class name. The fully qualified class name consists of the package name and the class name. But a class is uniquely identified in a JVM using its fully qualified class name along with the instance of the ClassLoader that loaded the class. Thus, if a class named Cl in the package Pg is loaded by an instance kl1 of the class loader KlassLoader, the class instance of C1, i.e. C1.class is keyed in the JVM as (Cl, Pg, kl1). This means that the two class loader instances (Cl, Pg, kl1) and (Cl, Pg, kl2) are not one and the same, and classes loaded by them are also completely different and not type-compatible to each other.



Suppose we have a support class SupportClass with two different versions.We can select the required version of  SupportClass at runtime.

File System looks as given below


where

SupportInterf.java

package support;

public interface SupportInterf {
public void displayVersion();
}


SupportClass.java ( Version 01)

package support;

public class SupportClass implements SupportInterf{

public void displayVersion(){
System.out.println("Class version is Version 01");
}
}

SupportClass.java (Version 02)

package support;

public class SupportClass implements SupportInterf{

public void displayVersion(){
System.out.println("Class version is Version 02");
}
}

We have to use a custom classloader , FileSystemClassLoader to load both the versions of the SupportClass into the jvm.

FileSystemClassLoader.java

package loader;

import java.io.*;
import java.net.URL;
import java.util.Enumeration;

import support.SupportInterf;

public class FileSystemClassLoader extends ClassLoader {

    private String currentRoot = null;

    public FileSystemClassLoader() throws FileNotFoundException {
        this(FileSystemClassLoader.class.getClassLoader(), System
                .getProperties().getProperty("java.home"));
    }

    public FileSystemClassLoader(String root) throws FileNotFoundException {
        this(FileSystemClassLoader.class.getClassLoader(), root);
    }

    public FileSystemClassLoader(ClassLoader parent)
            throws FileNotFoundException {
        this(parent, System.getProperties().getProperty("java.home"));
    }

    public FileSystemClassLoader(ClassLoader parent, String root)
            throws FileNotFoundException {
        super(parent);
        File f = new File(root);
        if (f.isDirectory()) {
            currentRoot = root;
        } else {
            throw new FileNotFoundException();
        }
    }

    public void execute(String codeName) {


        Class klass = null;
        try {
            klass = findClass(codeName);
            SupportInterf task = (SupportInterf) klass.newInstance();
            task.displayVersion();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public byte[] findClassBytes(String className) {

        try {
            String pathName = currentRoot + File.separatorChar
                    + className.replace('.', File.separatorChar) + ".class";

            FileInputStream inFile = new FileInputStream(pathName);
            byte[] classBytes = new byte[inFile.available()];
            inFile.read(classBytes);

            return classBytes;
        } catch (java.io.IOException ioEx) {
            return null;
        }
    }

    public URL findResourceURL(String resourceName) {
        return null;
    }

    public java.util.Enumeration findResourcesEnum(String resourceName) {
        return null;
    }

    public String findLibraryPath(String libraryName) {
        return null;
    }

    public Class findClass(String name) throws ClassNotFoundException {

        byte[] classBytes = findClassBytes(name);
        if (classBytes == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classBytes, 0, classBytes.length);
        }
    }

    public Class findClass(String name, byte[] classBytes)
            throws ClassNotFoundException {

        if (classBytes == null) {
            throw new ClassNotFoundException("(classBytes==null)");
        } else {
            return defineClass(name, classBytes, 0, classBytes.length);
        }
    }

}

Let us create a Main class to load SupportClass using the above FileSystemClassLoader .I have used Eclipse for it.See the package structure of
Eclipse project custom_classloader .


where

Main.java

package main;

import java.io.FileNotFoundException;

import loader.FileSystemClassLoader;

public class Main {
    /**
     * @param args
     * @throws FileNotFoundException
     */
public static void main(String[] args) throws FileNotFoundException {

String v01classpath="/home/radhakrishnan/customloader/supportV01";
FileSystemClassLoader loader4v01=new    FileSystemClassLoader(v01classpath);
loader4v01.execute("support.SupportClass");


String v02classpath="/home/radhakrishnan/customloader/supportV02";
FileSystemClassLoader loader4v02=new FileSystemClassLoader(v02classpath);
loader4v02.execute("support.SupportClass");

    }

}

SupportInterf.java

package support;

public interface SupportInterf {
public void displayVersion();
}

The Output of the above program is:

Class version is Version 01
Class version is Version 02

Please visit  ClassLoader & Usage of Custom ClassLoader to study more about class loaders.

No comments:

Post a Comment