From e9f1da85e4fc3e17bab7205001feef6ad5049116 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 7 Feb 2010 17:13:44 +0000 Subject: [PATCH] classpath for plugins --- .../src/net/i2p/router/web/PluginStarter.java | 56 +++++++++++++++++-- .../i2p/router/startup/ClientAppConfig.java | 13 ++++- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java index 006192d81..03f567f13 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -2,6 +2,9 @@ package net.i2p.router.web; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -39,6 +42,7 @@ public class PluginStarter implements Runnable { } static void startPlugins(RouterContext ctx) { + Log log = ctx.logManager().getLog(PluginStarter.class); Properties props = pluginProperties(); for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { String name = (String)iter.next(); @@ -47,9 +51,9 @@ public class PluginStarter implements Runnable { String app = name.substring(PluginStarter.PREFIX.length(), name.lastIndexOf(PluginStarter.ENABLED)); try { if (!startPlugin(ctx, app)) - System.err.println("Failed to start plugin: " + app); + log.error("Failed to start plugin: " + app); } catch (Exception e) { - System.err.println("Failed to start plugin: " + app + ' ' + e); + log.error("Failed to start plugin: " + app, e); } } } @@ -58,9 +62,10 @@ public class PluginStarter implements Runnable { /** @return true on success */ static boolean startPlugin(RouterContext ctx, String appName) throws Exception { + Log log = ctx.logManager().getLog(PluginStarter.class); File pluginDir = new File(ctx.getAppDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName); if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) { - System.err.println("Cannot start nonexistent plugin: " + appName); + log.error("Cannot start nonexistent plugin: " + appName); return false; } @@ -91,7 +96,7 @@ public class PluginStarter implements Runnable { WebAppStarter.startWebApp(ctx, server, warName, path); } } catch (IOException ioe) { - System.err.println("Error resolving '" + fileNames[i] + "' in '" + webappDir); + log.error("Error resolving '" + fileNames[i] + "' in '" + webappDir, ioe); } } } @@ -157,6 +162,15 @@ public class PluginStarter implements Runnable { argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath()); } } + if (app.classpath != null) { + String cp = new String(app.classpath); + if (cp.indexOf("$") >= 0) { + cp = cp.replace("$I2P", ctx.getBaseDir().getAbsolutePath()); + cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath()); + cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath()); + } + addToClasspath(cp, app.clientName, log); + } if (app.delay == 0) { // run this guy now LoadClientAppsJob.runClient(app.className, app.clientName, argVal, log); @@ -166,4 +180,38 @@ public class PluginStarter implements Runnable { } } } + + /** + * Perhaps there's an easy way to use Thread.setContextClassLoader() + * but I don't see how to make it magically get used for everything. + * So add this to the whole JVM's classpath. + */ + private static void addToClasspath(String classpath, String clientName, Log log) { + StringTokenizer tok = new StringTokenizer(classpath, ","); + while (tok.hasMoreTokens()) { + String elem = tok.nextToken().trim(); + File f = new File(elem); + if (!f.isAbsolute()) { + log.error("Plugin client " + clientName + " classpath element is not absolute: " + f); + continue; + } + try { + log.error("INFO: Adding plugin classpath: " + f); + addPath(f.toURI().toURL()); + } catch (Exception e) { + log.error("Plugin client " + clientName + " bad classpath element: " + f, e); + } + } + } + + /** + * http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/ + */ + public static void addPath(URL u) throws Exception { + URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); + Class urlClass = URLClassLoader.class; + Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class}); + method.setAccessible(true); + method.invoke(urlClassLoader, new Object[]{u}); + } } diff --git a/router/java/src/net/i2p/router/startup/ClientAppConfig.java b/router/java/src/net/i2p/router/startup/ClientAppConfig.java index b08e7577d..7afd0fc99 100644 --- a/router/java/src/net/i2p/router/startup/ClientAppConfig.java +++ b/router/java/src/net/i2p/router/startup/ClientAppConfig.java @@ -33,6 +33,9 @@ public class ClientAppConfig { public String args; public long delay; public boolean disabled; + /** @since 0.7.12 */ + public String classpath; + public ClientAppConfig(String cl, String client, String a, long d, boolean dis) { className = cl; clientName = client; @@ -41,6 +44,12 @@ public class ClientAppConfig { disabled = dis; } + /** @since 0.7.12 */ + public ClientAppConfig(String cl, String client, String a, long d, boolean dis, String cp) { + this(cl, client, a, d, dis); + classpath = cp; + } + public static File configFile(I2PAppContext ctx) { String clientConfigFile = ctx.getProperty(PROP_CLIENT_CONFIG_FILENAME, DEFAULT_CLIENT_CONFIG_FILENAME); File cfgFile = new File(clientConfigFile); @@ -104,6 +113,7 @@ public class ClientAppConfig { String delayStr = clientApps.getProperty(PREFIX + i + ".delay"); String onBoot = clientApps.getProperty(PREFIX + i + ".onBoot"); String disabled = clientApps.getProperty(PREFIX + i + ".startOnLoad"); + String classpath = clientApps.getProperty(PREFIX + i + ".classpath"); i++; boolean dis = disabled != null && "false".equals(disabled); @@ -115,11 +125,12 @@ public class ClientAppConfig { if (delayStr != null && !onStartup) try { delay = 1000*Integer.parseInt(delayStr); } catch (NumberFormatException nfe) {} - rv.add(new ClientAppConfig(className, clientName, args, delay, dis)); + rv.add(new ClientAppConfig(className, clientName, args, delay, dis, classpath)); } return rv; } + /** classpath not supported */ public static void writeClientAppConfig(RouterContext ctx, List apps) { File cfgFile = configFile(ctx); FileOutputStream fos = null;