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 b65d58fb6b..7c911156fa 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
@@ -54,6 +54,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);
@@ -322,6 +326,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/FileDumpHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java
index 6c95734f7b..eea306aa20 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/FileDumpHelper.java
@@ -113,17 +113,18 @@ public class FileDumpHelper extends HelperBase {
if (att == null)
att = new Attributes();
buf.append("");
+ String iv = getAtt(att, "Implementation-Version");
+ if (iv != null)
+ buf.append("").append(iv).append("");
String s = getAtt(att, "Base-Revision");
if (s != null && s.length() > 20) {
+ if (iv != null)
+ buf.append(" ");
buf.append("" +
"").append(s.substring(0, 20)).append("" +
" " +
"").append(s.substring(20)).append("");
- } else {
- s = getAtt(att, "Implementation-Version");
- if (s != null)
- buf.append("").append(s).append("");
}
buf.append(" | ");
s = getAtt(att, "Created-By");
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 6be4b1cd7e..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 plugins = getPlugins();
+ Map 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 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);
@@ -95,6 +187,7 @@ public class PluginStarter implements Runnable {
File pluginDir = new File(ctx.getConfigDir(), PluginUpdateHandler.PLUGIN_DIR + '/' + appName);
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
log.error("Cannot start nonexistent plugin: " + appName);
+ disablePlugin(appName);
return false;
}
@@ -104,6 +197,7 @@ public class PluginStarter implements Runnable {
(new VersionComparator()).compare(CoreVersion.VERSION, minVersion) < 0) {
String foo = "Plugin " + appName + " requires I2P version " + minVersion + " or higher";
log.error(foo);
+ disablePlugin(appName);
throw new Exception(foo);
}
@@ -112,6 +206,7 @@ public class PluginStarter implements Runnable {
(new VersionComparator()).compare(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);
}
@@ -121,6 +216,7 @@ public class PluginStarter implements Runnable {
(new VersionComparator()).compare(minVersion, jVersion) > 0) {
String foo = "Plugin " + appName + " requires Jetty version " + minVersion + " or higher";
log.error(foo);
+ disablePlugin(appName);
throw new Exception(foo);
}
@@ -129,6 +225,7 @@ public class PluginStarter implements Runnable {
(new VersionComparator()).compare(maxVersion, jVersion) < 0) {
String foo = "Plugin " + appName + " requires Jetty version " + maxVersion + " or lower";
log.error(foo);
+ disablePlugin(appName);
throw new Exception(foo);
}
@@ -334,7 +431,7 @@ public class PluginStarter implements Runnable {
Properties props = pluginProperties();
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
- if (name.startsWith(PREFIX + appName))
+ if (name.startsWith(PREFIX + appName + '.'))
iter.remove();
}
storePluginProperties(props);
@@ -373,6 +470,32 @@ public class PluginStarter implements Runnable {
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.valueOf(props.getProperty(prop, "true")).booleanValue();
+ }
+
+ /**
+ * Disable in plugins.config
+ *
+ * @since 0.8.13
+ */
+ public static void disablePlugin(String appName) {
+ Properties props = pluginProperties();
+ String prop = PREFIX + appName + ENABLED;
+ if (Boolean.valueOf(props.getProperty(prop, "true")).booleanValue()) {
+ props.setProperty(prop, "false");
+ storePluginProperties(props);
+ }
+ }
+
/**
* all installed plugins whether enabled or not
*/
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStopper.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStopper.java
index 2f29eddf27..ca81463d04 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStopper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStopper.java
@@ -4,7 +4,7 @@ import net.i2p.router.RouterContext;
import net.i2p.util.Log;
/**
- * Stop all plugins that are installed
+ * Stop all plugins that are installed and running
*
* @since 0.7.13
* @author zzz
@@ -21,19 +21,20 @@ public class PluginStopper extends PluginStarter {
}
/**
- * Stop all plugins
- * (whether or not they were ever started)
+ * Stop all running plugins
*
* this shouldn't throw anything
*/
- static void stopPlugins(RouterContext ctx) {
+ private static void stopPlugins(RouterContext ctx) {
Log log = ctx.logManager().getLog(PluginStopper.class);
for (String app : getPlugins()) {
- try {
- stopPlugin(ctx, app);
- } catch (Throwable e) {
- if (log.shouldLog(Log.WARN))
- log.warn("Failed to stop plugin: " + app, e);
+ if (isPluginRunning(app, ctx)) {
+ try {
+ stopPlugin(ctx, app);
+ } catch (Throwable e) {
+ if (log.shouldLog(Log.WARN))
+ log.warn("Failed to stop plugin: " + app, e);
+ }
}
}
}
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..73b6850a04 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 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,12 +115,17 @@ 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);
+ SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 20*60*1000);
}
private class Cleaner implements SimpleTimer.TimedEvent {
- private String _msg;
+ private final String _msg;
public Cleaner(String msg) {
_msg = msg;
}
@@ -126,6 +145,7 @@ public class PluginUpdateChecker extends UpdateHandler {
@Override
protected void update() {
+ _isNewerAvailable = false;
updateStatus("" + _("Checking for update of plugin {0}", _appName) + "");
// 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 = "" + _("New plugin version {0} is available", newVersion) + "";
- else
+ _isNewerAvailable = true;
+ } else {
msg = "" + _("No new version is available for plugin {0}", _appName) + "";
+ }
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 511bfb0cd0..ecef4bb19e 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java
@@ -93,11 +93,11 @@ 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 {
- private String _msg;
+ private final String _msg;
public Cleaner(String msg) {
_msg = msg;
}
@@ -286,6 +286,7 @@ public class PluginUpdateHandler extends UpdateHandler {
return;
}
+ boolean wasRunning = false;
File destDir = new SecureDirectory(appDir, appName);
if (destDir.exists()) {
if (Boolean.valueOf(props.getProperty("install-only")).booleanValue()) {
@@ -350,14 +351,16 @@ public class PluginUpdateHandler extends UpdateHandler {
return;
}
- // check if it is running first?
- try {
- if (!PluginStarter.stopPlugin(_context, appName)) {
- // failed, ignore
+ if (PluginStarter.isPluginRunning(appName, _context)) {
+ wasRunning = true;
+ try {
+ if (!PluginStarter.stopPlugin(_context, appName)) {
+ // failed, ignore
+ }
+ } catch (Throwable e) {
+ // no updateStatus() for this one
+ _log.error("Error stopping plugin " + appName, e);
}
- } catch (Throwable e) {
- // no updateStatus() for this one
- _log.error("Error stopping plugin " + appName, e);
}
} else {
@@ -390,8 +393,8 @@ public class PluginUpdateHandler extends UpdateHandler {
pluginProps.setProperty(PluginStarter.PREFIX + appName + PluginStarter.ENABLED, "false");
PluginStarter.storePluginProperties(pluginProps);
}
- } else {
- // start everything
+ } else if (wasRunning || PluginStarter.isPluginEnabled(appName)) {
+ // start everything unless it was disabled and not running before
try {
if (PluginStarter.startPlugin(_context, appName)) {
String linkName = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(_context));
@@ -411,6 +414,8 @@ public class PluginUpdateHandler extends UpdateHandler {
statusDone("" + _("Plugin {0} installed but failed to start", appName) + ": " + e + "");
_log.error("Error starting plugin " + appName, e);
}
+ } else {
+ statusDone("" + _("Plugin {0} installed", appName) + "");
}
}
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{
<%=intl._("All changes require restart to take effect.")%>
+ " />
" />
@@ -114,6 +115,7 @@ button span.hide{
+ " />
" />
@@ -125,7 +127,9 @@ button span.hide{
+ " />
" />
+ " />
<% } %>
|