[java] Java Plugin System

Started by
8 comments, last by Krumble 20 years ago
Hello, I''ve been having some trouble creating a system that can dynamically load ''plugin'' classes into a game I''m creating. I''ve searched the forum and googled, but I could not find a solution to the error I''m having. Here''s the relivant code, followed by the exception I''m getting.


MyClassLoader.java
class MyClassLoader extends ClassLoader {

    private String dirName;

    public MyClassLoader(String dirName) {
	this.dirName = dirName;
    }

    public Class findClass(String name) throws ClassNotFoundException {
	try {
	    byte[] b = readFile(dirName+name+".class");
	    return defineClass(name, b, 0, b.length);
	} catch(Exception e) {
	    throw new ClassNotFoundException(e.toString());
	}
    }

    private static byte[] readFile(String fileName) throws IOException {
	File f = new File(fileName);
	FileInputStream fis = new FileInputStream(f);
	byte[] r = new byte[(int)f.length()];
	fis.read(r);
	fis.close();
	return r;
    }

}

MyPlugin.java
interface MyPlugin {
    public String getValue();
}

PluginOne.java
class PluginOne implements MyPlugin {
    public PluginOne() {
    }

    public String getValue() {
	return "This is plugin number 1";
    }
}

Exception in thread "main" java.lang.IllegalAccessError: class PluginOne cannot access its superinterface MyPlugin at java.lang.ClassLoader.defineClass0(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:502) at java.lang.ClassLoader.defineClass(ClassLoader.java:431) at MyClassLoader.findClass(LoadPlugin.java:47) at java.lang.ClassLoader.loadClass(ClassLoader.java:299) at java.lang.ClassLoader.loadClass(ClassLoader.java:255) at LoadPlugin.main(LoadPlugin.java:11) This occurs even if I attempt to load the Interface first using my class loader. Thanks in advance, Kevin
Kevin.
Advertisement
Have MyClassLoader call its super(); in the first line of its constructor is what I would try first... Not really sure about the usefulness of this extending classloader exercise though. Especially when you can do something like this:

public class Renderer {}public class OGLRenderer extends Renderer{}public class SoftwareRenderer extends Renderer{}public int main(String[] args)  {   Renderer myrenderer;   ...   try {       myrenderer = new OGLRenderer();   }   catch (Exceptionclassnotfound or whatever)  {       myrenderer = new SoftwareRenderer();   }   finally {handle the mess}} 


Or code to that effect.
No dice on the super call. (Isn''t it implicit anyway?)

The reason I want a system like this is similar to a Quake modification style system.
Kevin.
I have never extended class loaders myself, the standard Class.forName was always good enough for my simple plug-ins. However, aren''t class loaders in a hierarchy and you need to pass a super class loader when explicitely creating a new one? Otherwise the new class loaders cannot access classes loaded by ther loaders.
Class.forName was always good enough for me as well, extending ClassLoader is a tricky thing to get right at the best of times.

[S-Type] [V-Script]
One thing to make sure... are you loading MyPlugin before PluginOne? If not, then the VM can''t see PluginOne''s interface. Also, you need a call to resolveClass(Class c) at the end of defineClass() if you wish to instantiate that class. Let me know how you get on with these 2 suggestions

Stu
And a woman needs a man... like a fish needs a bicycle...[U2 - Tryin' to throw your arms around the world]
ServantOfGlaaki is right. You need to load all parent classes before the plugin.


First make it work, then make it fast. --Brian Kernighan

The problems of this world cannot possibly be solved by skeptics or cynics whose horizons are limited by the obvious realities. We need men and women who can dream of things that never were. - John Fitzgerald Kennedy(35th US President)

Do not interrupt your enemy when he is making a mistake. - Napolean Bonaparte
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][LWJGL Tutorials for NeHe][LWJGL Wiki][jMonkey Engine]
Ok, here''s what I''ve got so far, still the same exception.

I''m curious if the error has something to do with the fact that the PluginOne class might not have permissions to the MyPlugin interface? Since the interface should be loaded already. Regardless, explicitly loading the interface works.

Thanks again,

Kevin

import java.io.*;public class LoadPlugin {    public static void main(String args[]) {	try {	    String dirname = readString("Directory Name? ");	    String classname = readString("Class Name? ");	    MyClassLoader mcl = new MyClassLoader(dirname);	    mcl.loadClass("MyPlugin"); //Can comment this line out for the same result	    Class c = mcl.loadClass(classname);	    MyPlugin p = (MyPlugin)c.newInstance();	    System.out.println(p.getValue());	} catch (Exception e) {	    System.out.println(e.toString());	}    }    private static String readString(String prompt) { 	try {     	    StringBuffer buffer = new StringBuffer();     	    System.out.print(prompt);     	    System.out.flush(); 	    int c = System.in.read();     	    while (c!= ''\n'' && c != -1) { 		buffer.append((char) c); 		c = System.in.read();     	    }     	    return buffer.toString().trim(); 	} catch (IOException e) {     	    return ""; 	}     } }class MyClassLoader extends ClassLoader {    private String dirName;    public MyClassLoader(String dirName) {	super();	this.dirName = dirName;    }    public Class findClass(String name) throws ClassNotFoundException {	try {	    byte[] b = readFile(dirName+name+".class");	    return defineClass(name, b, 0, b.length);	} catch(Exception e) {	    throw new ClassNotFoundException(e.toString());	}    }    public Class loadClass(String name) throws ClassNotFoundException {	return loadClass(name, true); //According to docs this should call resolveClass    }    private static byte[] readFile(String fileName) throws IOException {	File f = new File(fileName);	FileInputStream fis = new FileInputStream(f);	byte[] r = new byte[(int)f.length()];	fis.read(r);	fis.close();	return r;    }}
Kevin.
When you are loading classes instead of the VM, you have to load any parent classes yourself. They are not loaded automatically.


First make it work, then make it fast. --Brian Kernighan

The problems of this world cannot possibly be solved by skeptics or cynics whose horizons are limited by the obvious realities. We need men and women who can dream of things that never were. - John Fitzgerald Kennedy(35th US President)

Do not interrupt your enemy when he is making a mistake. - Napolean Bonaparte
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][LWJGL Tutorials for NeHe][LWJGL Wiki][jMonkey Engine]
Figured it out... I didn''t make my MyPlugin interface ''public'' so the VM wasn''t allowing the newly loaded class to access it

Thanks for the help guys
Kevin.

This topic is closed to new replies.

Advertisement