diff --git a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java index 83966140d8..1b295c68ea 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java @@ -101,6 +101,7 @@ public class ConsoleUpdateManager implements UpdateManager { notifyInstalled(NEWS, "", Long.toString(NewsHelper.lastUpdated(_context))); notifyInstalled(ROUTER_SIGNED, "", RouterVersion.VERSION); // hack to init from the current news file... do this before we register Updaters + // This will not kick off any Updaters as none are yet registered (new NewsFetcher(_context, this, Collections.EMPTY_LIST)).checkForUpdates(); for (String plugin : PluginStarter.getPlugins()) { Properties props = PluginStarter.pluginProperties(_context, plugin); @@ -608,6 +609,7 @@ public class ConsoleUpdateManager implements UpdateManager { /** * Called by the Updater, either after check() was called, or it found out on its own. + * Use this if there is only one UpdateMethod; otherwise use the Map method below. * * @param newsSource who told us * @param id plugin name for plugins, ignored otherwise @@ -620,50 +622,95 @@ public class ConsoleUpdateManager implements UpdateManager { UpdateType type, String id, UpdateMethod method, List updateSources, String newVersion, String minVersion) { + return notifyVersionAvailable(task, newsSource, type, id, + Collections.singletonMap(method, updateSources), + newVersion, minVersion); + } + + /** + * Called by the Checker, either after check() was called, or it found out on its own. + * Checkers must use this method if there are multiple UpdateMethods discoverd simultaneously. + * + * @param newsSource who told us + * @param id plugin name for plugins, ignored otherwise + * @param sourceMap Mapping of methods to sources + * @param newVersion The new version available + * @param minVersion The minimum installed version to be able to update to newVersion + * @return true if we didn't know already + * @since 0.9.6 + */ + public boolean notifyVersionAvailable(UpdateTask task, URI newsSource, + UpdateType type, String id, + Map> sourceMap, + String newVersion, String minVersion) { if (type == NEWS) { // shortcut notifyInstalled(NEWS, "", newVersion); return true; } UpdateItem ui = new UpdateItem(type, id); - VersionAvailable newVA = new VersionAvailable(newVersion, minVersion, method, updateSources); - Version old = _installed.get(ui); - if (_log.shouldLog(Log.INFO)) - _log.info("notifyVersionAvailable " + ui + ' ' + newVA + " old: " + old); - if (old != null && old.compareTo(newVA) >= 0) { - if (_log.shouldLog(Log.WARN)) - _log.warn(ui.toString() + ' ' + old + " already installed"); - return false; - } - old = _downloaded.get(ui); - if (old != null && old.compareTo(newVA) >= 0) { - if (_log.shouldLog(Log.WARN)) - _log.warn(ui.toString() + ' ' + old + " already downloaded"); - return false; - } - VersionAvailable oldVA = _available.get(ui); - if (oldVA != null) { - int comp = oldVA.compareTo(newVA); - if (comp > 0) { + boolean shouldUpdate = false; + for (Map.Entry> e : sourceMap.entrySet()) { + UpdateMethod method = e.getKey(); + List updateSources = e.getValue(); + + VersionAvailable newVA = new VersionAvailable(newVersion, minVersion, method, updateSources); + Version old = _installed.get(ui); + if (_log.shouldLog(Log.INFO)) + _log.info("notifyVersionAvailable " + ui + ' ' + newVA + " old: " + old); + if (old != null && old.compareTo(newVA) >= 0) { if (_log.shouldLog(Log.WARN)) - _log.warn(ui.toString() + ' ' + oldVA + " already available"); + _log.warn(ui.toString() + ' ' + old + " already installed"); + // don't bother updating sources return false; } - if (comp == 0) { - if (oldVA.sourceMap.putIfAbsent(method, updateSources) == null) { - if (_log.shouldLog(Log.WARN)) - _log.warn(ui.toString() + ' ' + oldVA + " updated with new source method"); - } else { + old = _downloaded.get(ui); + if (old != null && old.compareTo(newVA) >= 0) { + if (_log.shouldLog(Log.WARN)) + _log.warn(ui.toString() + ' ' + old + " already downloaded"); + // don't bother updating sources + return false; + } + VersionAvailable oldVA = _available.get(ui); + if (oldVA != null) { + int comp = oldVA.compareTo(newVA); + if (comp > 0) { if (_log.shouldLog(Log.WARN)) _log.warn(ui.toString() + ' ' + oldVA + " already available"); - } - return false; + continue; + } else if (comp == 0) { + List oldSources = oldVA.sourceMap.putIfAbsent(method, updateSources); + if (oldSources == null) { + // merge with existing VersionAvailable + // new method + if (_log.shouldLog(Log.WARN)) + _log.warn(ui.toString() + ' ' + oldVA + " updated with new source method"); + } else if (!oldSources.containsAll(updateSources)) { + // merge with existing VersionAvailable + // new sources to existing method + for (URI uri : updateSources) { + if (!oldSources.contains(uri)) { + if (_log.shouldLog(Log.WARN)) + _log.warn(ui.toString() + ' ' + oldVA + " adding " + uri + " to method " + method); + oldSources.add(uri); + } + } + } else { + if (_log.shouldLog(Log.WARN)) + _log.warn(ui.toString() + ' ' + oldVA + " already available"); + } + continue; + } // else new version is newer } - } - if (_log.shouldLog(Log.INFO)) - _log.info(ui.toString() + ' ' + newVA + " now available"); - _available.put(ui, newVA); + // Use the new VersionAvailable + if (_log.shouldLog(Log.INFO)) + _log.info(ui.toString() + ' ' + newVA + " now available"); + _available.put(ui, newVA); + shouldUpdate = true; + } + if (!shouldUpdate) + return false; String msg = null; switch (type) { @@ -679,7 +726,12 @@ public class ConsoleUpdateManager implements UpdateManager { if (shouldInstall() && !(isUpdateInProgress(ROUTER_SIGNED) || isUpdateInProgress(ROUTER_UNSIGNED))) { + if (_log.shouldLog(Log.INFO)) + _log.info("Updating " + ui + " after notify"); update_fromCheck(type, id, DEFAULT_MAX_TIME); + } else { + if (_log.shouldLog(Log.INFO)) + _log.info("Not updating " + ui + ", update disabled or in progress"); } // ConfigUpdateHandler, SummaryHelper, SummaryBarRenderer handle status display break; @@ -890,7 +942,7 @@ public class ConsoleUpdateManager implements UpdateManager { } /** from NewsFetcher */ - private boolean shouldInstall() { + boolean shouldInstall() { String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY); if ("notify".equals(policy) || NewsHelper.dontInstall(_context)) return false; @@ -1049,7 +1101,9 @@ public class ConsoleUpdateManager implements UpdateManager { } static String linkify(String url) { - return "" + url + ""; + String durl = url.length() <= 28 ? url : + url.substring(0, 25) + "…"; + return "" + durl + ""; } /** translate a string */ diff --git a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java index c018efccc2..61ae0354c9 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java @@ -148,21 +148,21 @@ class NewsFetcher extends UpdateRunner { // TODO if minversion > our version, continue // and look for a second entry with clearnet URLs // TODO clearnet URLs, notify with HTTP_CLEARNET and/or HTTPS_CLEARNET - _mgr.notifyVersionAvailable(this, _currentURI, - ROUTER_SIGNED, "", HTTP, - _mgr.getUpdateURLs(ROUTER_SIGNED, "", HTTP), - ver, ""); + Map> sourceMap = new HashMap(4); + sourceMap.put(HTTP, _mgr.getUpdateURLs(ROUTER_SIGNED, "", HTTP)); String key = FileUtil.isPack200Supported() ? SU2_KEY : SUD_KEY; String murl = args.get(key); if (murl != null) { List uris = tokenize(murl); if (!uris.isEmpty()) { Collections.shuffle(uris, _context.random()); - _mgr.notifyVersionAvailable(this, _currentURI, - ROUTER_SIGNED, "", TORRENT, - uris, ver, ""); + sourceMap.put(TORRENT, uris); } } + // notify about all sources at once + _mgr.notifyVersionAvailable(this, _currentURI, + ROUTER_SIGNED, "", sourceMap, + ver, ""); } else { if (_log.shouldLog(Log.DEBUG)) _log.debug("Our version is current"); diff --git a/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java b/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java index 51dd3b8ad4..c8cfd25c12 100644 --- a/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java +++ b/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java @@ -39,6 +39,7 @@ class NewsTimerTask implements SimpleTimer.TimedEvent { private final RouterContext _context; private final Log _log; private final ConsoleUpdateManager _mgr; + private boolean _firstRun = true; private static final long INITIAL_DELAY = 5*60*1000; private static final long RUN_DELAY = 10*60*1000; @@ -64,7 +65,20 @@ class NewsTimerTask implements SimpleTimer.TimedEvent { // nonblocking fetchUnsignedHead(); } + } else if (_firstRun) { + // This covers the case where we got a new news but then shut down before it + // was successfully downloaded, and then restarted within the 36 hour delay + // before fetching news again. + // If we already know about a new version (from ConsoleUpdateManager calling + // NewsFetcher.checkForUpdates() before any Updaters were registered), + // this will fire off an update. + // If disabled this does nothing. + // TODO unsigned too? + if (_mgr.shouldInstall() && + !_mgr.isCheckInProgress() && !_mgr.isUpdateInProgress()) + _mgr.update(ROUTER_SIGNED); } + _firstRun = false; } private boolean shouldFetchNews() { 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 7074ca116c..5a1ff889a9 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -69,9 +69,13 @@ public class PluginStarter implements Runnable { public void run() { if (_context.getBooleanPropertyDefaultTrue("plugins.autoUpdate") && - (!NewsHelper.isUpdateInProgress()) && - (!RouterVersion.VERSION.equals(_context.getProperty("router.previousVersion")))) - updateAll(_context, true); + !NewsHelper.isUpdateInProgress()) { + String prev = _context.getProperty("router.previousVersion"); + if (prev != null && + (new VersionComparator()).compare(RouterVersion.VERSION, prev) > 0) { + updateAll(_context, true); + } + } startPlugins(_context); } diff --git a/core/java/src/net/i2p/update/UpdateManager.java b/core/java/src/net/i2p/update/UpdateManager.java index 760c711ba6..06ecbeac62 100644 --- a/core/java/src/net/i2p/update/UpdateManager.java +++ b/core/java/src/net/i2p/update/UpdateManager.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.Writer; import java.net.URI; import java.util.List; +import java.util.Map; /** * The central resource coordinating updates. @@ -34,6 +35,7 @@ public interface UpdateManager { /** * Called by the Checker, either after check() was called, or it found out on its own. + * Use this if there is only one UpdateMethod; otherwise use the Map method below. * * @param newsSource who told us * @param id plugin name for plugins, ignored otherwise @@ -48,6 +50,23 @@ public interface UpdateManager { UpdateMethod method, List updateSources, String newVersion, String minVersion); + /** + * Called by the Checker, either after check() was called, or it found out on its own. + * Checkers must use this method if there are multiple UpdateMethods discoverd simultaneously. + * + * @param newsSource who told us + * @param id plugin name for plugins, ignored otherwise + * @param sourceMap Mapping of methods to sources + * @param newVersion The new version available + * @param minVersion The minimum installed version to be able to update to newVersion + * @return true if we didn't know already + * @since 0.9.6 + */ + public boolean notifyVersionAvailable(UpdateTask task, URI newsSource, + UpdateType type, String id, + Map> sourceMap, + String newVersion, String minVersion); + /** * Called by the Checker after check() was called and all notifyVersionAvailable() callbacks are finished * @param newer notifyVersionAvailable was called diff --git a/history.txt b/history.txt index b2f35658f4..3b2343cdab 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,17 @@ +2013-04-19 zzz + * AppManager: Register jetty, console, and SAM with manager + * i2psnark: Disable spellcheck in more form fields + * LogManager: Add support for saving properties added in recent releases + * Updates: + - Notify manager about all available update methods at once, so the priority + system works and it doesn't only update via HTTP + - Start router update download at startup if available + - Only check plugins when core version increases, not decreases, so we + don't update plugins when downgrading + - Limit length of URL shown on summary bar + * WorkingDir: Correctly strip DOS line endings while migrating, + to fix eepsite location on 0.9.5 Windows installs (ticket #919) + 2013-04-18 zzz * i2psnark: Fix params after P-R-G * i2ptunnel: Set target=_top in all external links to break out of console iframe diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 7ca1a3ec2c..5a58b19bc2 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 = 10; + public final static long BUILD = 11; /** for example "-test" */ public final static String EXTRA = "";