/** * @return true on success * @throws just about anything, caller would be wise to catch Throwable */ public static boolean stopPlugin(RouterContext ctx, String appName) throws Exception { Log log = ctx.logManager().getLog(PluginStarter.class); File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName); if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) { log.error("Cannot stop nonexistent plugin: " + appName); return false; } // stop things in clients.config File clientConfig = new File(pluginDir, "clients.config"); if (clientConfig.exists()) { Properties props = new Properties(); DataHelper.loadProps(props, clientConfig); List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig); runClientApps(ctx, pluginDir, clients, "stop"); } // stop console webapps in console/webapps // ContextHandlerCollection server = WebAppStarter.getConsoleServer(); // if (server != null) { /* File consoleDir = new File(pluginDir, "console"); Properties props = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath()); File webappDir = new File(consoleDir, "webapps"); String fileNames[] = webappDir.list(RouterConsoleRunner.WarFilenameFilter.instance()); if (fileNames != null) { for (int i = 0; i < fileNames.length; i++) { String warName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war")); if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) { continue; } WebAppStarter.stopWebApp(server, warName); } } */ if (pluginWars.containsKey(appName)) { Iterator<String> wars = pluginWars.get(appName).iterator(); while (wars.hasNext()) { String warName = wars.next(); WebAppStarter.stopWebApp(warName); } pluginWars.get(appName).clear(); } // } // remove summary bar link Properties props = pluginProperties(ctx, appName); String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx)); if (name == null) name = ConfigClientsHelper.stripHTML(props, "consoleLinkName"); if (name != null && name.length() > 0) NavHelper.unregisterApp(name); if (log.shouldLog(Log.WARN)) log.warn("Stopping plugin: " + appName); return true; }
/** @return true on success - caller should call stopPlugin() first */ static boolean deletePlugin(RouterContext ctx, String appName) throws Exception { Log log = ctx.logManager().getLog(PluginStarter.class); File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName); if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) { log.error("Cannot delete nonexistent plugin: " + appName); return false; } // uninstall things in clients.config File clientConfig = new File(pluginDir, "clients.config"); if (clientConfig.exists()) { Properties props = new Properties(); DataHelper.loadProps(props, clientConfig); List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig); runClientApps(ctx, pluginDir, clients, "uninstall"); } // unregister themes, and switch to default if we are unregistering the current theme File dir = new File(pluginDir, "console/themes"); File[] tfiles = dir.listFiles(); if (tfiles != null) { String current = ctx.getProperty(CSSHelper.PROP_THEME_NAME); Map<String, String> changes = new HashMap<String, String>(); List<String> removes = new ArrayList<String>(); for (int i = 0; i < tfiles.length; i++) { String name = tfiles[i].getName(); if (tfiles[i].isDirectory() && (!Arrays.asList(STANDARD_THEMES).contains(tfiles[i]))) { removes.add(ConfigUIHelper.PROP_THEME_PFX + name); if (name.equals(current)) changes.put(CSSHelper.PROP_THEME_NAME, CSSHelper.DEFAULT_THEME); } } ctx.router().saveConfig(changes, removes); } boolean deleted = FileUtil.rmdir(pluginDir, false); Properties props = pluginProperties(); for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { String name = (String) iter.next(); if (name.startsWith(PREFIX + appName + '.')) iter.remove(); } if (!deleted) { // This happens on Windows when there are plugin jars in classpath // Mark it as deleted, we will try again after restart log.logAlways(Log.WARN, "Deletion of " + pluginDir + " failed, will try again at restart"); props.setProperty(PREFIX + appName + ENABLED, DELETED); } storePluginProperties(props); return true; }
/** * @return true on success * @throws just about anything, caller would be wise to catch Throwable */ 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); String minVersion = ConfigClientsHelper.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); throw new Exception(foo); } minVersion = ConfigClientsHelper.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); throw new Exception(foo); } String jVersion = LogsHelper.jettyVersion(); minVersion = ConfigClientsHelper.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); throw new Exception(foo); } String maxVersion = ConfigClientsHelper.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); 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]))) ctx.router() .setConfigSetting(ConfigUIHelper.PROP_THEME_PFX + name, tfiles[i].getAbsolutePath()); // we don't need to save } } // 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"); String fileNames[] = webappDir.list(RouterConsoleRunner.WarFilenameFilter.instance()); if (fileNames != null) { if (!pluginWars.containsKey(appName)) pluginWars.put(appName, new ConcurrentHashSet<String>()); for (int i = 0; i < fileNames.length; i++) { try { String warName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war")); // log.error("Found webapp: " + warName); // 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 = new File(webappDir, fileNames[i]).getCanonicalPath(); WebAppStarter.startWebApp(ctx, server, warName, path); pluginWars.get(appName).add(warName); } } catch (IOException ioe) { log.error("Error resolving '" + fileNames[i] + "' in '" + webappDir, ioe); } } // Check for iconfile in plugin.properties String icfile = ConfigClientsHelper.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(); if (files != null) { boolean added = false; for (int i = 0; i < files.length; i++) { File f = files[i]; if (f.getName().endsWith(".jar")) { try { addPath(f.toURI().toURL()); log.error("INFO: Adding translation plugin to classpath: " + f); added = true; } catch (Exception e) { log.error("Plugin " + appName + " bad classpath element: " + f, e); } } } if (added) Translate.clearCache(); } } // add summary bar link String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx)); if (name == null) name = ConfigClientsHelper.stripHTML(props, "consoleLinkName"); String url = ConfigClientsHelper.stripHTML(props, "consoleLinkURL"); if (name != null && url != null && name.length() > 0 && url.length() > 0) { String tip = ConfigClientsHelper.stripHTML(props, "consoleLinkTooltip_" + Messages.getLanguage(ctx)); if (tip == null) tip = ConfigClientsHelper.stripHTML(props, "consoleLinkTooltip"); NavHelper.registerApp(name, url, tip, iconfile); } return true; }