diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java
index 9d28994f39..53da37bc7a 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java
@@ -20,13 +20,11 @@ public class ConfigReseedHandler extends FormHandler {
if (_action.equals(_("Save changes and reseed now"))) {
saveChanges();
- boolean reseedInProgress = Boolean.valueOf(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress")).booleanValue();
- if (reseedInProgress) {
+ if (!_context.netDb().reseedChecker().requestReseed()) {
addFormError(_("Reseeding is already in progress"));
} else {
// skip the nonce checking in ReseedHandler
addFormNotice(_("Starting reseed process"));
- (new ReseedHandler(_context)).requestReseed();
}
return;
}
@@ -74,8 +72,15 @@ public class ConfigReseedHandler extends FormHandler {
saveString(Reseeder.PROP_SPROXY_PASSWORD, "spassword");
saveBoolean(Reseeder.PROP_SPROXY_AUTH_ENABLE, "sauth");
String url = getJettyString("reseedURL");
- if (url != null)
- changes.put(Reseeder.PROP_RESEED_URL, url.trim().replace("\r\n", ",").replace("\n", ","));
+ if (url != null) {
+ url = url.trim().replace("\r\n", ",").replace("\n", ",");
+ if (url.length() <= 0) {
+ addFormNotice("Restoring default URLs");
+ removes.add(Reseeder.PROP_RESEED_URL);
+ } else {
+ changes.put(Reseeder.PROP_RESEED_URL, url);
+ }
+ }
String mode = getJettyString("mode");
boolean req = "1".equals(mode);
boolean disabled = "2".equals(mode);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java
index 31c5efc413..9d3fce6c4a 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java
@@ -24,11 +24,7 @@ public class ReseedHandler extends HelperBase {
}
}
- public void requestReseed() {
- synchronized (ReseedHandler.class) {
- if (_reseedRunner == null)
- _reseedRunner = new Reseeder(_context);
- _reseedRunner.requestReseed();
- }
+ private void requestReseed() {
+ _context.netDb().reseedChecker().requestReseed();
}
}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index cfafb6c7e6..a24c341991 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -676,12 +676,12 @@ public class SummaryHelper extends HelperBase {
.append("");
}
- boolean reseedInProgress = Boolean.valueOf(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress")).booleanValue();
+ boolean reseedInProgress = _context.netDb().reseedChecker().inProgress();
// If showing the reseed link is allowed
if (allowReseed()) {
if (reseedInProgress) {
// While reseed occurring, show status message instead
- buf.append("").append(System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","")).append("
");
+ buf.append("").append(_context.netDb().reseedChecker().getStatus()).append("
");
} else {
// While no reseed occurring, show reseed link
long nonce = _context.random().nextLong();
@@ -696,7 +696,7 @@ public class SummaryHelper extends HelperBase {
}
// If a new reseed ain't running, and the last reseed had errors, show error message
if (!reseedInProgress) {
- String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
+ String reseedErrorMessage = _context.netDb().reseedChecker().getError();
if (reseedErrorMessage.length() > 0) {
buf.append("").append(reseedErrorMessage).append("
");
}
diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
index 741f949534..f1fbd1b9fd 100644
--- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
@@ -17,6 +17,7 @@ import net.i2p.data.DatabaseEntry;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo;
+import net.i2p.router.networkdb.reseed.ReseedChecker;
/**
* Defines the mechanism for interacting with I2P's network database
@@ -73,4 +74,7 @@ public abstract class NetworkDatabaseFacade implements Service {
public Set getLeases() { return Collections.EMPTY_SET; }
/** public for NetDbRenderer in routerconsole */
public Set getRouters() { return Collections.EMPTY_SET; }
+
+ /** @since 0.9 */
+ public ReseedChecker reseedChecker() { return null; };
}
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
index d49e0a9abd..74dbbc4282 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
@@ -42,6 +42,7 @@ import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.DatabaseLookupMessageHandler;
import net.i2p.router.networkdb.DatabaseStoreMessageHandler;
import net.i2p.router.networkdb.PublishLocalRouterInfoJob;
+import net.i2p.router.networkdb.reseed.ReseedChecker;
import net.i2p.router.peermanager.PeerProfile;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log;
@@ -67,6 +68,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
private long _lastExploreNew;
protected final PeerSelector _peerSelector;
protected final RouterContext _context;
+ private final ReseedChecker _reseedChecker;
+
/**
* Map of Hash to RepublishLeaseSetJob for leases we'realready managing.
* This is added to when we create a new RepublishLeaseSetJob, and the values are
@@ -146,6 +149,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
_publishingLeaseSets = new HashMap(8);
_activeRequests = new HashMap(8);
_enforceNetId = DEFAULT_ENFORCE_NETID;
+ _reseedChecker = new ReseedChecker(context);
context.statManager().createRateStat("netDb.lookupLeaseSetDeferred", "how many lookups are deferred for a single leaseSet lookup?", "NetworkDatabase", new long[] { 60*60*1000 });
context.statManager().createRateStat("netDb.exploreKeySet", "how many keys are queued for exploration?", "NetworkDatabase", new long[] { 60*60*1000 });
// following are for StoreJob
@@ -167,6 +171,12 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
protected PeerSelector createPeerSelector() { return new PeerSelector(_context); }
public PeerSelector getPeerSelector() { return _peerSelector; }
+ /** @since 0.9 */
+ @Override
+ public ReseedChecker reseedChecker() {
+ return _reseedChecker;
+ }
+
KBucketSet getKBuckets() { return _kb; }
DataStore getDataStore() { return _ds; }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
index 6353d7a595..f5881c7dae 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
@@ -382,7 +382,7 @@ class PersistentDataStore extends TransientDataStore {
}
if (!_alreadyWarned) {
- ReseedChecker.checkReseed(_context, routerCount);
+ _facade.reseedChecker().checkReseed(routerCount);
_alreadyWarned = true;
_initialized = true;
}
diff --git a/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java
index ea3e0408d3..efb9b33e21 100644
--- a/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java
+++ b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java
@@ -1,6 +1,7 @@
package net.i2p.router.networkdb.reseed;
import java.io.File;
+import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
@@ -19,29 +20,144 @@ import net.i2p.util.Log;
*/
public class ReseedChecker {
+ private final RouterContext _context;
+ private final Log _log;
+ private final AtomicBoolean _inProgress = new AtomicBoolean();
+ private String _lastStatus = "";
+ private String _lastError = "";
+
private static final int MINIMUM = 15;
- public static void checkReseed(RouterContext context, int count) {
+ /**
+ * All reseeding must be done through this instance.
+ * Access through context.netDb().reseedChecker(), others should not instantiate
+ *
+ * @since 0.9
+ */
+ public ReseedChecker(RouterContext context) {
+ _context = context;
+ _log = context.logManager().getLog(ReseedChecker.class);
+ }
+
+ /**
+ * Check if a reseed is needed, and start it
+ *
+ * @param count current number of known routers
+ * @return true if a reseed was started
+ */
+ public boolean checkReseed(int count) {
if (count >= MINIMUM)
- return;
+ return false;
+
+ if (_context.getBooleanProperty(Reseeder.PROP_DISABLE)) {
+ String s = "Only " + count + " peers remaining but reseed disabled by configuration";
+ _lastError = s;
+ _log.logAlways(Log.WARN, s);
+ return false;
+ }
// we check the i2p installation directory for a flag telling us not to reseed,
// but also check the home directory for that flag too, since new users installing i2p
// don't have an installation directory that they can put the flag in yet.
File noReseedFile = new File(new File(System.getProperty("user.home")), ".i2pnoreseed");
File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p");
- File noReseedFileAlt2 = new File(context.getConfigDir(), ".i2pnoreseed");
- File noReseedFileAlt3 = new File(context.getConfigDir(), "noreseed.i2p");
- Log _log = context.logManager().getLog(ReseedChecker.class);
+ File noReseedFileAlt2 = new File(_context.getConfigDir(), ".i2pnoreseed");
+ File noReseedFileAlt3 = new File(_context.getConfigDir(), "noreseed.i2p");
if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) {
if (count <= 1)
_log.logAlways(Log.INFO, "Downloading peer router information for a new I2P installation");
else
_log.logAlways(Log.WARN, "Very few known peers remaining - reseeding now");
- Reseeder reseeder = new Reseeder(context);
- reseeder.requestReseed();
+ return requestReseed();
} else {
- _log.logAlways(Log.WARN, "Only " + count + " peers remaining but reseed disabled by config file");
+ String s = "Only " + count + " peers remaining but reseed disabled by config file";
+ _lastError = s;
+ _log.logAlways(Log.WARN, s);
+ return false;
}
}
+
+ /**
+ * Start a reseed
+ *
+ * @return true if a reseed was started, false if already in progress
+ * @since 0.9
+ */
+ public boolean requestReseed() {
+ if (_inProgress.compareAndSet(false, true)) {
+ try {
+ Reseeder reseeder = new Reseeder(_context, this);
+ reseeder.requestReseed();
+ return true;
+ } catch (Throwable t) {
+ _log.error("Reseed failed to start", t);
+ done();
+ return false;
+ }
+ } else {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Reseed already in prgress");
+ return false;
+ }
+ }
+
+ /**
+ * Is a reseed in progress?
+ *
+ * @since 0.9
+ */
+ public boolean inProgress() {
+ return _inProgress.get();
+ }
+
+ /**
+ * The reseed is complete
+ *
+ * @since 0.9
+ */
+ void done() {
+ _inProgress.set(false);
+ }
+
+ /**
+ * Status from current reseed attempt,
+ * probably empty if no reseed in progress.
+ *
+ * @return non-null, may be empty
+ * @since 0.9
+ */
+ public String getStatus() {
+ return _lastStatus;
+ }
+
+ /**
+ * Status from current reseed attempt
+ *
+ * @param s non-null, may be empty
+ * @since 0.9
+ */
+ void setStatus(String s) {
+ _lastStatus = s;
+ }
+
+ /**
+ * Error from last or current reseed attempt
+ *
+ * @return non-null, may be empty
+ * @since 0.9
+ */
+ public String getError() {
+ return _lastError;
+ }
+
+ /**
+ * Status from last or current reseed attempt
+ *
+ * @param s non-null, may be empty
+ * @since 0.9
+ */
+ void setError(String s) {
+ _lastError = s;
+ }
+
}
diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
index 0d896bc46f..cdce1e2908 100644
--- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
+++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
@@ -42,10 +42,9 @@ import net.i2p.util.Translate;
* the router log, and the wrapper log.
*/
public class Reseeder {
- /** FIXME don't keep a static reference, store _isRunning some other way */
- private static ReseedRunner _reseedRunner;
private final RouterContext _context;
private final Log _log;
+ private final ReseedChecker _checker;
// Reject unreasonably big files, because we download into a ByteArrayOutputStream.
private static final long MAX_RESEED_RESPONSE_SIZE = 2 * 1024 * 1024;
@@ -81,11 +80,6 @@ public class Reseeder {
"https://75.145.125.59/netDb/" + "," +
"https://i2p.mooo.com/netDb/";
- private static final String PROP_INPROGRESS = "net.i2p.router.web.ReseedHandler.reseedInProgress";
- /** the console shows this message while reseedInProgress == false */
- private static final String PROP_ERROR = "net.i2p.router.web.ReseedHandler.errorMessage";
- /** the console shows this message while reseedInProgress == true */
- private static final String PROP_STATUS = "net.i2p.router.web.ReseedHandler.statusMessage";
public static final String PROP_PROXY_HOST = "router.reseedProxyHost";
public static final String PROP_PROXY_PORT = "router.reseedProxyPort";
/** @since 0.8.2 */
@@ -106,32 +100,27 @@ public class Reseeder {
public static final String PROP_SPROXY_USERNAME = "router.reseedSSLProxy.username";
public static final String PROP_SPROXY_PASSWORD = "router.reseedSSLProxy.password";
public static final String PROP_SPROXY_AUTH_ENABLE = "router.reseedSSLProxy.authEnable";
+ /** @since 0.9 */
+ public static final String PROP_DISABLE = "router.reseedDisable";
// from PersistentDataStore
private static final String ROUTERINFO_PREFIX = "routerInfo-";
private static final String ROUTERINFO_SUFFIX = ".dat";
- public Reseeder(RouterContext ctx) {
+ Reseeder(RouterContext ctx, ReseedChecker rc) {
_context = ctx;
_log = ctx.logManager().getLog(Reseeder.class);
+ _checker = rc;
}
- public void requestReseed() {
- synchronized (Reseeder.class) {
- if (_reseedRunner == null)
- _reseedRunner = new ReseedRunner();
- if (_reseedRunner.isRunning()) {
- return;
- } else {
- // set to daemon so it doesn't hang a shutdown
- Thread reseed = new I2PAppThread(_reseedRunner, "Reseed", true);
- reseed.start();
- }
- }
-
+ void requestReseed() {
+ ReseedRunner reseedRunner = new ReseedRunner();
+ // set to daemon so it doesn't hang a shutdown
+ Thread reseed = new I2PAppThread(reseedRunner, "Reseed", true);
+ reseed.start();
}
- public class ReseedRunner implements Runnable, EepGet.StatusListener {
+ private class ReseedRunner implements Runnable, EepGet.StatusListener {
private boolean _isRunning;
private String _proxyHost;
private int _proxyPort;
@@ -143,20 +132,21 @@ public class Reseeder {
public ReseedRunner() {
}
- public boolean isRunning() { return _isRunning; }
-
/*
* Do it.
- * We update PROP_ERROR here.
*/
public void run() {
+ try {
+ run2();
+ } finally {
+ _checker.done();
+ }
+ }
+
+ private void run2() {
_isRunning = true;
- System.clearProperty(PROP_ERROR);
- System.setProperty(PROP_STATUS, _("Reseeding"));
- System.setProperty(PROP_INPROGRESS, "true");
- _attemptStarted = 0;
- _gotDate = 0;
- _sslState = null; // start fresh
+ _checker.setError("");
+ _checker.setStatus(_("Reseeding"));
if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
_proxyHost = _context.getProperty(PROP_PROXY_HOST);
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
@@ -165,24 +155,22 @@ public class Reseeder {
int total = reseed(false);
if (total >= 50) {
System.out.println("Reseed complete, " + total + " received");
- System.clearProperty(PROP_ERROR);
+ _checker.setError("");
} else if (total > 0) {
System.out.println("Reseed complete, only " + total + " received");
- System.setProperty(PROP_ERROR, ngettext("Reseed fetched only 1 router.",
+ _checker.setError(ngettext("Reseed fetched only 1 router.",
"Reseed fetched only {0} routers.", total));
} else {
System.out.println("Reseed failed, check network connection");
System.out.println(
"Ensure that nothing blocks outbound HTTP, check the logs, " +
"and if nothing helps, read the FAQ about reseeding manually.");
- System.setProperty(PROP_ERROR, _("Reseed failed.") + ' ' +
+ _checker.setError(_("Reseed failed.") + ' ' +
_("See {0} for help.",
"" + _("reseed configuration page") + ""));
}
- System.setProperty(PROP_INPROGRESS, "false");
- System.clearProperty(PROP_STATUS);
- _sslState = null; // don't hold ref
_isRunning = false;
+ _checker.setStatus("");
}
// EepGet status listeners
@@ -311,7 +299,7 @@ public class Reseeder {
* Jetty directory listings are not compatible, as they look like
* HREF="/full/path/to/routerInfo-...
*
- * We update PROP_STATUS here.
+ * We update the status here.
*
* @param echoStatus apparently always false
* @return count of routerinfos successfully fetched
@@ -320,7 +308,7 @@ public class Reseeder {
try {
// Don't use context clock as we may be adjusting the time
final long timeLimit = System.currentTimeMillis() + MAX_TIME_PER_HOST;
- System.setProperty(PROP_STATUS, _("Reseeding: fetching seed URL."));
+ _checker.setStatus(_("Reseeding: fetching seed URL."));
System.err.println("Reseeding from " + seedURL);
URL dir = new URL(seedURL);
byte contentRaw[] = readURL(dir);
@@ -377,7 +365,7 @@ public class Reseeder {
for (Iterator iter = urlList.iterator();
iter.hasNext() && fetched < 200 && System.currentTimeMillis() < timeLimit; ) {
try {
- System.setProperty(PROP_STATUS,
+ _checker.setStatus(
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
if (!fetchSeed(seedURL, iter.next()))