diff --git a/apps/addressbook/build.xml b/apps/addressbook/build.xml index 2fe77475cb9d1dab7707b0e547e25a1ccefa2e3f..ad27a009059cb83d09cdaebac3cab0e145e93160 100644 --- a/apps/addressbook/build.xml +++ b/apps/addressbook/build.xml @@ -55,6 +55,7 @@ <property name="workspace.changes" value="" /> <manifest> <attribute name="Main-Class" value="addressbook.Daemon"/> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes}" /> @@ -73,6 +74,7 @@ <property name="workspace.changes.tr" value="" /> <war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}"> <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/apps/i2psnark/java/build.xml b/apps/i2psnark/java/build.xml index edc92ed5b102c5ac9e48b3e3921261efaf83da8a..c5a3b106fd7fd5e3d44efcb0184818a04652c005 100644 --- a/apps/i2psnark/java/build.xml +++ b/apps/i2psnark/java/build.xml @@ -60,6 +60,7 @@ <manifest> <attribute name="Main-Class" value="org.klomp.snark.Snark" /> <attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" /> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> @@ -98,6 +99,7 @@ <!-- include only the web stuff, as of 0.7.12 the router will add i2psnark.jar to the classpath for the war --> <classes dir="./build/obj" includes="**/web/*.class" /> <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/apps/i2ptunnel/java/build.xml b/apps/i2ptunnel/java/build.xml index 1778ffb8722dc2102d5d996994c05c11acbfd74f..1b62279145de25669f48b9c6356ee25069488ba1 100644 --- a/apps/i2ptunnel/java/build.xml +++ b/apps/i2ptunnel/java/build.xml @@ -62,6 +62,7 @@ <manifest> <attribute name="Main-Class" value="net.i2p.i2ptunnel.I2PTunnel" /> <attribute name="Class-Path" value="i2p.jar mstreaming.jar" /> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.j.tr}" /> @@ -134,6 +135,7 @@ <war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml" basedir="../jsp/" excludes="web.xml, web-fragment.xml, web-out.xml, **/*.java, *.jsp"> <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.w.tr}" /> diff --git a/apps/ministreaming/java/build.xml b/apps/ministreaming/java/build.xml index 8904bafb3b1ddb7f01c3e3356c881611b50aafb8..235088fc312852a00a4acd178198fc3b7267932d 100644 --- a/apps/ministreaming/java/build.xml +++ b/apps/ministreaming/java/build.xml @@ -50,6 +50,7 @@ <property name="workspace.changes.tr" value="" /> <jar destfile="./build/mstreaming.jar" basedir="./build/obj" includes="**/*.class" > <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml index f49153486c6587179aa8355893eb6c5c587b884c..3dba335781c6f87708177aa15ce91c80d5017dba 100644 --- a/apps/routerconsole/java/build.xml +++ b/apps/routerconsole/java/build.xml @@ -35,6 +35,7 @@ <dependset> <srcfilelist dir="." files="../../../router/java/build/obj/net/i2p/router/RouterVersion.class" /> <targetfilelist dir="." files="build/obj/net/i2p/router/web/NewsFetcher.class" /> + <targetfilelist dir="." files="build/obj/net/i2p/router/web/PluginStarter.class" /> <targetfilelist dir="." files="build/obj/net/i2p/router/web/SummaryHelper.class" /> <targetfilelist dir="." files="build/obj/net/i2p/router/web/UpdateHandler.class" /> </dependset> @@ -90,6 +91,7 @@ <!-- top level installer will rename to jrobin.jar --> <!-- DTG added in 0.8.4, not in the classpath for very old installs, before we changed wrapper.config to specify * --> <attribute name="Class-Path" value="i2p.jar router.jar jrobin.jar desktopgui.jar" /> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.j.tr}" /> @@ -169,6 +171,7 @@ <war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml" basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml, web-out.xml"> <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.w.tr}" /> @@ -292,6 +295,8 @@ <uptodate property="precompilejsp.uptodate" targetfile="../jsp/web-out.xml"> <srcfiles dir= "../jsp" includes="**/*.jsp, *.jsi, **/*.html, *.css, susimail/susimail, web.xml"/> + <!-- so the version is right on logs.jsp --> + <srcfiles dir= "../../../router/java/src/net/i2p/router" includes="RouterVersion.java"/> </uptodate> <target name="javadoc"> 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 b65d58fb6b78eb5d2a9ee4b866cdb14047b97536..7c911156fa327f5c5d3f1036a2359e49a2e6f514 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 6c95734f7ba80924fdc4b3b50d4c935c1aafa718..eea306aa209f174fdb77cf725fbfced0306f8085 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("<td align=\"center\">"); + String iv = getAtt(att, "Implementation-Version"); + if (iv != null) + buf.append("<b>").append(iv).append("</b>"); String s = getAtt(att, "Base-Revision"); if (s != null && s.length() > 20) { + if (iv != null) + buf.append("<br>"); buf.append("<a href=\"http://stats.i2p/cgi-bin/viewmtn/revision/info/").append(s) .append("\">" + "<tt>").append(s.substring(0, 20)).append("</tt>" + "<br>" + "<tt>").append(s.substring(20)).append("</tt></a>"); - } else { - s = getAtt(att, "Implementation-Version"); - if (s != null) - buf.append("<b>").append(s).append("</b>"); } buf.append("</td><td>"); 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 6be4b1cd7ee034d23a7e953f0e7eb7431e630a98..c4c567b3f9d7124a1a559dec2a34d0d1476544b3 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); @@ -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 2f29eddf273e66f67b0266fc7ae8d7e360667292..ca81463d04223486f9fbb861083f7d1d37d22636 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 cf3f75f3ebba6e6263af039f461e7717e8200396..73b6850a04dc544e949e0c4539a63a05e03c880c 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,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("<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 511bfb0cd075de25f00352148c67a4a16c89a9d2..ecef4bb19e9aea665b40b8e32c18845283b8ad28 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("<b>" + _("Plugin {0} installed but failed to start", appName) + ": " + e + "</b>"); _log.error("Error starting plugin " + appName, e); } + } else { + statusDone("<b>" + _("Plugin {0} installed", appName) + "</b>"); } } 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 2f2d3db74f2575ffb283548ca5695e0f45cce372..5b7eaec03c2bfe90288ee8be0fa4c2c83160171d 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 4c70beafed1b3f7d984408b7d6bce64c843d5ce1..25df053f46a55e5f0450dc3d5d42d3f02ac4c325 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> diff --git a/apps/sam/java/build.xml b/apps/sam/java/build.xml index d55ed0d61c3bca555838c1067b553d83caf21986..4433c057543ae511bb5b7a83a20ee29169507fbd 100644 --- a/apps/sam/java/build.xml +++ b/apps/sam/java/build.xml @@ -66,6 +66,7 @@ <manifest> <attribute name="Main-Class" value="net.i2p.sam.SAMBridge" /> <attribute name="Class-Path" value="i2p.jar mstreaming.jar streaming.jar" /> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/apps/streaming/java/build.xml b/apps/streaming/java/build.xml index b5c842f5b94641dd273d53dd46104893e1b84470..5f278f307c67183780e29bb28cb3294c0721d6df 100644 --- a/apps/streaming/java/build.xml +++ b/apps/streaming/java/build.xml @@ -63,6 +63,7 @@ <property name="workspace.changes.tr" value="" /> <jar destfile="./build/streaming.jar" basedir="./build/obj" includes="**/*.class" > <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/apps/susidns/src/build.xml b/apps/susidns/src/build.xml index d287e5a4d013d576563de775a45cfb83ade59f8d..3fff55a34c9fce7f4e652c65027842ddd79d6a28 100644 --- a/apps/susidns/src/build.xml +++ b/apps/susidns/src/build.xml @@ -97,6 +97,7 @@ <include name="WEB-INF/classes/${project}.properties"/> </fileset> <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/apps/susimail/build.xml b/apps/susimail/build.xml index 8a8d33c4ad00b9fdb7b3c395014174dbdfe8bb9f..3368283d115e8b65a72f790f99e4c982d7f4b75d 100644 --- a/apps/susimail/build.xml +++ b/apps/susimail/build.xml @@ -45,6 +45,7 @@ <war destfile="susimail.war" webxml="src/WEB-INF/web.xml" basedir="src/" excludes="WEB-INF/web.xml LICENSE src/**/*"> <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/apps/systray/java/build.xml b/apps/systray/java/build.xml index 4dd50f0c98914c25456a0f2b25b70d1e74dac31e..5baf4ab6224a5b7183af97e6a43053b9b3e78180 100644 --- a/apps/systray/java/build.xml +++ b/apps/systray/java/build.xml @@ -44,6 +44,7 @@ <manifest> <attribute name="Main-Class" value="net.i2p.apps.systray.SysTray" /> <attribute name="Class-Path" value="systray4j.jar" /> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/build.xml b/build.xml index dd49fd8cb511e75e7c602d7ede0d4393c1cb06f9..6365da50b01513768683c8e7d7fe3343dc1ca8fc 100644 --- a/build.xml +++ b/build.xml @@ -182,12 +182,14 @@ </exec> </target> - <target name="buildProperties" depends="getMtnRev" > + <target name="buildProperties" depends="getMtnRev, getReleaseNumber, getBuildNumber" > <!-- default if not set above --> <property name="workspace.version" value="unknown" /> <tstamp> <format property="build.timestamp" pattern="yyyy-MM-dd HH:mm:ss z" timezone="UTC" locale="en" /> </tstamp> + <property name="full.version" value="${release.number}-${build.number}${build.extra}" /> + <echo message="Building version ${full.version} (mtn rev ${workspace.version})" /> </target> <!-- end of sub-build.xml targets --> diff --git a/core/java/build.xml b/core/java/build.xml index d6275085945258c6f4fad34e43b62fdf50395ce6..c3eaf5c0780dd3d9f59692af034f100916ccf2c5 100644 --- a/core/java/build.xml +++ b/core/java/build.xml @@ -58,6 +58,7 @@ <property name="workspace.changes.tr" value="" /> <jar destfile="./build/i2p.jar" basedir="./build/obj" includes="**/*.class" > <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/history.txt b/history.txt index 0ca887885c4e88e7ad6c6d2bc4b5ee2504344432..da999adc4b6373491e4a025f21d0233bf121849d 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,19 @@ +2012-01-16 zzz + * Build: Put Implementation-Version in manifests + * NetDB: Hopefully fix rare NPE (ticket #589) + * Plugins: + - Only stop a plugin before update if it was running + - Only stop a plugin at shutdown if it was running + - Don't start a plugin after update if it was disabled + - Disable plugin if it fails version checks at startup + - Auto-update plugins after a router update + - Add update-all button and more cancel buttons + * Router: + - Add synchronized change-and-save-config methods to avoid races + - Save previous version in config so we know when we updated + * Transport: Revert change from -2, put addresses back in RouterInfo + when hidden, broke inbound tunnel building + 2012-01-14 zzz * i2ptunnel: Partial fix for dest formatting (ticket #581) * jars.jsp: New debug page diff --git a/router/java/build.xml b/router/java/build.xml index ea9f7b6135f6505c65267e0d761b85c4f48cd79f..e933086cf805521f2df9b380d6623be6d1c5a74d 100644 --- a/router/java/build.xml +++ b/router/java/build.xml @@ -72,6 +72,7 @@ <property name="workspace.changes.tr" value="" /> <jar destfile="./build/router.jar" basedir="./build/obj" includes="**/*.class" > <manifest> + <attribute name="Implementation-Version" value="${full.version}" /> <attribute name="Build-Date" value="${build.timestamp}" /> <attribute name="Base-Revision" value="${workspace.version}" /> <attribute name="Workspace-Changes" value="${workspace.changes.tr}" /> diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 084731b7bbf96518c1b023a4ed8c0793d75fefae..f97422d02e720848627d16af11ddfa1397f44570 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -15,6 +15,7 @@ import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.Writer; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; @@ -241,6 +242,8 @@ public class Router implements RouterClock.ClockShiftListener { String now = Long.toString(System.currentTimeMillis()); _config.put("router.firstInstalled", now); _config.put("router.updateLastInstalled", now); + // First added in 0.8.13 + _config.put("router.previousVersion", RouterVersion.VERSION); saveConfig(); } // ********* Start no threads before here ********* // @@ -324,9 +327,23 @@ public class Router implements RouterClock.ClockShiftListener { public String getConfigSetting(String name) { return _config.get(name); } + + /** + * Warning, race between here and saveConfig(), + * saveConfig(String name, String value) or saveConfig(Map toAdd, Set toRemove) is recommended. + * + * @since 0.8.13 + */ public void setConfigSetting(String name, String value) { _config.put(name, value); } + + /** + * Warning, race between here and saveConfig(), + * saveConfig(String name, String value) or saveConfig(Map toAdd, Set toRemove) is recommended. + * + * @since 0.8.13 + */ public void removeConfigSetting(String name) { _config.remove(name); } @@ -1041,6 +1058,13 @@ public class Router implements RouterClock.ClockShiftListener { _log.log(Log.CRIT, "Error running shutdown task", t); } } + + // Set the last version to the current version, since 0.8.13 + if (!RouterVersion.VERSION.equals(_config.get("router.previousVersion"))) { + _config.put("router.previousVersion", RouterVersion.VERSION); + saveConfig(); + } + _context.removeShutdownTasks(); try { _context.clientManager().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the client manager", t); } try { _context.namingService().shutdown(); } catch (Throwable t) { _log.error("Error shutting down the naming service", t); } @@ -1244,6 +1268,45 @@ public class Router implements RouterClock.ClockShiftListener { return true; } + /** + * Updates the current config and then saves it. + * Prevents a race in the interval between setConfigSetting() / removeConfigSetting() and saveConfig(), + * Synchronized with getConfig() / saveConfig() + * + * @param name setting to add/change/remove before saving + * @param value if non-null, updated value; if null, setting will be removed + * @return success + * @since 0.8.13 + */ + public synchronized boolean saveConfig(String name, String value) { + if (value != null) + _config.put(name, value); + else + _config.remove(name); + return saveConfig(); + } + + /** + * Updates the current config and then saves it. + * Prevents a race in the interval between setConfigSetting() / removeConfigSetting() and saveConfig(), + * Synchronized with getConfig() / saveConfig() + * + * @param toAdd settings to add/change before saving, may be null or empty + * @param toRemove settings to remove before saving, may be null or empty + * @return success + * @since 0.8.13 + */ + public synchronized boolean saveConfig(Map toAdd, Collection<String> toRemove) { + if (toAdd != null) + _config.putAll(toAdd); + if (toRemove != null) { + for (String s : toRemove) { + _config.remove(toRemove); + } + } + return saveConfig(); + } + /** * The clock shift listener. * Restart the router if we should. @@ -1345,6 +1408,8 @@ public class Router implements RouterClock.ClockShiftListener { // This may be useful someday. First added in 0.8.2 // Moved above the extract so we don't NCDFE _config.put("router.updateLastInstalled", "" + System.currentTimeMillis()); + // Set the last version to the current version, since 0.8.13 + _config.put("router.previousVersion", RouterVersion.VERSION); saveConfig(); ok = FileUtil.extractZip(updateFile, _context.getBaseDir()); } diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 0725033fa600cfb5180ac82283f5194ee35b34b0..f6e6df3a5c0d10fbf1efc2f7069c342c2fe16f4e 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 3; + public final static long BUILD = 4; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java index f27be069a496ae0863938bff25ae9e445783c5a6..09560458056307b19defcf4f2cefcfa545c2c7c6 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java @@ -25,10 +25,10 @@ import net.i2p.util.Log; * */ class KBucketSet { - private Log _log; - private I2PAppContext _context; - private LocalHash _us; - private KBucket _buckets[]; + private final Log _log; + private final I2PAppContext _context; + private final LocalHash _us; + private final KBucket _buckets[]; private volatile int _size; public final static int BASE = 8; // must go into KEYSIZE_BITS evenly @@ -41,7 +41,7 @@ class KBucketSet { _us = new LocalHash(us); _context = context; _log = context.logManager().getLog(KBucketSet.class); - createBuckets(); + _buckets = createBuckets(); context.statManager().createRateStat("netDb.KBSGetAllTime", "Time to add all Hashes to the Collector", "NetworkDatabase", new long[] { 60*60*1000 }); } @@ -132,12 +132,13 @@ class KBucketSet { public KBucket getBucket(int bucket) { return _buckets[bucket]; } - protected void createBuckets() { - _buckets = new KBucket[NUM_BUCKETS]; + protected KBucket[] createBuckets() { + KBucket[] buckets = new KBucket[NUM_BUCKETS]; for (int i = 0; i < NUM_BUCKETS-1; i++) { - _buckets[i] = createBucket(i*BASE, (i+1)*BASE); + buckets[i] = createBucket(i*BASE, (i+1)*BASE); } - _buckets[NUM_BUCKETS-1] = createBucket(BASE*(NUM_BUCKETS-1), BASE*(NUM_BUCKETS) + 1); + buckets[NUM_BUCKETS-1] = createBucket(BASE*(NUM_BUCKETS-1), BASE*(NUM_BUCKETS) + 1); + return buckets; } protected KBucket createBucket(int start, int end) { diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index c1302b9aa5f4abe93edd4f504478da34a4f87615..73fb5cb93016c5e3e00dc8fb74ba03d1b8534013 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -179,8 +179,9 @@ public class CommSystemFacadeImpl extends CommSystemFacade { /** @return non-null, possibly empty */ @Override public Set<RouterAddress> createAddresses() { - if (_context.router().isHidden()) - return Collections.EMPTY_SET; + // No, don't do this, it makes it almost impossible to build inbound tunnels + //if (_context.router().isHidden()) + // return Collections.EMPTY_SET; Map<String, RouterAddress> addresses = null; boolean newCreated = false;