Files
i2p.i2p/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java

846 lines
35 KiB
Java
Raw Normal View History

2010-02-07 13:32:49 +00:00
package net.i2p.router.web;
import java.io.File;
import java.io.IOException;
2010-02-07 17:13:44 +00:00
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
2010-02-26 16:58:01 +00:00
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
2010-02-07 13:32:49 +00:00
import java.util.Iterator;
import java.util.List;
import java.util.Map;
2010-02-07 13:32:49 +00:00
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
2010-02-07 13:32:49 +00:00
import net.i2p.CoreVersion;
2010-02-07 13:32:49 +00:00
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
2010-02-07 13:32:49 +00:00
import net.i2p.router.startup.ClientAppConfig;
import net.i2p.router.startup.LoadClientAppsJob;
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
import net.i2p.router.update.ConsoleUpdateManager;
import static net.i2p.update.UpdateType.*;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
2010-02-07 13:32:49 +00:00
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
2010-02-07 18:18:31 +00:00
import net.i2p.util.Translate;
import net.i2p.util.VersionComparator;
2010-02-07 13:32:49 +00:00
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
2010-02-07 13:32:49 +00:00
/**
2010-02-26 16:58:01 +00:00
* Start/stop/delete plugins that are already installed
* Get properties of installed plugins
* Get or change settings in plugins.config
2010-02-07 13:32:49 +00:00
*
* @since 0.7.12
* @author zzz
*/
public class PluginStarter implements Runnable {
protected RouterContext _context;
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
public static final String PREFIX = "plugin.";
public static final String ENABLED = ".startOnLoad";
public static final String PLUGIN_DIR = "plugins";
2010-02-26 16:58:01 +00:00
private static final String[] STANDARD_WEBAPPS = { "i2psnark", "i2ptunnel", "susidns",
"susimail", "addressbook", "routerconsole" };
private static final String[] STANDARD_THEMES = { "images", "light", "dark", "classic",
"midnight" };
private static Map<String, ThreadGroup> pluginThreadGroups = new ConcurrentHashMap<String, ThreadGroup>(); // one thread group per plugin (map key=plugin name)
private static Map<String, Collection<SimpleTimer2.TimedEvent>> _pendingPluginClients =
new ConcurrentHashMap<String, Collection<SimpleTimer2.TimedEvent>>();
private static Map<String, ClassLoader> _clCache = new ConcurrentHashMap<String, ClassLoader>();
private static Map<String, Collection<String>> pluginWars = new ConcurrentHashMap<String, Collection<String>>();
2010-02-07 13:32:49 +00:00
public PluginStarter(RouterContext ctx) {
_context = ctx;
}
static boolean pluginsEnabled(I2PAppContext ctx) {
return ctx.getBooleanPropertyDefaultTrue("router.enablePlugins");
}
2010-02-07 13:32:49 +00:00
public void run() {
if (_context.getBooleanPropertyDefaultTrue("plugins.autoUpdate") &&
!NewsHelper.isUpdateInProgress()) {
String prev = _context.getProperty("router.previousVersion");
if (prev != null &&
VersionComparator.comp(RouterVersion.VERSION, prev) > 0) {
updateAll(_context, true);
}
}
2010-02-07 13:32:49 +00:00
startPlugins(_context);
}
/**
* threaded
* @since 0.8.13
*/
static void updateAll(RouterContext ctx) {
Thread t = new I2PAppThread(new PluginUpdater(ctx), "PluginUpdater", true);
t.start();
}
/**
* thread
* @since 0.8.13
*/
private static class PluginUpdater implements Runnable {
private final RouterContext _ctx;
public PluginUpdater(RouterContext ctx) {
_ctx = ctx;
}
public void run() {
updateAll(_ctx, false);
}
}
/**
* inline
* @since 0.8.13
*/
private static void updateAll(RouterContext ctx, boolean delay) {
List<String> plugins = getPlugins();
Map<String, String> toUpdate = new HashMap<String, String>();
for (String appName : plugins) {
Properties props = pluginProperties(ctx, appName);
String url = props.getProperty("updateURL");
if (url != null)
toUpdate.put(appName, url);
}
if (toUpdate.isEmpty())
return;
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
ConsoleUpdateManager mgr = UpdateHandler.updateManager(ctx);
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
if (mgr == null)
return;
if (mgr.isUpdateInProgress())
return;
if (delay) {
// wait for proxy
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
mgr.update(TYPE_DUMMY, 3*60*1000);
mgr.notifyProgress(null, Messages.getString("Checking for plugin updates", ctx));
int loop = 0;
do {
try {
Thread.sleep(5*1000);
} catch (InterruptedException ie) {}
if (loop++ > 40) break;
} while (mgr.isUpdateInProgress(TYPE_DUMMY));
}
Log log = ctx.logManager().getLog(PluginStarter.class);
int updated = 0;
for (Map.Entry<String, String> entry : toUpdate.entrySet()) {
String appName = entry.getKey();
if (log.shouldLog(Log.WARN))
log.warn("Checking for update plugin: " + appName);
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
// blocking
if (mgr.checkAvailable(PLUGIN, appName, 60*1000) == null) {
if (log.shouldLog(Log.WARN))
log.warn("No update available for plugin: " + appName);
continue;
}
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
if (log.shouldLog(Log.WARN))
log.warn("Updating plugin: " + appName);
// non-blocking
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
mgr.update(PLUGIN, appName, 30*60*1000);
int loop = 0;
do {
// only wait for 4 minutes, then we will
// keep going
try {
Thread.sleep(5*1000);
} catch (InterruptedException ie) {}
if (loop++ > 48) break;
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
} while (mgr.isUpdateInProgress(PLUGIN, appName));
if (mgr.getUpdateAvailable(PLUGIN, appName) != null)
updated++;
}
if (updated > 0)
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
mgr.notifyComplete(null, ngettext("1 plugin updated", "{0} plugins updated", updated, ctx));
else
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
mgr.notifyComplete(null, Messages.getString("Plugin update check complete", ctx));
}
/** this shouldn't throw anything */
2010-02-07 13:32:49 +00:00
static void startPlugins(RouterContext ctx) {
2010-02-07 17:13:44 +00:00
Log log = ctx.logManager().getLog(PluginStarter.class);
2010-02-07 13:32:49 +00:00
Properties props = pluginProperties();
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) {
if (Boolean.parseBoolean(props.getProperty(name))) {
String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED));
// plugins could have been started after update
if (isPluginRunning(app, ctx))
continue;
2010-02-07 13:32:49 +00:00
try {
if (!startPlugin(ctx, app))
2010-02-07 17:13:44 +00:00
log.error("Failed to start plugin: " + app);
} catch (Throwable e) {
2010-02-07 17:13:44 +00:00
log.error("Failed to start plugin: " + app, e);
2010-02-07 13:32:49 +00:00
}
}
}
}
}
/**
* @return true on success
* @throws just about anything, caller would be wise to catch Throwable
*/
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
public static boolean startPlugin(RouterContext ctx, String appName) throws Exception {
2010-02-07 17:13:44 +00:00
Log log = ctx.logManager().getLog(PluginStarter.class);
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName);
2010-02-07 13:32:49 +00:00
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
2010-02-07 17:13:44 +00:00
log.error("Cannot start nonexistent plugin: " + appName);
disablePlugin(appName);
2010-02-07 13:32:49 +00:00
return false;
}
2012-03-13 09:49:15 +00:00
// Do we need to extract an update?
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
File pluginUpdate = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName + "/app.xpi2p.zip" );
2012-03-13 09:49:15 +00:00
if(pluginUpdate.exists()) {
// Compare the start time of the router with the plugin.
2012-03-15 00:39:11 +00:00
if(ctx.router().getWhenStarted() > pluginUpdate.lastModified()) {
2012-03-13 09:49:15 +00:00
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);
2012-03-13 09:49:15 +00:00
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);
2010-02-07 13:32:49 +00:00
2010-02-26 16:58:01 +00:00
// 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
}
}
2010-02-07 13:32:49 +00:00
// 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);
2010-02-07 13:32:49 +00:00
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig);
runClientApps(ctx, pluginDir, clients, "start");
2010-02-07 13:32:49 +00:00
}
// start console webapps in console/webapps
ContextHandlerCollection server = WebAppStarter.getConsoleServer();
2010-02-07 13:32:49 +00:00
if (server != null) {
File consoleDir = new File(pluginDir, "console");
Properties wprops = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath());
2010-02-08 22:19:59 +00:00
File webappDir = new File(consoleDir, "webapps");
2010-02-07 13:32:49 +00:00
String fileNames[] = webappDir.list(RouterConsoleRunner.WarFilenameFilter.instance());
if (fileNames != null) {
if(!pluginWars.containsKey(appName))
pluginWars.put(appName, new ConcurrentHashSet<String>());
2010-02-07 13:32:49 +00:00
for (int i = 0; i < fileNames.length; i++) {
try {
String warName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war"));
//log.error("Found webapp: " + warName);
2010-02-08 22:19:59 +00:00
// check for duplicates in $I2P
2010-02-26 16:58:01 +00:00
if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) {
2010-02-08 22:19:59 +00:00
log.error("Skipping duplicate webapp " + warName + " in plugin " + appName);
continue;
}
String enabled = wprops.getProperty(RouterConsoleRunner.PREFIX + warName + ENABLED);
2010-02-07 13:32:49 +00:00
if (! "false".equals(enabled)) {
if (log.shouldLog(Log.INFO))
log.info("Starting webapp: " + warName);
2010-02-07 13:32:49 +00:00
String path = new File(webappDir, fileNames[i]).getCanonicalPath();
WebAppStarter.startWebApp(ctx, server, warName, path);
pluginWars.get(appName).add(warName);
2010-02-07 13:32:49 +00:00
}
} catch (IOException ioe) {
2010-02-07 17:13:44 +00:00
log.error("Error resolving '" + fileNames[i] + "' in '" + webappDir, ioe);
2010-02-07 13:32:49 +00:00
}
}
}
} else {
log.error("No console web server to start plugins?");
2010-02-07 13:32:49 +00:00
}
// add translation jars in console/locale
2010-02-07 18:18:31 +00:00
// 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();
}
}
2010-02-07 13:32:49 +00:00
// add summary bar link
2010-02-08 23:28:09 +00:00
String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx));
2010-02-08 16:15:23 +00:00
if (name == null)
2010-02-08 23:28:09 +00:00
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");
if (tip != null)
NavHelper.registerApp(name, url, tip);
else
NavHelper.registerApp(name, url);
}
2010-02-07 13:32:49 +00:00
return true;
}
/**
* @return true on success
* @throws just about anything, caller would be wise to catch Throwable
*/
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
public static boolean stopPlugin(RouterContext ctx, String appName) throws Exception {
2010-02-08 20:37:49 +00:00
Log log = ctx.logManager().getLog(PluginStarter.class);
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName);
2010-02-08 20:37:49 +00:00
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");
2010-02-08 20:37:49 +00:00
}
// stop console webapps in console/webapps
//ContextHandlerCollection server = WebAppStarter.getConsoleServer();
//if (server != null) {
/*
2010-02-08 22:19:59 +00:00
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"));
2010-02-26 16:58:01 +00:00
if (Arrays.asList(STANDARD_WEBAPPS).contains(warName)) {
2010-02-08 22:19:59 +00:00
continue;
}
WebAppStarter.stopWebApp(server, warName);
}
}
*/
2010-11-27 13:52:57 +00:00
if(pluginWars.containsKey(appName)) {
Iterator <String> wars = pluginWars.get(appName).iterator();
while (wars.hasNext()) {
String warName = wars.next();
WebAppStarter.stopWebApp(warName);
2010-11-27 13:52:57 +00:00
}
pluginWars.get(appName).clear();
}
//}
2010-02-08 22:19:59 +00:00
2010-02-08 20:37:49 +00:00
// remove summary bar link
Properties props = pluginProperties(ctx, appName);
2010-02-08 23:28:09 +00:00
String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx));
2010-02-08 20:37:49 +00:00
if (name == null)
2010-02-08 23:28:09 +00:00
name = ConfigClientsHelper.stripHTML(props, "consoleLinkName");
2010-02-08 20:37:49 +00:00
if (name != null && name.length() > 0)
NavHelper.unregisterApp(name);
if (log.shouldLog(Log.WARN))
log.warn("Stopping plugin: " + appName);
2010-02-08 20:37:49 +00:00
return true;
}
2010-02-26 16:58:01 +00:00
/** @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);
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName);
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
2010-02-26 16:58:01 +00:00
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");
}
2010-02-26 16:58:01 +00:00
// 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>();
2010-02-26 16:58:01 +00:00
for (int i = 0; i < tfiles.length; i++) {
String name = tfiles[i].getName();
2010-04-10 15:29:16 +00:00
if (tfiles[i].isDirectory() && (!Arrays.asList(STANDARD_THEMES).contains(tfiles[i]))) {
removes.add(ConfigUIHelper.PROP_THEME_PFX + name);
2010-02-26 16:58:01 +00:00
if (name.equals(current))
changes.put(CSSHelper.PROP_THEME_NAME, CSSHelper.DEFAULT_THEME);
2010-02-26 16:58:01 +00:00
}
}
ctx.router().saveConfig(changes, removes);
2010-02-26 16:58:01 +00:00
}
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();
}
storePluginProperties(props);
return true;
}
2010-02-08 16:15:23 +00:00
/** plugin.config */
public static Properties pluginProperties(I2PAppContext ctx, String appName) {
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
File cfgFile = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName + '/' + "plugin.config");
2010-02-08 16:15:23 +00:00
Properties rv = new Properties();
try {
DataHelper.loadProps(rv, cfgFile);
} catch (IOException ioe) {}
return rv;
}
/**
* plugins.config
* this auto-adds a propery for every dir in the plugin directory
*/
2010-02-07 13:32:49 +00:00
public static Properties pluginProperties() {
File dir = I2PAppContext.getGlobalContext().getConfigDir();
Properties rv = new Properties();
File cfgFile = new File(dir, "plugins.config");
try {
DataHelper.loadProps(rv, cfgFile);
} catch (IOException ioe) {}
List<String> names = getPlugins();
for (String name : names) {
String prop = PREFIX + name + ENABLED;
if (rv.getProperty(prop) == null)
rv.setProperty(prop, "true");
}
return rv;
}
/**
* Is the plugin enabled in plugins.config?
* Default true
*
* @since 0.8.13
*/
public static boolean isPluginEnabled(String appName) {
Properties props = pluginProperties();
String prop = PREFIX + appName + ENABLED;
return Boolean.parseBoolean(props.getProperty(prop, "true"));
}
/**
* Disable in plugins.config
*
* @since 0.8.13
*/
public static void disablePlugin(String appName) {
Properties props = pluginProperties();
String prop = PREFIX + appName + ENABLED;
if (Boolean.parseBoolean(props.getProperty(prop, "true"))) {
props.setProperty(prop, "false");
storePluginProperties(props);
}
}
/**
* all installed plugins whether enabled or not
*/
public static List<String> getPlugins() {
List<String> rv = new ArrayList<String>();
Big refactor of the router console update subsystem, in preparation for implementing out-of-console updaters like i2psnark. - Add new update interfaces in net.i2p.update - All update implementations moved to routerconsole update/ - Implement an UpdateManager that registers with the RouterContext - UpdateManager handles multiple types of things to update (router, plugins, news, ...) and methods of updating (HTTP, ...) - UpdateManager maintains list of installed, downloaded, and available versions of everything - Define Updaters that can check for a new version and/or download an item - Individual Updaters register with the UpdateManager obtained from I2PAppContext, identifying the type of update item and update method they can handle. - Updaters need only core libs, no router.jar or routerconsole access required. - All checks and updates are initiated via the UpdateManager. - All status on checks and updates in-progress or completed are obtained from the UpdateManager. No more use of System properties to broadcast update state. - All update and checker tasks are intantiated on demand and threaded; no more static references left over. - Split out the Runners and Checkers from the Handlers and make the inheritance more sane. - No more permanent NewsFetcher thread; run on the SimpleScheduler queue and thread a checker task only to fetch the news. - No more static NewsFetcher instance in routerconsole. All helper methods that are still required are moved to NewsHelper. The UpdateManager implements the policy for when to check and download. All requests go through the UpdateManager. For each update type, there's several parts: - The xxxUpdateHandler implements the Updater - The xxxUpdateChecker implements the UpdateTask for checking - The xxxUpdateRunner implements the UpdateTask for downloading New and moved classes: web/ update/ ---- ------- new ConsoleUpdateManager.java new PluginUpdateChecker.java from PluginUpdateChecker PluginUpdateChecker -> PluginUpdateHandler.java PluginUpdateHandler.java -> PluginUpdateRunner new UnsignedUpdateHandler.java UnsignedUpdateHandler -> UnsignedUpdateRunner.java new UnsignedUpdateChecker from NewsFetcher UpdateHandler.java remains new UpdateHandler.java new UpdateRunner.java from UpdateHandler move NewsHandler from NewsFetcher new NewsFetcher new NewsTimerTask new DummyHandler Initial checkin. Unfinished, untested, unpolished.
2012-06-18 22:09:45 +00:00
File pluginDir = new File(I2PAppContext.getGlobalContext().getConfigDir(), PLUGIN_DIR);
2010-02-07 13:32:49 +00:00
File[] files = pluginDir.listFiles();
if (files == null)
return rv;
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory())
rv.add(files[i].getName());
}
Collections.sort(rv); // ensure the list is in sorted order.
return rv;
}
/**
* The signing keys from all the plugins
* @return Map of key to keyname
* Last one wins if a dup (installer should prevent dups)
*/
public static Map<String, String> getPluginKeys(I2PAppContext ctx) {
Map<String, String> rv = new HashMap<String, String>();
List<String> names = getPlugins();
for (String name : names) {
Properties props = pluginProperties(ctx, name);
String pubkey = props.getProperty("key");
String signer = props.getProperty("signer");
if (pubkey != null && signer != null && pubkey.length() == 172 && signer.length() > 0)
rv.put(pubkey, signer);
2010-02-07 13:32:49 +00:00
}
return rv;
}
2010-02-08 16:15:23 +00:00
/**
* plugins.config
*/
public static void storePluginProperties(Properties props) {
File cfgFile = new File(I2PAppContext.getGlobalContext().getConfigDir(), "plugins.config");
try {
DataHelper.storeProps(props, cfgFile);
} catch (IOException ioe) {}
}
/**
* @param action "start" or "stop" or "uninstall"
* @throws 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 {
2010-02-07 13:32:49 +00:00
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>());
2010-02-07 13:32:49 +00:00
for(ClientAppConfig app : apps) {
if (action.equals("start") && app.disabled)
2010-02-07 13:32:49 +00:00
continue;
2010-02-08 20:37:49 +00:00
String argVal[];
if (action.equals("start")) {
// start
2010-02-08 20:37:49 +00:00
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)
2010-02-08 20:37:49 +00:00
continue;
argVal = LoadClientAppsJob.parseArgs(args);
2010-02-08 20:37:49 +00:00
}
2010-02-07 13:32:49 +00:00
// 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;
2010-02-07 17:13:44 +00:00
if (app.classpath != null) {
String cp = app.classpath;
2010-02-07 17:13:44 +00:00
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);
}
}
2010-02-07 17:13:44 +00:00
}
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);
2010-02-07 13:32:49 +00:00
// run this guy now
LoadClientAppsJob.runClient(app.className, app.clientName, argVal, ctx, log, pluginThreadGroup, cl);
2010-02-07 13:32:49 +00:00
} else {
// If there is some delay, there may be a really good reason for it.
// Loading a class would be one of them!
// So we do a quick check first, If it bombs out, we delay and try again.
// If it bombs after that, then we throw the ClassNotFoundException.
try {
// quick check
LoadClientAppsJob.testClient(app.className, cl);
} catch (ClassNotFoundException ex) {
// Try again 1 or 2 seconds later.
// This should be enough time. Although it is a lousy hack
// it should work for most cases.
// Perhaps it may be even better to delay a percentage
// if > 1, and reduce the delay time.
// 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);
2010-02-07 13:32:49 +00:00
}
}
}
2010-02-07 17:13:44 +00:00
/**
* Simple override to track whether a plugin's client is delayed and queued
* @since 0.9.6
*/
private static class TrackedDelayedClient extends LoadClientAppsJob.DelayedRunClient {
private final String _pluginName;
public TrackedDelayedClient(String pluginName,
SimpleTimer2 pool, RouterContext enclosingContext, String className, String clientName,
String args[], ThreadGroup threadGroup, ClassLoader cl) {
super(pool, enclosingContext, className, clientName, args, threadGroup, cl);
_pluginName = pluginName;
_pendingPluginClients.get(pluginName).add(this);
}
@Override
public boolean cancel() {
boolean rv = super.cancel();
_pendingPluginClients.get(_pluginName).remove(this);
return rv;
}
@Override
public void timeReached() {
super.timeReached();
_pendingPluginClients.get(_pluginName).remove(this);
}
}
public static boolean isPluginRunning(String pluginName, RouterContext ctx) {
Log log = ctx.logManager().getLog(PluginStarter.class);
boolean isJobRunning = false;
Collection<SimpleTimer2.TimedEvent> pending = _pendingPluginClients.get(pluginName);
if (pending != null && !pending.isEmpty()) {
// TODO have a pending indication too
isJobRunning = true;
}
boolean isWarRunning = false;
if(pluginWars.containsKey(pluginName)) {
Iterator <String> it = pluginWars.get(pluginName).iterator();
while(it.hasNext() && !isWarRunning) {
String warName = it.next();
if(WebAppStarter.isWebAppRunning(warName)) {
isWarRunning = true;
}
}
}
2010-04-23 16:28:14 +00:00
if (log.shouldLog(Log.DEBUG))
log.debug("plugin name = <" + pluginName + ">; threads running? " + isClientThreadRunning(pluginName) + "; webapp runing? " + isWarRunning + "; jobs running? " + isJobRunning);
return isClientThreadRunning(pluginName) || isWarRunning || isJobRunning;
//
//if (log.shouldLog(Log.DEBUG))
// log.debug("plugin name = <" + pluginName + ">; threads running? " + isClientThreadRunning(pluginName) + "; webapp runing? " + WebAppStarter.isWebAppRunning(pluginName) + "; jobs running? " + isJobRunning);
//return isClientThreadRunning(pluginName) || WebAppStarter.isWebAppRunning(pluginName) || isJobRunning;
//
}
/**
* Returns <code>true</code> if one or more client threads are running in a given plugin.
* @param pluginName
2013-06-30 17:13:53 +00:00
* @return true if running
*/
private static boolean isClientThreadRunning(String pluginName) {
ThreadGroup group = pluginThreadGroups.get(pluginName);
if (group == null)
return false;
Thread[] activeThreads = new Thread[1];
group.enumerate(activeThreads);
return activeThreads[0] != null;
}
2010-02-07 17:13:44 +00:00
/**
* 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.
*/
/******
2010-02-07 17:13:44 +00:00
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 {
addPath(f.toURI().toURL());
if (log.shouldLog(Log.WARN))
log.warn("INFO: Adding plugin to classpath: " + f);
2010-02-07 17:13:44 +00:00
} catch (Exception e) {
log.error("Plugin client " + clientName + " bad classpath element: " + f, e);
}
}
}
*****/
/**
* @return null if no valid elements
*/
private static URL[] classpathToURLArray(String classpath, String clientName, Log log) {
StringTokenizer tok = new StringTokenizer(classpath, ",");
List<URL> urls = new ArrayList<URL>();
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 {
urls.add(f.toURI().toURL());
if (log.shouldLog(Log.WARN))
log.warn("INFO: Adding plugin to classpath: " + f);
} catch (Exception e) {
log.error("Plugin client " + clientName + " bad classpath element: " + f, e);
}
}
if (urls.isEmpty())
return null;
return urls.toArray(new URL[urls.size()]);
}
2010-02-07 17:13:44 +00:00
/**
* http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/
*/
private static void addPath(URL u) throws Exception {
2010-02-07 17:13:44 +00:00
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
2013-11-24 23:41:06 +00:00
Class<URLClassLoader> urlClass = URLClassLoader.class;
2010-02-07 17:13:44 +00:00
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u});
}
/** translate a string */
private static String ngettext(String s, String p, int n, I2PAppContext ctx) {
return Messages.getString(n, s, p, ctx);
}
2010-02-07 13:32:49 +00:00
}