Search in sources :

Example 1 with ConcurrentHashSet

use of net.i2p.util.ConcurrentHashSet in project i2p.i2p by i2p.

the class PluginStarter method startPlugin.

/**
 *  @return true on success
 *  @throws Exception just about anything, caller would be wise to catch Throwable
 */
@SuppressWarnings("deprecation")
public static boolean startPlugin(RouterContext ctx, String appName) throws Exception {
    Log log = ctx.logManager().getLog(PluginStarter.class);
    File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName);
    String iconfile = null;
    if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
        log.error("Cannot start nonexistent plugin: " + appName);
        disablePlugin(appName);
        return false;
    }
    // Do we need to extract an update?
    File pluginUpdate = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName + "/app.xpi2p.zip");
    if (pluginUpdate.exists()) {
        // Compare the start time of the router with the plugin.
        if (ctx.router().getWhenStarted() > pluginUpdate.lastModified()) {
            if (!FileUtil.extractZip(pluginUpdate, pluginDir)) {
                pluginUpdate.delete();
                String foo = "Plugin '" + appName + "' failed to update! File '" + pluginUpdate + "' deleted. You may need to remove and install the plugin again.";
                log.error(foo);
                disablePlugin(appName);
                throw new Exception(foo);
            } else {
                pluginUpdate.delete();
                // Need to always log this, and  log.logAlways() did not work for me.
                System.err.println("INFO: Plugin updated: " + appName);
            }
        }
    // silently fail to update, because we have not restarted.
    }
    Properties props = pluginProperties(ctx, appName);
    // For the following, we use the exact same translated strings as in PluginUpdateRunner
    // to avoid duplication
    String minVersion = stripHTML(props, "min-i2p-version");
    if (minVersion != null && VersionComparator.comp(CoreVersion.VERSION, minVersion) < 0) {
        String foo = "Plugin " + appName + " requires I2P version " + minVersion + " or higher";
        log.error(foo);
        disablePlugin(appName);
        foo = gettext("This plugin requires I2P version {0} or higher", minVersion, ctx);
        throw new Exception(foo);
    }
    minVersion = stripHTML(props, "min-java-version");
    if (minVersion != null && VersionComparator.comp(System.getProperty("java.version"), minVersion) < 0) {
        String foo = "Plugin " + appName + " requires Java version " + minVersion + " or higher";
        log.error(foo);
        disablePlugin(appName);
        foo = gettext("This plugin requires Java version {0} or higher", minVersion, ctx);
        throw new Exception(foo);
    }
    String jVersion = RouterConsoleRunner.jettyVersion();
    minVersion = stripHTML(props, "min-jetty-version");
    if (minVersion != null && VersionComparator.comp(minVersion, jVersion) > 0) {
        String foo = "Plugin " + appName + " requires Jetty version " + minVersion + " or higher";
        log.error(foo);
        disablePlugin(appName);
        foo = gettext("Plugin requires Jetty version {0} or higher", minVersion, ctx);
        throw new Exception(foo);
    }
    String blacklistVersion = jetty9Blacklist.get(appName);
    String curVersion = stripHTML(props, "version");
    if (blacklistVersion != null && VersionComparator.comp(curVersion, blacklistVersion) <= 0) {
        String foo = "Plugin " + appName + " requires Jetty version 8.9999 or lower";
        log.error(foo);
        disablePlugin(appName);
        foo = gettext("Plugin requires Jetty version {0} or lower", "8.9999", ctx);
        throw new Exception(foo);
    }
    String maxVersion = stripHTML(props, "max-jetty-version");
    if (maxVersion != null && VersionComparator.comp(maxVersion, jVersion) < 0) {
        String foo = "Plugin " + appName + " requires Jetty version " + maxVersion + " or lower";
        log.error(foo);
        disablePlugin(appName);
        foo = gettext("Plugin requires Jetty version {0} or lower", maxVersion, ctx);
        throw new Exception(foo);
    }
    if (log.shouldLog(Log.INFO))
        log.info("Starting plugin: " + appName);
    // register themes
    File dir = new File(pluginDir, "console/themes");
    File[] tfiles = dir.listFiles();
    if (tfiles != null) {
        for (int i = 0; i < tfiles.length; i++) {
            String name = tfiles[i].getName();
            if (tfiles[i].isDirectory() && (!Arrays.asList(STANDARD_THEMES).contains(tfiles[i]))) {
                // deprecated
                ctx.router().setConfigSetting(CSSHelper.PROP_THEME_PFX + name, tfiles[i].getAbsolutePath());
            // we don't need to save
            }
        }
    }
    // handle console icons for plugins without web-resources through prop icon-code
    String fullprop = props.getProperty("icon-code");
    if (fullprop != null && fullprop.length() > 1) {
        byte[] decoded = Base64.decode(fullprop);
        if (decoded != null) {
            NavHelper.setBinary(appName, decoded);
            iconfile = "/Plugins/pluginicon?plugin=" + appName;
        } else {
            iconfile = "/themes/console/images/plugin.png";
        }
    }
    // load and start things in clients.config
    File clientConfig = new File(pluginDir, "clients.config");
    if (clientConfig.exists()) {
        Properties cprops = new Properties();
        DataHelper.loadProps(cprops, clientConfig);
        List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig);
        runClientApps(ctx, pluginDir, clients, "start");
    }
    // start console webapps in console/webapps
    ContextHandlerCollection server = WebAppStarter.getConsoleServer();
    if (server != null) {
        File consoleDir = new File(pluginDir, "console");
        Properties wprops = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath());
        File webappDir = new File(consoleDir, "webapps");
        File[] files = webappDir.listFiles(RouterConsoleRunner.WAR_FILTER);
        if (files != null) {
            if (!pluginWars.containsKey(appName))
                pluginWars.put(appName, new ConcurrentHashSet<String>());
            for (int i = 0; i < files.length; i++) {
                try {
                    String warName = files[i].getName();
                    warName = warName.substring(0, warName.lastIndexOf(".war"));
                    // check for duplicates in $I2P
                    if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) {
                        log.error("Skipping duplicate webapp " + warName + " in plugin " + appName);
                        continue;
                    }
                    String enabled = wprops.getProperty(RouterConsoleRunner.PREFIX + warName + ENABLED);
                    if (!"false".equals(enabled)) {
                        if (log.shouldLog(Log.INFO))
                            log.info("Starting webapp: " + warName);
                        String path = files[i].getCanonicalPath();
                        WebAppStarter.startWebApp(ctx, server, warName, path);
                        pluginWars.get(appName).add(warName);
                    }
                } catch (IOException ioe) {
                    log.error("Error resolving '" + files[i] + "' in '" + webappDir, ioe);
                }
            }
            // Check for iconfile in plugin.properties
            String icfile = stripHTML(props, "console-icon");
            if (icfile != null && !icfile.contains("..")) {
                StringBuilder buf = new StringBuilder(32);
                buf.append('/').append(appName);
                if (!icfile.startsWith("/"))
                    buf.append('/');
                buf.append(icfile);
                iconfile = buf.toString();
            }
        }
    } else {
        log.error("No console web server to start plugins?");
    }
    // add translation jars in console/locale
    // These will not override existing resource bundles since we are adding them
    // later in the classpath.
    File localeDir = new File(pluginDir, "console/locale");
    if (localeDir.exists() && localeDir.isDirectory()) {
        File[] files = localeDir.listFiles(new FileSuffixFilter(".jar"));
        if (files != null) {
            boolean added = false;
            for (int i = 0; i < files.length; i++) {
                File f = files[i];
                try {
                    addPath(f.toURI().toURL());
                    log.info("INFO: Adding translation plugin to classpath: " + f);
                    added = true;
                } catch (ClassCastException e) {
                    log.logAlways(Log.WARN, "Java version: " + System.getProperty("java.version") + " does not support adding classpath element: " + f + " for plugin " + appName);
                } catch (RuntimeException e) {
                    log.error("Plugin " + appName + " bad classpath element: " + f, e);
                }
            }
            if (added)
                Translate.clearCache();
        }
    }
    // add summary bar link
    String name = stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx));
    if (name == null)
        name = stripHTML(props, "consoleLinkName");
    String url = stripHTML(props, "consoleLinkURL");
    if (name != null && url != null && name.length() > 0 && url.length() > 0) {
        String tip = stripHTML(props, "consoleLinkTooltip_" + Messages.getLanguage(ctx));
        if (tip == null)
            tip = stripHTML(props, "consoleLinkTooltip");
        NavHelper.registerApp(name, url, tip, iconfile);
    }
    return true;
}
Also used : Log(net.i2p.util.Log) ContextHandlerCollection(org.eclipse.jetty.server.handler.ContextHandlerCollection) IOException(java.io.IOException) Properties(java.util.Properties) IOException(java.io.IOException) ConcurrentHashSet(net.i2p.util.ConcurrentHashSet) ClientAppConfig(net.i2p.router.startup.ClientAppConfig) FileSuffixFilter(net.i2p.util.FileSuffixFilter) File(java.io.File)

