Introduction

Many frameworks support plugins for increasing flexibility. They need to be loaded during runtime making it possible to change the supported features used in application without recompiling the framework and application themselves. Loading a plug-in means loading java classes, which is done by a class loader. We want to load the plug-ins from jar files, without providing a name for every JAR. Unfortunately, the java standard API does not contain a class loader for loading classes from a list of jar files in a directory. We need to provide it ourselves. In addition, the standard class loading mechanism is designed with following idea in mind: in runtime the application accesses the known classname, that is searched for in class path and loaded if found. In our case, we want to force the framework load it extensions, without setting up the class path before applications starts, which is a little different use case.

Simple JAR class loader

The first step is loading classes from jar files. For this purpose we create a class named JarFileClassLoader which should be able to load a class from a jar file. It inherits from ClassLoader, the Java default implementation for a class loader. Loading a class from a jar file means scanning each jar file entries, decide if it is a class then load it. Because we want to be flexible about the criterion we use to select our classes, we introduce an interface ICriterion, which decides for a given class if the criterion is fulfilled or not. Using this, the JarFileClassLoader can now scan a single jar file and load every class fulfilling the injected criterion. Loading a class means scanning the caches of the parent class loaders, calling the loadClass(String) method of the parents if not found and scanning the jar file afterwards if the class was still not found. This beahviour is described in a previous post, introducing the basics of Java Class Loading.

JarFileClassLoader is capable of loading a class if it does not reference other classes. These references have to be resolved during class loading time. Let consider the example of classes A and B where A references B. If A is loaded by a class loader CL, CL is asked for loading B also. This is done by calling the loadClass(String) method of CL. If B is contained in the jar file CL loads classes from, CL will find it and return it to the JVM. If B is not contained in CL’s jar file, CL’s parents are asked to load it. Thus the class will be loaded if it is contained in the classpath where one of the java class loaders will find it. If the class is contained in another jar file in the list, but not in the class path it will not be found and the loading will end with a ClassNotFoundException.
Plugin Class Loader

Plugin class loader

To tackle this problem we introduce another class, the PluginLoader. It contains a collection of JarFileClassLoaders and inherits from ClassLoader, thus is a ClassLoader itself. The PluginLoader acts as a parent for all JarFileClassLoaders and is therefore asked every time a class has to be loaded. It will then iterate over all JarFileClassLoaders calling the loadClass(String) method on all of them to find the class it looks for. This way, classes contained in sibling class loaders are found also without violating the rule that a class loader can only see classes of itself or its parents.

Putting everything together

Now the structure of our plug-in loader component becomes clear. What we additionally need to do, is get rid of all the stack overflows we produce with the above classes. The problem here is that any JarFileClassLoader asks its parent, the PluginLoader, to load a class. This will ask all child JarFileClassLoaders to load the class including the CL which asked the PluginLoader before. The result is an infinite call stack loop – a stack overflow. Therefore we relax the concept of the java class loading. We introduce a method loadClassSimple(String) on the JarFileClassLoaders which looks just in its very own jar file. If it does not find the wanted class it just throws an Exception without asking a parent or cache-lookups. This method is called by the PluginLoader only, therefore the rest of the class loading is not affected.
Plugin Class Loader at runtime
Last but not least we consider some performance issues. If a class is searched in other jar files, those have to be scanned every time again resulting in poor performance. Therefore we introduce a cache in the JarFileClassLoader containing every class that was loaded before. Every request is served now by trying to find the class in the cache first.

Limitations

Due to the fact, that PluginLoader asks its child JarClassLoaders to load a class in particuliar order, and the first class found will be taken, the order in which JarClassLoaders are asked matters. This issue can cause problems if e.G. different versions of the same library are deployed on location scanned by the PluginLoader and should be handled with care.

References

  • [2007,book] bibtex Go to document
    G. Krueger and T. Stark, Handbuch der Java-Programmierung, , 2007.
    @book{2007_KRUEGER,
      author = {Guido Krueger and Thomas Stark},
      title = {Handbuch der Java-Programmierung},
      month = Nov, year = {2007},
      url = {http://www.javabuch.de/} publisher = {Addison-Wesley} isbn = {3-8273-2373-8}
    }
  • [,techreport] bibtex Go to document
    "Java Class Loading: The Basics,".
    @techreport{CL_BASICS author = {Brandon E. Taylor},
      title = {Java Class Loading: The Basics},
      url = {http://www.developer.com/java/other/article.php/2248831}