简体   繁体   中英

Loading Inner Classes using a ClassLoader

I am writing a program that lets users type java code into a text area, then compile it and load it into the program as a sort of "plugin." I am currently able to compile the .java files and load the outer classes, but I am not able to load/instantiate inner classes written by users without errors. Currently this is what I use to load the outer classes, this code works and I am able to easily use the outer classes without complications whatsoever. (I did some editing for better readability, if you notice a typo tell me)

private ArrayList<String> execute(ArrayList<String> fileNames) {
    ArrayList<String> successStories = new ArrayList();
    ArrayList<Class<?>> eventHandlers = new ArrayList();
    // Load all classes first...
    for (int i = 0; i < fileNames.size(); i++) {
        Class<?> clazz = loadClassByName2(fileNames.get(i));
        if (EventHandler.class.isAssignableFrom(clazz)) {
            eventHandlers.add(clazz);
            successStories.add(fileNames.get(i));
        } else if (InterfaceInnerClass.class.isAssignableFrom(clazz)) {
            successStories.add(fileNames.get(i));
        } else {
            System.out.println(clazz.getName() + " couldn't be loaded");
        }
    }
    // Then instantiate the handlers.
    for (int i = 0; i < eventHandlers.size(); i++) {
        try {
            Object obj = eventHandlers.get(i).newInstance();
            if (obj instanceof EventHandler) {
                EventHandler EH = (EventHandler)obj;
                EH.name = EH.getClass().getSimpleName();
                CmdEvents.addEvent(EH);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return successStories;
}

public static Class<?> loadClassByName2(String name) {
    try {
        // My program sets up classpath environment variables so "./" is all that is needed as the URL
        URLClassLoader classLoader = new URLClassLoader(
                new URL[] { new File("./").toURI().toURL() });
        // Load the class from the classloader by name....
        Class<?> c = classLoader.loadClass("plugins.event_handlers." + name);
        classLoader.close();
        return c;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

The original list of file names is sent from a GUI with every .class file in the plugin directory listed. They user selects the classes they want to load, clicks a button and it sends those filenames to these methods. Within this code EventHandler is a class which you will see below, InterfaceInnerClass is simply an interface used as a tag to ensure there aren't any serious problems, and CmdEvents is a console command within my program used to manage these "plugin" classes. Like I said above, this code works fine for outer classes, the problem is when I try to load inner classes. The code for my EventHandler abstract class is as follows.

public abstract class EventHandler {
    public String name; // Don't mind this being public, I have my reasons for this.

    public abstract void execute(String input);
    public abstract boolean condition(String input);
}

The way my program works, it recieves a String from a user, then calls condition(String) and if it returns true, it calls execute(String). I wrote some test code to try out my loader as follows.

package plugins.event_handlers;
public class Test_Handler extends events.EventHandler {

    public void execute(String input) {
        System.out.println("Testing...");

        TestInner inner = new TestInner();
        inner.test();

        System.out.println("Did it work?");
    }
    public boolean condition(String input) {
        return input.contains("testinput");
    }
    public class TestInner implements events.InterfaceInnerClass {
        public TestInner() {
            System.out.println("The inner works!");
        }

        public void test() {
            System.out.println("Inner class has been tested");
        }
    }
}

I run my program, select both the Test_Handler.class and Test_Handler$TestInner.class, then click the button. When the method returns an ArrayList or successfully loaded classes, it returns BOTH the outer and inner class. However, when I run the program and pass "testinput" to the condition and execute methods, this is my output.

Testing... Exception in thread "Execute_Thread_Test_Handler" java.lang.NoClassDefFoundError: plugins/event_handlers/Test_Handler$TestInner at plugins.event_handlers.Test_Handler.execute(Test_Handler.java:11) at events.ThreadEventExecutor.run(ThreadEventExecutor.java:20) Caused by: java.lang.ClassNotFoundException: plugins.event_handlers.Test_Handler$TestInner at java.net.URLClassLoader$1.run(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) ... 2 more

What I am wanting it to print is

Testing... The inner works! Inner class has been tested Did it work?

So finally my question, how do I make the above code work? I do not want to have to make my users write their own classloaders and such to load an inner/seperate class (because not all my users will necessarily be amazing at coding) so I need to be able to reference the inner class type without the code blowing up.

Well its been a very long time since I asked but I was looking back and forth between my code and the classloader javadocs and I'm simply being dumb.

In my loadClassByName2 I call classLoader.close(); which should NOT be done if the freshly loaded class is going to load any more classes.

According to the javadocs, each class type keeps track of the classloader that loaded it. If that class type ever needs to reference an unloaded class, it calls upon its classloader to find and load it. When I closed the classloader immediately after loading the single class I made it so the classloader was not able to find/load any other classes (including local/internal classes).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM