Archived

This topic is now archived and is closed to further replies.

Krumble

[java] Java Plugin System

Recommended Posts

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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;
}

}

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites