Search code examples
javaclassloader

Custom Class Loader in java different behaviour


I am working on a project which requires dynamic Loading of a class from a file system, I googled and found out that I need to use custom ClassLoader. I have implemented my own class loader which is working fine when I run it on console, the problem I when I try to deploy the application on the server it results in ClassNotFoundException.

The problem is the class which I am trying to load contain some references to another class which is already loaded by the application but it is trying to load the reference from the same location.

public class HandlerClassLoader extends ClassLoader {

static Logger log = Logger.getLogger(HandlerClassLoader.class.getName());
URLClassLoader ucl;
private ProcessDefinition processDefinition = null;
boolean flag = false;

public URL[] loadJars() throws MalformedURLException {

    PropertiesFiles propFiles = new PropertiesFiles();

    File f = new File(propFiles.getValue("jarslocation"));

    log.debug("Loading JARS files from " + f.getAbsolutePath());

    List<URL> urls = new ArrayList<URL>();

    String filesName[] = f.list();

    for (String jars : filesName)
        if (jars.endsWith("jar")) {
            urls.add(new URL("file:///"
                    + propFiles.getValue("jarslocation") + "/" + jars));
        }

    URL[] array = urls.toArray(new URL[urls.size()]);

    return array;
}

public HandlerClassLoader() {
    super(Thread.currentThread().getContextClassLoader());
    log.debug("Called to the " + this.getClass().getName()
            + " No Parameter Constructor");
    try {
        ucl = new URLClassLoader(loadJars());

    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}

public HandlerClassLoader(ClassLoader parent,
        ProcessDefinition processDefinition) {
    super(parent);
    log.debug("Called to the " + this.getClass().getName()
            + " Parameterized Constructor");
    try {
        ucl = new URLClassLoader(loadJars());
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
    this.processDefinition = processDefinition;
}

public Class<?> findClass(String className) throws ClassNotFoundException {
    log.debug("findClass method of " + this.getClass().getName()
            + " is called with className : " + className);

    return ucl.loadClass(className);
}

I think the delegation principle is not working or maybe that server must have a different implementation of classloader.


Solution

  • Most likely, you do not delegate to the correct parent. I assume that picking up Thread.currentThread().getContextClassLoader() results in using the wrong class loader on the application server while this is simply the system class loader (class path) when running from a console app.

    Therefore, you need to make sure that the parent that you are handing to your custom class loader is capable of seeing the classes you are intending to load by it.

    On a final note, implementing your own class loader is a tricky business. For example, you have not accounted for locating ressources or for defining packages. Both might be required by third-party libraries that you are using. If you really only need to load files from disk, consider using a URLClassLoader.