use of net.i2p.router.startup.ClientAppConfig in project i2p.i2p by i2p.
the class PluginStarter method deletePlugin.
/**
* @return true on success - caller should call stopPlugin() first
* @since public since 0.9.33, was package private
*/
public 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(CSSHelper.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;
}
use of net.i2p.router.startup.ClientAppConfig 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;
}
use of net.i2p.router.startup.ClientAppConfig 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);
}
}
}
use of net.i2p.router.startup.ClientAppConfig in project i2p.i2p by i2p.
the class ConfigClientsHandler method startClient.
private void startClient(int i) {
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
if (i >= clients.size()) {
addFormError(_t("Bad client index."));
return;
}
ClientAppConfig ca = clients.get(i);
LoadClientAppsJob.runClient(ca.className, ca.clientName, LoadClientAppsJob.parseArgs(ca.args), _context, _log);
addFormNotice(_t("Client {0} started", ca.clientName));
// Give a chance for status to update
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
use of net.i2p.router.startup.ClientAppConfig in project i2p.i2p by i2p.
the class ConfigClientsHandler method stopClient.
/**
* @since Implemented in 0.9.6 using ClientAppManager
*/
private void stopClient(int i) {
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(_context);
if (i >= clients.size()) {
addFormError(_t("Bad client index."));
return;
}
ClientAppConfig ca = clients.get(i);
ClientApp clientApp = _context.routerAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
if (clientApp != null && clientApp.getState() == ClientAppState.RUNNING) {
try {
// todo parseArgs(ca.stopArgs) ?
clientApp.shutdown(null);
addFormNotice(_t("Client {0} stopped", ca.clientName));
// Give a chance for status to update
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
} catch (Throwable t) {
addFormError("Cannot stop client " + ca.className + ": " + t);
_log.error("Error stopping client " + ca.className, t);
}
} else {
addFormError("Cannot stop client " + i + ": " + ca.className);
}
}
Aggregations