* Reseeder: Get rid of static instance, root in netDB,

don't use system properties for status
This commit is contained in:
zzz
2012-03-22 19:47:44 +00:00
parent ca57b71266
commit adcd1e8d9a
8 changed files with 182 additions and 63 deletions

View File

@ -20,13 +20,11 @@ public class ConfigReseedHandler extends FormHandler {
if (_action.equals(_("Save changes and reseed now"))) { if (_action.equals(_("Save changes and reseed now"))) {
saveChanges(); saveChanges();
boolean reseedInProgress = Boolean.valueOf(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress")).booleanValue(); if (!_context.netDb().reseedChecker().requestReseed()) {
if (reseedInProgress) {
addFormError(_("Reseeding is already in progress")); addFormError(_("Reseeding is already in progress"));
} else { } else {
// skip the nonce checking in ReseedHandler // skip the nonce checking in ReseedHandler
addFormNotice(_("Starting reseed process")); addFormNotice(_("Starting reseed process"));
(new ReseedHandler(_context)).requestReseed();
} }
return; return;
} }
@ -74,8 +72,15 @@ public class ConfigReseedHandler extends FormHandler {
saveString(Reseeder.PROP_SPROXY_PASSWORD, "spassword"); saveString(Reseeder.PROP_SPROXY_PASSWORD, "spassword");
saveBoolean(Reseeder.PROP_SPROXY_AUTH_ENABLE, "sauth"); saveBoolean(Reseeder.PROP_SPROXY_AUTH_ENABLE, "sauth");
String url = getJettyString("reseedURL"); String url = getJettyString("reseedURL");
if (url != null) if (url != null) {
changes.put(Reseeder.PROP_RESEED_URL, url.trim().replace("\r\n", ",").replace("\n", ",")); 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"); String mode = getJettyString("mode");
boolean req = "1".equals(mode); boolean req = "1".equals(mode);
boolean disabled = "2".equals(mode); boolean disabled = "2".equals(mode);

View File

@ -24,11 +24,7 @@ public class ReseedHandler extends HelperBase {
} }
} }
public void requestReseed() { private void requestReseed() {
synchronized (ReseedHandler.class) { _context.netDb().reseedChecker().requestReseed();
if (_reseedRunner == null)
_reseedRunner = new Reseeder(_context);
_reseedRunner.requestReseed();
}
} }
} }

View File

