From fc6f4ecc7430f4130eb9b5de7d011eba6f1a36fb Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 15 Jan 2012 21:15:08 +0000 Subject: [PATCH] * Plugins: - Auto-update plugins after a router update - Add update-all button --- .../i2p/router/web/ConfigClientsHandler.java | 14 +++ .../src/net/i2p/router/web/PluginStarter.java | 92 +++++++++++++++++++ .../i2p/router/web/PluginUpdateChecker.java | 32 ++++++- .../i2p/router/web/PluginUpdateHandler.java | 2 +- .../src/net/i2p/router/web/UpdateHandler.java | 2 +- apps/routerconsole/jsp/configclients.jsp | 4 + 6 files changed, 141 insertions(+), 5 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java index eb28a4b90e..3e9a10db9b 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -53,6 +53,10 @@ public class ConfigClientsHandler extends FormHandler { installPlugin(); return; } + if (_action.equals(_("Update All Installed Plugins"))) { + updateAllPlugins(); + return; + } // value if (_action.startsWith("Start ")) { String app = _action.substring(6); @@ -321,6 +325,16 @@ public class ConfigClientsHandler extends FormHandler { installPlugin(url); } + /** @since 0.8.13 */ + private void updateAllPlugins() { + addFormNotice(_("Updating all plugins")); + PluginStarter.updateAll(_context); + // So that update() will post a status to the summary bar before we reload + try { + Thread.sleep(1000); + } catch (InterruptedException ie) {} + } + private void installPlugin(String url) { if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) { addFormError(_("Plugin or update download already in progress.")); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java index 29f083bc92..c4c567b3f9 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -22,10 +22,12 @@ import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.router.Job; import net.i2p.router.RouterContext; +import net.i2p.router.RouterVersion; import net.i2p.router.startup.ClientAppConfig; import net.i2p.router.startup.LoadClientAppsJob; import net.i2p.util.ConcurrentHashSet; import net.i2p.util.FileUtil; +import net.i2p.util.I2PAppThread; import net.i2p.util.Log; import net.i2p.util.Translate; import net.i2p.util.VersionComparator; @@ -63,9 +65,96 @@ public class PluginStarter implements Runnable { } public void run() { + if (_context.getBooleanPropertyDefaultTrue("plugins.autoUpdate") && + (!Boolean.valueOf(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS)).booleanValue()) && + (!RouterVersion.VERSION.equals(_context.getProperty("router.previousVersion")))) + updateAll(_context, true); 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(); + 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; + PluginUpdateChecker puc = PluginUpdateChecker.getInstance(ctx); + if (puc.isRunning()) + return; + + if (delay) { + // wait for proxy + System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "true"); + puc.setAppStatus(Messages.getString("Checking for plugin updates", ctx)); + try { + Thread.sleep(3*60*1000); + } catch (InterruptedException ie) {} + System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "false"); + } + + Log log = ctx.logManager().getLog(PluginStarter.class); + for (Map.Entry<String, String> entry : toUpdate.entrySet()) { + String appName = entry.getKey(); + if (log.shouldLog(Log.WARN)) + log.warn("Checking for update plugin: " + appName); + puc.update(appName); + do { + try { + Thread.sleep(5*1000); + } catch (InterruptedException ie) {} + } while (puc.isRunning()); + if (!puc.isNewerAvailable()) { + if (log.shouldLog(Log.WARN)) + log.warn("No update available for plugin: " + appName); + continue; + } + PluginUpdateHandler puh = PluginUpdateHandler.getInstance(ctx); + String url = entry.getValue(); + if (log.shouldLog(Log.WARN)) + log.warn("Updating plugin: " + appName); + puh.update(url); + do { + try { + Thread.sleep(5*1000); + } catch (InterruptedException ie) {} + } while (puh.isRunning()); + } + } + /** this shouldn't throw anything */ static void startPlugins(RouterContext ctx) { Log log = ctx.logManager().getLog(PluginStarter.class); @@ -75,6 +164,9 @@ public class PluginStarter implements Runnable { if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) { if (Boolean.valueOf(props.getProperty(name)).booleanValue()) { String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED)); + // plugins could have been started after update + if (isPluginRunning(app, ctx)) + continue; try { if (!startPlugin(ctx, app)) log.error("Failed to start plugin: " + app); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java index cf3f75f3eb..1e7fceb40f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateChecker.java @@ -36,6 +36,7 @@ public class PluginUpdateChecker extends UpdateHandler { private String _appName; private String _oldVersion; private String _xpi2pURL; + private volatile boolean _isNewerAvailable; private static PluginUpdateChecker _instance; public static final synchronized PluginUpdateChecker getInstance(RouterContext ctx) { @@ -49,12 +50,19 @@ public class PluginUpdateChecker extends UpdateHandler { super(ctx); } - /** check all plugins */ + /** + * check all plugins + * @deprecated not finished + */ public void update() { Thread t = new I2PAppThread(new AllCheckerRunner(), "AllAppChecker", true); t.start(); } + /** + * check all plugins + * @deprecated not finished + */ public class AllCheckerRunner implements Runnable { public void run() { List<String> plugins = PluginStarter.getPlugins(); @@ -85,12 +93,18 @@ public class PluginUpdateChecker extends UpdateHandler { _xpi2pURL = xpi2pURL; _appName = appName; _oldVersion = oldVersion; + _isNewerAvailable = false; System.setProperty(PROP_UPDATE_IN_PROGRESS, "true"); I2PAppThread update = new I2PAppThread(_pluginUpdateCheckerRunner, "AppChecker", true); update.start(); } } + /** @since 0.8.13 */ + public void setAppStatus(String status) { + updateStatus(status); + } + public boolean isRunning() { return _pluginUpdateCheckerRunner != null && _pluginUpdateCheckerRunner.isRunning(); } @@ -101,6 +115,11 @@ public class PluginUpdateChecker extends UpdateHandler { return false; } + /** @since 0.8.13 */ + public boolean isNewerAvailable() { + return _isNewerAvailable; + } + private void scheduleStatusClean(String msg) { SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000); } @@ -126,6 +145,7 @@ public class PluginUpdateChecker extends UpdateHandler { @Override protected void update() { + _isNewerAvailable = false; updateStatus("<b>" + _("Checking for update of plugin {0}", _appName) + "</b>"); // use the same settings as for updater // always proxy, or else FIXME @@ -142,6 +162,10 @@ public class PluginUpdateChecker extends UpdateHandler { } } + public boolean isNewerAvailable() { + return _isNewerAvailable; + } + @Override public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) { } @@ -151,10 +175,12 @@ public class PluginUpdateChecker extends UpdateHandler { String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray())); boolean newer = (new VersionComparator()).compare(newVersion, _oldVersion) > 0; String msg; - if (newer) + if (newer) { msg = "<b>" + _("New plugin version {0} is available", newVersion) + "</b>"; - else + _isNewerAvailable = true; + } else { msg = "<b>" + _("No new version is available for plugin {0}", _appName) + "</b>"; + } updateStatus(msg); scheduleStatusClean(msg); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java index e2499d1312..df7f19a4cd 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java @@ -93,7 +93,7 @@ public class PluginUpdateHandler extends UpdateHandler { } private void scheduleStatusClean(String msg) { - SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000); + SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 20*60*1000); } private class Cleaner implements SimpleTimer.TimedEvent { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java index 2f2d3db74f..5b7eaec03c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java @@ -127,7 +127,7 @@ public class UpdateHandler { } public class UpdateRunner implements Runnable, EepGet.StatusListener { - protected boolean _isRunning; + protected volatile boolean _isRunning; protected boolean done; protected EepGet _get; protected final DecimalFormat _pct = new DecimalFormat("0.0%"); diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp index 4c70beafed..25df053f46 100644 --- a/apps/routerconsole/jsp/configclients.jsp +++ b/apps/routerconsole/jsp/configclients.jsp @@ -103,6 +103,7 @@ button span.hide{ <jsp:getProperty name="clientshelper" property="form2" /> <p><i><%=intl._("All changes require restart to take effect.")%></i> </p><hr><div class="formaction"> + <input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" /> <input type="submit" name="action" class="accept" value="<%=intl._("Save WebApp Configuration")%>" /> </div></form></div> @@ -114,6 +115,7 @@ button span.hide{ <input type="hidden" name="nonce" value="<%=pageNonce%>" > <jsp:getProperty name="clientshelper" property="form3" /> <hr><div class="formaction"> + <input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" /> <input type="submit" name="action" class="accept" value="<%=intl._("Save Plugin Configuration")%>" /> </div></form></div> @@ -125,7 +127,9 @@ button span.hide{ <p> <input type="text" size="60" name="pluginURL" > </p><hr><div class="formaction"> + <input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" /> <input type="submit" name="action" class="download" value="<%=intl._("Install Plugin")%>" /> + <input type="submit" name="action" class="reload" value="<%=intl._("Update All Installed Plugins")%>" /> </div></form></div> <% } %> </div></div></body></html> -- GitLab