Using reflection to load dynamic commands
Hi,
I am writing an irc bot and I want commands to be automatically updated even while the bot is running. Here's my current situation:
Command interface:
publicinterface Command{
publicint getLevel();
public String manual();
publicvoid exec(String[] args);
}
Every command implements the Command interface and is named cmd.XXXCommand, for example: cmd.HelloCommand. The command is executed like this:
// Get an instance of the command
Class c = Class.forName("cmd." + command +"Command");
Command cmd = (Command)c.newInstance();
// Execute the command
cmd.execute(args);
Using the above approach new commands are automatically loaded and used by the running program. However an existing command is not updated while the program is running!
I guess the reason is that the JVM has a copy of the loaded classes in memory to improve performance. Is there a way to get around this?
Any help is appreciated. Thanks.
# 6
Srini_Kandula,
I used a modified version of the FileClassLoader I found from Google. There was no comment left with it so I can't remember where exactly. But here's the code:
[CODE]package quangntenemy.irc.bot;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FileClassLoader extends ClassLoader {
private String root;
public FileClassLoader(String rootDir) {
if (rootDir == null)
throw new IllegalArgumentException("Null root directory");
root = rootDir;
} // end constructor
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class c = findLoadedClass(name);
if (c == null) {
try {
c = findSystemClass(name);
} catch (Exception e) {
}
}
if (c == null) {
String filename = name.replace('.', File.separatorChar) + ".class";
try {
byte data[] = loadClassData(filename);
c = defineClass(name, data, 0, data.length);
if (c == null) throw new ClassNotFoundException(name);
} catch (IOException e) {
throw new ClassNotFoundException("Error reading file: "
+ filename);
}
}
if (resolve) resolveClass(c);
return c;
} // end loadClass
private byte[] loadClassData(String filename) throws IOException {
File f = new File(root, filename);
int size = (int) f.length();
byte buff[] = new byte[size];
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
dis.readFully(buff);
dis.close();
return buff;
} // end loadClassData
} // end class
[/CODE]
As ejp said, the key point is to "Use a new class loader for each load, and let the old one be GCd". Oh and the class should not be placed in the classpath, otherwise it has been loaded by the system ClassLoader.
_dnoyeB (Beyond? :D), yes I am starting to use a Security Manager for it as the code is growing too big to control access to system resources manually :)