Search code examples
javajarclassloaderurlclassloader

Java Dynammic Class loading inside webapp


I made a java project, the project only contais this class:

package test.processor;

public abstract class Processor {

    public abstract void loadData(String objectId);
    public abstract void processData();
    public abstract void saveData(String objectId);

}

The project is exported as a jar file (processor.jar)

Then I made another project that imports processor.jar and there is a class that extends Processor:

package test.process;

import test.processor.Processor;

public class Process extends Processor{

    @Override
    public void loadData(String objectId) {
        System.out.println("LOAD DATAAAAAAAAAAAA");     
    }

    @Override
    public void processData() {
        System.out.println("PROCESS DATAAAAAAAAAAAA");
    }

    @Override
    public void saveData(String objectId) {
        System.out.println("SAVE DATAAAAAAAAAAAA");
    }

}

This project is also exported as jar (plugin.jar).

Finally, I coded something to load the plugins dynamically:

import test.processor.Processor;

public class Test {

    public void testPlugins(){

        Processor plugin = (Processor) loadJar(
                "C:\\Users\\...\\Desktop\\plugin.jar",
                "test.process.Process");
        processor.loadData("dada");

    }

    private Object loadJar(String jar, String className){

        File jarFile = new File(jar);
        Object instance = null;
        try {
            URL jarpath = jarFile.toURI().toURL();
            String jarUrl = "jar:" + jarpath + "!/";
            URL urls[] = { new URL(jarUrl) };
            URLClassLoader child = new URLClassLoader(urls);
            Class classToLoad = Class.forName(nomeClasse, true, child);
            instance = classToLoad.newInstance();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return instance;

    }


} 

If I run that code inside a main method it works correctly, once I try to run it in the server there is a problem when loading the class, I get a ClassNotFoundException (Processor). I tried putting the jar in the tomcat/lib, project/WEB-INF/lib and nothing changed.

Any idea of what Im doing wrong?


Solution

  • I didn't solve it the way I wanted, but I solved it:

    First I tried loading the process.jar manually:

    private Object loadJars(String processJar, String pluginJar, String className){
    
        File processJarFile = new File(processJar);
        File pluginJarFile = new File(pluginJar);
    
    
        Object instance = null;
        try {
    
            URL processJarPath = processJarFile.toURI().toURL();
            String processJarUrl = "jar:" + processJarPath + "!/";
    
            URL pluginJarPath = pluginJarFile.toURI().toURL();
            String pluginJarUrl = "jar:" + pluginJarPath + "!/";
    
    
            URL urls[] = { new URL(processJarUrl), new URL(pluginJarUrl) };
            URLClassLoader child = new URLClassLoader(urls);
            Class classToLoad = Class.forName(nomeClasse, true, child);
            instance = classToLoad.newInstance();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return instance;
    
    }
    

    That loads the Process class correctly, the problem happens in the testPlugins mehod, once it tries to cast to Processor (ClassCastException, can't cast Process to Processor):

    public void testPlugins(){
    
        Processor plugin = (Processor) loadJars("C:\\Users\\...\\Desktop\\processor.jar",
                "C:\\Users\\...\\Desktop\\plugin.jar",
                "test.process.Process");
        processor.loadData("dada");
    
    }
    

    Still need to read a lot about classloading but I guess the problem is that it doesn't recognize the Processor loaded from C:\Users\...\Desktop\processor.jar as the same as the Processor loaded from the webapp context or it "forgets" Process extends Processor.

    I was in a hurry so I didn't have time to research, to solve the problem I invoked the methods using reflection:

    public void modifiedTestPlugins(){
    
        Object plugin = loadJar("C:\\Users\\...\\Desktop\\processor.jar",
                "C:\\Users\\...\\Desktop\\plugin.jar",
                "test.process.Process");
    
        try {
            Method processData = findMethod(obj.getClass(), "processData");
    
            //here I invoke the processData method, it prints: PROCESS DATAAAAAAAAAAAA
            loadData.invoke(processData, new Object[]{});
        } catch (Exception e) {
            e.printStackTrace();
        }
    
    }
    
    private static Method findMethod(Class clazz, String methodName) throws Exception {
        Method[] methods = clazz.getMethods();
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals(methodName))
                return methods[i];
        }
        return null;
    }