Thursday, March 14, 2013

Custom Class Loaders

Custom class loaders are a pretty interesting subject area.
Now why would anyone want to write their own custom class loader, the question is why not. It's always better to write your own class loader to load the classes your applications use, this way you can make sure that when your applications are un-deployed the classes you loaded will be eligible for garbage collection and the world would be a better place. Also it's just darn good practice, you can avoid the notorious class loader exceptions and linkage exceptions and most of all out of memory exceptions. Unlike some languages Java manages it's own memory, as a developer you just have to make sure you do your part by disposing objects you no longer need.

But you often take this for granted sometimes because you are just too darn lazy and sometimes because you just don't get it. Well you should, if you haven't already, read about java class loaders and how they load classes.

Let's see what sort of situations demand custom class loaders.


1. To allow class loading from alternative repositories
This is the most common scenario, you can load classes from networks, different directories, over ftp etc

2. To partition code
Used mostly in Servlet engines, you can have class loaders load multiple instances of the same application in the same JVM without any issues

3.To allow unloading of classes
Class loaders maintain a cache of the classes they load you can de-reference
the class loader to unload all the classes that it loaded. This is quite handy, you can unload your classes when you are done. This way you can avoid perm gen errors.

4. To change the way bytecode is loaded
For example you can load encrypted byte-code over the wire

5. To modify the loaded byte-code
used with AOP mostly

6. Automatically verify a digital signature before executing un-trusted code

7. Transparently decrypt code with a use supplied password
    Related to point 4 above

8. To support hot deployment on an application server

9. Use different versions of the same class on the fly to do something

The possibilities are endless. Let's see when class loaders become a huge issue,


1. Say, a Developer accidently loads two different versions of the same class, there is no guarantee which version of the class will be invoked, and the funny thing is the JVM won't complain

2. An application may consist of several class loaders, they might be unrelated, you get this a lot in application servers/ web servers. Dependent libraries may require different versions of the same class, in which case you might get linkage errors and class loaders version issues.

Let's now see how you can write your own. Remember when writing your own class loader its always better to link to a super class loader. If your class loader cannot load the class the chances are that your super class loader can, and mostly because it's just good practice.


import java.io.FileInputStream;
import java.util.Hashtable;

public class customclassloader extends ClassLoader {
 private Hashtable<String, Class<?>> classes = new Hashtable<String, Class<?>>();

 public customclassloader() {
  // chaining is important here
  super(customclassloader.class.getClassLoader());
 }

 private byte getClassFromExternalLocation(String className)[] {
  System.out.println("Fetching class " + className
    + "from sampledirectory\\");
  byte result[];
  try {
   FileInputStream fileInputStream = new FileInputStream(
     "sampledirectory\\" + className + ".ext");
   result = new byte[fileInputStream.available()];
   fileInputStream.read(result);
   return result;
  } catch (Exception e) {
   System.out.println("Unable to load class " + className);
   return null;
  }
 }

 // Main function called by client to resolve class
 public synchronized Class<?> loadClass(String className, boolean resolveIt)
   throws ClassNotFoundException {
  System.out.println("Attempting to load class " + className);
  Class<?> result;
  byte classData[];

  /* Check our local cache for the class */
  result = (Class<?>) classes.get(className);
  if (result != null) {
   System.out.println("Returning a cached instance");
   return result;
  }

  /* Call super class loader */
  try {
   result = super.findSystemClass(className);
   System.out.println("Calling the super class loader");
   return result;
  } catch (ClassNotFoundException e) {
   System.out.println("Not a class parent classloader can load");
  }

  /* Try to load it from our repository */
  classData = getClassFromExternalLocation(className);
  if (classData == null) {
   throw new ClassNotFoundException();
  }

  /* Define the class */
  result = defineClass(className, classData, 0, classData.length);
  if (result == null) {
   System.out.println("Format Error");
   throw new ClassFormatError();
  }
  /* load reference classes as well if required */
  if (resolveIt) {
   resolveClass(result);
  }
  /* Store the loaded class in the cache */
  classes.put(className, result);
  return result;
 }
}

Notice how method "getClassFromExternalLocation" loads the class from a different directory. You can read more about class loaders in one of my previous posts here.

No comments:

Post a Comment