Example 2 with ConcurrentHashSet

use of net.i2p.util.ConcurrentHashSet in project i2p.i2p by i2p.

the class PluginStarter method runClientApps.

/**
 *  @param action "start" or "stop" or "uninstall"
 *  @throws Exception just about anything if an app has a delay less than zero, caller would be wise to catch Throwable
 *  If no apps have a delay less than zero, it shouldn't throw anything
 */
private static void runClientApps(RouterContext ctx, File pluginDir, List<ClientAppConfig> apps, String action) throws Exception {
    Log log = ctx.logManager().getLog(PluginStarter.class);
    // initialize pluginThreadGroup and _pendingPluginClients
    String pluginName = pluginDir.getName();
    if (!pluginThreadGroups.containsKey(pluginName))
        pluginThreadGroups.put(pluginName, new ThreadGroup(pluginName));
    ThreadGroup pluginThreadGroup = pluginThreadGroups.get(pluginName);
    if (action.equals("start"))
        _pendingPluginClients.put(pluginName, new ConcurrentHashSet<SimpleTimer2.TimedEvent>());
    for (ClientAppConfig app : apps) {
        // bypass all the logic below.
        if (action.equals("stop")) {
            String[] argVal = LoadClientAppsJob.parseArgs(app.args);
            // Do this after parsing so we don't need to worry about quoting
            for (int i = 0; i < argVal.length; i++) {
                if (argVal[i].indexOf('$') >= 0) {
                    argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                    argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                    argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
                }
            }
            ClientApp ca = ctx.routerAppManager().getClientApp(app.className, argVal);
            if (ca != null) {
                // even if (ca.getState() != ClientAppState.RUNNING), we do this, we don't want to fall thru
                try {
                    ca.shutdown(LoadClientAppsJob.parseArgs(app.stopargs));
                } catch (Throwable t) {
                    throw new Exception(t);
                }
                continue;
            }
        }
        if (action.equals("start") && app.disabled)
            continue;
        String[] argVal;
        if (action.equals("start")) {
            // start
            argVal = LoadClientAppsJob.parseArgs(app.args);
        } else {
            String args;
            if (action.equals("stop"))
                args = app.stopargs;
            else if (action.equals("uninstall"))
                args = app.uninstallargs;
            else
                throw new IllegalArgumentException("bad action");
            // args must be present
            if (args == null || args.length() <= 0)
                continue;
            argVal = LoadClientAppsJob.parseArgs(args);
        }
        // do this after parsing so we don't need to worry about quoting
        for (int i = 0; i < argVal.length; i++) {
            if (argVal[i].indexOf('$') >= 0) {
                argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
            }
        }
        ClassLoader cl = null;
        if (app.classpath != null) {
            String cp = 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());
            }
            // Old way - add for the whole JVM
            // addToClasspath(cp, app.clientName, log);
            // New way - add only for this client
            // We cache the ClassLoader we start the client with, so
            // we can reuse it for stopping and uninstalling.
            // If we don't, the client won't be able to find its
            // static members.
            String clCacheKey = pluginName + app.className + app.args;
            if (!action.equals("start"))
                cl = _clCache.get(clCacheKey);
            if (cl == null) {
                URL[] urls = classpathToURLArray(cp, app.clientName, log);
                if (urls != null) {
                    cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
                    if (action.equals("start"))
                        _clCache.put(clCacheKey, cl);
                }
            }
        }
        if (app.delay < 0 && action.equals("start")) {
            // this will throw exceptions
            LoadClientAppsJob.runClientInline(app.className, app.clientName, argVal, log, cl);
        } else if (app.delay == 0 || !action.equals("start")) {
            // quick check, will throw ClassNotFoundException on error
            LoadClientAppsJob.testClient(app.className, cl);
            // run this guy now
            LoadClientAppsJob.runClient(app.className, app.clientName, argVal, ctx, log, pluginThreadGroup, cl);
        } else {
            // If it bombs after that, then we throw the ClassNotFoundException.
            try {
                // quick check
                LoadClientAppsJob.testClient(app.className, cl);
            } catch (ClassNotFoundException ex) {
                // Under normal circumstances there will be no delay at all.
                try {
                    if (app.delay > 1) {
                        Thread.sleep(2000);
                    } else {
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException ie) {
                }
                // quick check, will throw ClassNotFoundException on error
                LoadClientAppsJob.testClient(app.className, cl);
            }
            // wait before firing it up
            SimpleTimer2.TimedEvent evt = new TrackedDelayedClient(pluginName, ctx.simpleTimer2(), ctx, app.className, app.clientName, argVal, pluginThreadGroup, cl);
            evt.schedule(app.delay);
        }
    }
}
Also used : Log(net.i2p.util.Log) ClientApp(net.i2p.app.ClientApp) IOException(java.io.IOException) URL(java.net.URL) ConcurrentHashSet(net.i2p.util.ConcurrentHashSet) URLClassLoader(java.net.URLClassLoader) ClientAppConfig(net.i2p.router.startup.ClientAppConfig) URLClassLoader(java.net.URLClassLoader) SimpleTimer2(net.i2p.util.SimpleTimer2)

Aggregations

IOException (java.io.IOException)2 ClientAppConfig (net.i2p.router.startup.ClientAppConfig)2 ConcurrentHashSet (net.i2p.util.ConcurrentHashSet)2 Log (net.i2p.util.Log)2 File (java.io.File)1 URL (java.net.URL)1 URLClassLoader (java.net.URLClassLoader)1 Properties (java.util.Properties)1 ClientApp (net.i2p.app.ClientApp)1 FileSuffixFilter (net.i2p.util.FileSuffixFilter)1 SimpleTimer2 (net.i2p.util.SimpleTimer2)1 ContextHandlerCollection (org.eclipse.jetty.server.handler.ContextHandlerCollection)1