JoinClassLoader - A Java class loader to combine multiple class loaders

Sometimes it's necessary to combine multiple class loaders into a new one.

For example, when using custom form components in NetBeans with custom property editors, the property editor class cannot access the NetBeans specific classes, because NetBeans loads the bean and the property editor through a separate isolated class loader. There are two possible solutions for this problem:

The JoinClassLoader class below may be used to combine class loaders.


// Copyright 2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
// www.source-code.biz, www.inventec.ch/chdh
//
// This module is multi-licensed and may be used under the terms
// of any of the following licenses:
//
//  LGPL, GNU Lesser General Public License, V2 or later, http://www.gnu.org/licenses/lgpl.html
//  AL, Apache License, V2.0 or later, http://www.apache.org/licenses
//
// Please contact the author if you need another license.
// This module is provided "as is", without warranties of any kind.

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.SecureClassLoader;
import java.util.Enumeration;
import java.util.Vector;

/**
* A class loader that combines multiple class loaders into one.<br>
* The classes loaded by this class loader are associated with this class loader,
* i.e. Class.getClassLoader() points to this class loader.
*/
public class JoinClassLoader extends ClassLoader {

private ClassLoader[] delegateClassLoaders;

public JoinClassLoader (ClassLoader parent, ClassLoader... delegateClassLoaders) {
   super(parent);
   this.delegateClassLoaders = delegateClassLoaders; }

protected Class<?> findClass (String name) throws ClassNotFoundException {
   // It would be easier to call the loadClass() methods of the delegateClassLoaders
   // here, but we have to load the class from the byte code ourselves, because we
   // need it to be associated with our class loader.
   String path = name.replace('.', '/') + ".class";
   URL url = findResource(path);
   if (url == null) {
      throw new ClassNotFoundException(name); }
   ByteBuffer byteCode;
   try {
      byteCode = loadResource(url); }
    catch (IOException e) {
      throw new ClassNotFoundException(name, e); }
   return defineClass(name, byteCode, null); }

private ByteBuffer loadResource (URL url) throws IOException {
   InputStream stream = null;
   try {
      stream = url.openStream();
      int initialBufferCapacity = Math.min(0x40000, stream.available() + 1);
      if (initialBufferCapacity <= 2) {
         initialBufferCapacity = 0x10000; }
       else {
         initialBufferCapacity = Math.max(initialBufferCapacity, 0x200); }
      ByteBuffer buf = ByteBuffer.allocate(initialBufferCapacity);
      while (true) {
         if (!buf.hasRemaining()) {
            ByteBuffer newBuf = ByteBuffer.allocate(2*buf.capacity());
            buf.flip();
            newBuf.put(buf);
            buf = newBuf; }
         int len = stream.read(buf.array(), buf.position(), buf.remaining());
         if (len <= 0) {
            break; }
         buf.position(buf.position()+len); }
      buf.flip();
      return buf; }
    finally {
      if (stream != null) {
         stream.close(); }}}

protected URL findResource (String name) {
   for (ClassLoader delegate : delegateClassLoaders) {
      URL resource = delegate.getResource(name);
      if (resource != null) {
         return resource; }}
   return null; }

protected Enumeration<URL> findResources (String name) throws IOException {
   Vector<URL> vector = new Vector<URL>();
   for (ClassLoader delegate : delegateClassLoaders) {
      Enumeration<URL> enumeration = delegate.getResources(name);
      while (enumeration.hasMoreElements()) {
         vector.add(enumeration.nextElement()); }}
   return vector.elements(); }

} // end class JoinClassLoader

Author: Christian d'Heureuse (www.source-code.biz, www.inventec.ch/chdh)
Index