I'm working with a Java sort-of-application-server (Smartfox) which can run multiple applications ("extensions") but has a very inconvenient classpath setup to go along with it, along with issues when trying to use SLF4J.
To work around that I'd like to wrap my applications in their own classloaders. Such a containing classloader should be much like Tomcat's, in that it
- Can load classes from a directory containing JARs.
- Prefers classes from its own classpath over those from the parent
Is there a library somewhere that has such a classloader I can just "drag and drop" in my project? If not, would it be hard to create it myself? Any known pitfalls?
Since I had trouble embedding an OSGi container and it was indeed a bit overkill, I rolled my own solution. But I'll learn to use OSGi one day, in a situation where I don't need to embed the framework.
If you somehow happen to want to use this code, it's under the "do whatever you want with it" license.
public class SmartfoxExtensionContainer extends AbstractExtension {
private AbstractExtension extension;
private void initRealExtension() {
final String zone = this.getOwnerZone();
System.out.println("[SmartfoxExtensionContainer] ========= Init extension for zone " + zone + " =========");
try {
// load properties
File propFile = new File("wext/" + zone + ".properties");
System.out.println("[SmartfoxExtensionContainer] Load config from " + propFile.getCanonicalPath());
Properties props = new Properties();
final FileInputStream ins = new FileInputStream(propFile);
try {
props.load(new InputStreamReader(ins, "UTF-8"));
} finally {
try {
ins.close();
} catch (IOException e) {}
}
// construct classloader
File jarDir = new File(props.getProperty("classpath", "wext/" + zone));
System.out.println("[SmartfoxExtensionContainer] Load classes from " + jarDir.getCanonicalPath());
if (!jarDir.isDirectory()) throw new RuntimeException("That is not an existing directory");
final File[] fs = jarDir.listFiles();
URL[] urls = new URL[fs.length];
for (int f = 0; f < fs.length; f++) {
System.out.println("[SmartfoxExtensionContainer] " + fs[f].getName());
urls[f] = fs[f].toURI().toURL();
}
SelfishClassLoader cl = new SelfishClassLoader(urls, SmartfoxExtensionContainer.class.getClassLoader());
// get real extension class
String mainClass = props.getProperty("mainClass", "Extension");
System.out.println("[SmartfoxExtensionContainer] Main class: " + mainClass);
@SuppressWarnings("unchecked")
Class<? extends AbstractExtension> extClass = (Class<? extends AbstractExtension>) cl.loadClass(mainClass);
// create extension and copy settings
extension = extClass.newInstance();
extension.setOwner(this.getOwnerZone(), this.getOwnerRoom());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/* ======================= DELEGATES ======================= */
@Override
public void init() {
initRealExtension();
extension.init();
}
@Override
public void destroy() {
extension.destroy();
}
@Override
public void handleRequest(String arg0, ActionscriptObject arg1, User arg2, int arg3) {
extension.handleRequest(arg0, arg1, arg2, arg3);
}
@Override
public void handleRequest(String arg0, String[] arg1, User arg2, int arg3) {
extension.handleRequest(arg0, arg1, arg2, arg3);
}
@Override
public void handleInternalEvent(InternalEventObject arg0) {
extension.handleInternalEvent(arg0);
}
@Override
public Object handleInternalRequest(Object params) {
return extension.handleInternalRequest(params);
}
@Override
public void handleRequest(String cmd, JSONObject jso, User u, int fromRoom) {
extension.handleRequest(cmd, jso, u, fromRoom);
}
/* ======================= CUSTOM CLASSLOADER ======================= */
private static class SelfishClassLoader extends URLClassLoader {
SelfishClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
// override default behaviour: find classes in local path first, then parent
@Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// First, check if the class has already been loaded
Class<?> clz = findLoadedClass(name);
if (clz == null) {
try {
clz = findClass(name);
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from current class loader
}
if (clz == null) {
// If still not found, then invoke parent.findClass in order
// to find the class.
clz = getParent().loadClass(name);
}
}
if (resolve) {
resolveClass(clz);
}
return clz;
};
}
}
来源:https://stackoverflow.com/questions/7645707/is-there-a-tomcat-like-classloader-that-can-be-used-standalone