@ -676,12 +676,12 @@ public class SummaryHelper extends HelperBase {
.append("</a></h4>"); .append("</a></h4>");
} }
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 showing the reseed link is allowed
if (allowReseed()) { if (allowReseed()) {
if (reseedInProgress) { if (reseedInProgress) {
// While reseed occurring, show status message instead // While reseed occurring, show status message instead
buf.append("<i>").append(System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","")).append("</i><br>"); buf.append("<i>").append(_context.netDb().reseedChecker().getStatus()).append("</i><br>");
} else { } else {
// While no reseed occurring, show reseed link // While no reseed occurring, show reseed link
long nonce = _context.random().nextLong(); 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 a new reseed ain't running, and the last reseed had errors, show error message
if (!reseedInProgress) { if (!reseedInProgress) {
String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage",""); String reseedErrorMessage = _context.netDb().reseedChecker().getError();
if (reseedErrorMessage.length() > 0) { if (reseedErrorMessage.length() > 0) {
buf.append("<i>").append(reseedErrorMessage).append("</i><br>"); buf.append("<i>").append(reseedErrorMessage).append("</i><br>");
} }

View File

@ -17,6 +17,7 @@ import net.i2p.data.DatabaseEntry;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.LeaseSet; import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo; import net.i2p.data.RouterInfo;
import net.i2p.router.networkdb.reseed.ReseedChecker;
/** /**
* Defines the mechanism for interacting with I2P's network database * Defines the mechanism for interacting with I2P's network database
@ -73,4 +74,7 @@ public abstract class NetworkDatabaseFacade implements Service {
public Set<LeaseSet> getLeases() { return Collections.EMPTY_SET; } public Set<LeaseSet> getLeases() { return Collections.EMPTY_SET; }
/** public for NetDbRenderer in routerconsole */ /** public for NetDbRenderer in routerconsole */
public Set<RouterInfo> getRouters() { return Collections.EMPTY_SET; } public Set<RouterInfo> getRouters() { return Collections.EMPTY_SET; }
/** @since 0.9 */
public ReseedChecker reseedChecker() { return null; };
} }

View File

@ -42,6 +42,7 @@ import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.DatabaseLookupMessageHandler; import net.i2p.router.networkdb.DatabaseLookupMessageHandler;
import net.i2p.router.networkdb.DatabaseStoreMessageHandler; import net.i2p.router.networkdb.DatabaseStoreMessageHandler;
import net.i2p.router.networkdb.PublishLocalRouterInfoJob; import net.i2p.router.networkdb.PublishLocalRouterInfoJob;
import net.i2p.router.networkdb.reseed.ReseedChecker;
import net.i2p.router.peermanager.PeerProfile; import net.i2p.router.peermanager.PeerProfile;
import net.i2p.util.ConcurrentHashSet; import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log; import net.i2p.util.Log;
@ -67,6 +68,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
private long _lastExploreNew; private long _lastExploreNew;
protected final PeerSelector _peerSelector; protected final PeerSelector _peerSelector;
protected final RouterContext _context; protected final RouterContext _context;
private final ReseedChecker _reseedChecker;
/** /**
* Map of Hash to RepublishLeaseSetJob for leases we'realready managing. * Map of Hash to RepublishLeaseSetJob for leases we'realready managing.
* This is added to when we create a new RepublishLeaseSetJob, and the values are * 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); _publishingLeaseSets = new HashMap(8);
_activeRequests = new HashMap(8); _activeRequests = new HashMap(8);
_enforceNetId = DEFAULT_ENFORCE_NETID; _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.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 }); context.statManager().createRateStat("netDb.exploreKeySet", "how many keys are queued for exploration?", "NetworkDatabase", new long[] { 60*60*1000 });
// following are for StoreJob // following are for StoreJob
@ -167,6 +171,12 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
protected PeerSelector createPeerSelector() { return new PeerSelector(_context); } protected PeerSelector createPeerSelector() { return new PeerSelector(_context); }
public PeerSelector getPeerSelector() { return _peerSelector; } public PeerSelector getPeerSelector() { return _peerSelector; }
/** @since 0.9 */
@Override
public ReseedChecker reseedChecker() {
return _reseedChecker;
}
KBucketSet getKBuckets() { return _kb; } KBucketSet getKBuckets() { return _kb; }
DataStore getDataStore() { return _ds; } DataStore getDataStore() { return _ds; }

View File

@ -382,7 +382,7 @@ class PersistentDataStore extends TransientDataStore {
} }
if (!_alreadyWarned) { if (!_alreadyWarned) {
ReseedChecker.checkReseed(_context, routerCount); _facade.reseedChecker().checkReseed(routerCount);
_alreadyWarned = true; _alreadyWarned = true;
_initialized = true; _initialized = true;
} }

View File

@ -1,6 +1,7 @@
package net.i2p.router.networkdb.reseed; package net.i2p.router.networkdb.reseed;
import java.io.File; import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.util.Log; import net.i2p.util.Log;
@ -19,29 +20,144 @@ import net.i2p.util.Log;
*/ */
public class ReseedChecker { 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; 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) 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, // 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 // 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. // 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 noReseedFile = new File(new File(System.getProperty("user.home")), ".i2pnoreseed");
File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p"); File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p");
File noReseedFileAlt2 = new File(context.getConfigDir(), ".i2pnoreseed"); File noReseedFileAlt2 = new File(_context.getConfigDir(), ".i2pnoreseed");
File noReseedFileAlt3 = new File(context.getConfigDir(), "noreseed.i2p"); File noReseedFileAlt3 = new File(_context.getConfigDir(), "noreseed.i2p");
Log _log = context.logManager().getLog(ReseedChecker.class);
if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) { if (!noReseedFile.exists() && !noReseedFileAlt1.exists() && !noReseedFileAlt2.exists() && !noReseedFileAlt3.exists()) {
if (count <= 1) if (count <= 1)
_log.logAlways(Log.INFO, "Downloading peer router information for a new I2P installation"); _log.logAlways(Log.INFO, "Downloading peer router information for a new I2P installation");
else else
_log.logAlways(Log.WARN, "Very few known peers remaining - reseeding now"); _log.logAlways(Log.WARN, "Very few known peers remaining - reseeding now");
Reseeder reseeder = new Reseeder(context); return requestReseed();
reseeder.requestReseed();
} else { } 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;
}
} }

View File

@ -42,10 +42,9 @@ import net.i2p.util.Translate;
* the router log, and the wrapper log. * the router log, and the wrapper log.
*/ */
public class Reseeder { 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 RouterContext _context;
private final Log _log; private final Log _log;
private final ReseedChecker _checker;
// Reject unreasonably big files, because we download into a ByteArrayOutputStream. // Reject unreasonably big files, because we download into a ByteArrayOutputStream.
private static final long MAX_RESEED_RESPONSE_SIZE = 2 * 1024 * 1024; 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://75.145.125.59/netDb/" + "," +
"https://i2p.mooo.com/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_HOST = "router.reseedProxyHost";
public static final String PROP_PROXY_PORT = "router.reseedProxyPort"; public static final String PROP_PROXY_PORT = "router.reseedProxyPort";
/** @since 0.8.2 */ /** @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_USERNAME = "router.reseedSSLProxy.username";
public static final String PROP_SPROXY_PASSWORD = "router.reseedSSLProxy.password"; public static final String PROP_SPROXY_PASSWORD = "router.reseedSSLProxy.password";
public static final String PROP_SPROXY_AUTH_ENABLE = "router.reseedSSLProxy.authEnable"; public static final String PROP_SPROXY_AUTH_ENABLE = "router.reseedSSLProxy.authEnable";
/** @since 0.9 */
public static final String PROP_DISABLE = "router.reseedDisable";
// from PersistentDataStore // from PersistentDataStore
private static final String ROUTERINFO_PREFIX = "routerInfo-"; private static final String ROUTERINFO_PREFIX = "routerInfo-";
private static final String ROUTERINFO_SUFFIX = ".dat"; private static final String ROUTERINFO_SUFFIX = ".dat";
public Reseeder(RouterContext ctx) { Reseeder(RouterContext ctx, ReseedChecker rc) {
_context = ctx; _context = ctx;
_log = ctx.logManager().getLog(Reseeder.class); _log = ctx.logManager().getLog(Reseeder.class);
_checker = rc;
} }
public void requestReseed() { void requestReseed() {
synchronized (Reseeder.class) { ReseedRunner reseedRunner = new ReseedRunner();
if (_reseedRunner == null)
_reseedRunner = new ReseedRunner();
if (_reseedRunner.isRunning()) {
return;
} else {
// set to daemon so it doesn't hang a shutdown // set to daemon so it doesn't hang a shutdown
Thread reseed = new I2PAppThread(_reseedRunner, "Reseed", true); Thread reseed = new I2PAppThread(reseedRunner, "Reseed", true);
reseed.start(); reseed.start();
} }
}
} private class ReseedRunner implements Runnable, EepGet.StatusListener {
public class ReseedRunner implements Runnable, EepGet.StatusListener {
private boolean _isRunning; private boolean _isRunning;
private String _proxyHost; private String _proxyHost;
private int _proxyPort; private int _proxyPort;
@ -143,20 +132,21 @@ public class Reseeder {
public ReseedRunner() { public ReseedRunner() {
} }
public boolean isRunning() { return _isRunning; }
/* /*
* Do it. * Do it.
* We update PROP_ERROR here.
*/ */
public void run() { public void run() {
try {
run2();
} finally {
_checker.done();
}
}
private void run2() {
_isRunning = true; _isRunning = true;
System.clearProperty(PROP_ERROR); _checker.setError("");
System.setProperty(PROP_STATUS, _("Reseeding")); _checker.setStatus(_("Reseeding"));
System.setProperty(PROP_INPROGRESS, "true");
_attemptStarted = 0;
_gotDate = 0;
_sslState = null; // start fresh
if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) { if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
_proxyHost = _context.getProperty(PROP_PROXY_HOST); _proxyHost = _context.getProperty(PROP_PROXY_HOST);
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1); _proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
@ -165,24 +155,22 @@ public class Reseeder {
int total = reseed(false); int total = reseed(false);
if (total >= 50) { if (total >= 50) {
System.out.println("Reseed complete, " + total + " received"); System.out.println("Reseed complete, " + total + " received");
System.clearProperty(PROP_ERROR); _checker.setError("");
} else if (total > 0) { } else if (total > 0) {
System.out.println("Reseed complete, only " + total + " received"); 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)); "Reseed fetched only {0} routers.", total));
} else { } else {
System.out.println("Reseed failed, check network connection"); System.out.println("Reseed failed, check network connection");
System.out.println( System.out.println(
"Ensure that nothing blocks outbound HTTP, check the logs, " + "Ensure that nothing blocks outbound HTTP, check the logs, " +
"and if nothing helps, read the FAQ about reseeding manually."); "and if nothing helps, read the FAQ about reseeding manually.");
System.setProperty(PROP_ERROR, _("Reseed failed.") + ' ' + _checker.setError(_("Reseed failed.") + ' ' +
_("See {0} for help.", _("See {0} for help.",
"<a target=\"_top\" href=\"/configreseed\">" + _("reseed configuration page") + "</a>")); "<a target=\"_top\" href=\"/configreseed\">" + _("reseed configuration page") + "</a>"));
} }
System.setProperty(PROP_INPROGRESS, "false");
System.clearProperty(PROP_STATUS);
_sslState = null; // don't hold ref
_isRunning = false; _isRunning = false;
_checker.setStatus("");
} }
// EepGet status listeners // EepGet status listeners
@ -311,7 +299,7 @@ public class Reseeder {
* Jetty directory listings are not compatible, as they look like * Jetty directory listings are not compatible, as they look like
* HREF="/full/path/to/routerInfo-... * HREF="/full/path/to/routerInfo-...
* *
* We update PROP_STATUS here. * We update the status here.
* *
* @param echoStatus apparently always false * @param echoStatus apparently always false
* @return count of routerinfos successfully fetched * @return count of routerinfos successfully fetched
@ -320,7 +308,7 @@ public class Reseeder {
try { try {
// Don't use context clock as we may be adjusting the time // Don't use context clock as we may be adjusting the time
final long timeLimit = System.currentTimeMillis() + MAX_TIME_PER_HOST; 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); System.err.println("Reseeding from " + seedURL);
URL dir = new URL(seedURL); URL dir = new URL(seedURL);
byte contentRaw[] = readURL(dir); byte contentRaw[] = readURL(dir);
@ -377,7 +365,7 @@ public class Reseeder {
for (Iterator<String> iter = urlList.iterator(); for (Iterator<String> iter = urlList.iterator();
iter.hasNext() && fetched < 200 && System.currentTimeMillis() < timeLimit; ) { iter.hasNext() && fetched < 200 && System.currentTimeMillis() < timeLimit; ) {
try { try {
System.setProperty(PROP_STATUS, _checker.setStatus(
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors)); _("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
if (!fetchSeed(seedURL, iter.next())) if (!fetchSeed(seedURL, iter.next()))