* Clock: Cleanups and javadocs

* EepGet: Cleanups and javadocs
    * Reseed: Use the reseeder as a clock source
This commit is contained in:
zzz
2011-02-11 23:59:10 +00:00
parent 3f3385fdde
commit f11a543233
13 changed files with 169 additions and 102 deletions

View File

@ -59,7 +59,6 @@ public class I2PSocketEepGet extends EepGet {
// public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
super(ctx, false, null, -1, numRetries, minSize, maxSize, outputFile, outputStream, url, true, null, null);
_socketManager = mgr;
_log = ctx.logManager().getLog(I2PSocketEepGet.class);
}
/**

View File

@ -3,11 +3,9 @@ package net.i2p.router.web;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import net.i2p.I2PAppContext;
import net.i2p.crypto.TrustedUpdate;
@ -15,6 +13,7 @@ import net.i2p.data.DataHelper;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
import net.i2p.router.util.RFC822Date;
import net.i2p.util.EepGet;
import net.i2p.util.EepHead;
import net.i2p.util.FileUtil;
@ -73,7 +72,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
if (_lastFetch == 0)
_lastFetch = _lastUpdated;
if (_lastModified == null)
_lastModified = to822Date(_lastFetch);
_lastModified = RFC822Date.to822Date(_lastFetch);
} else {
_lastUpdated = 0;
_lastFetch = 0;
@ -212,7 +211,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
String lastmod = get.getLastModified();
if (lastmod != null) {
if (!(_context.isRouterContext())) return;
long modtime = parse822Date(lastmod);
long modtime = RFC822Date.parse822Date(lastmod);
if (modtime <= 0) return;
String lastUpdate = _context.getProperty(UpdateHandler.PROP_LAST_UPDATE_TIME);
if (lastUpdate == null) {
@ -251,44 +250,6 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
handler.update();
}
/**
* http://jimyjoshi.com/blog/2007/08/rfc822dateparsinginjava.html
* Apparently public domain
* Probably don't need all of these...
*/
private static final SimpleDateFormat rfc822DateFormats[] = new SimpleDateFormat[] {
new SimpleDateFormat("EEE, d MMM yy HH:mm:ss z", Locale.US),
new SimpleDateFormat("EEE, d MMM yy HH:mm z", Locale.US),
new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US),
new SimpleDateFormat("EEE, d MMM yyyy HH:mm z", Locale.US),
new SimpleDateFormat("d MMM yy HH:mm z", Locale.US),
new SimpleDateFormat("d MMM yy HH:mm:ss z", Locale.US),
new SimpleDateFormat("d MMM yyyy HH:mm z", Locale.US),
new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)
};
/**
* new Date(String foo) is deprecated, so let's do this the hard way
*
* @param s non-null
* @return -1 on failure
*/
public static long parse822Date(String s) {
for (int i = 0; i < rfc822DateFormats.length; i++) {
try {
Date date = rfc822DateFormats[i].parse(s);
if (date != null)
return date.getTime();
} catch (ParseException pe) {}
}
return -1;
}
/** @since 0.8.2 */
private static String to822Date(long t) {
return (new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)).format(new Date(t));
}
private static final String VERSION_STRING = "version=\"" + RouterVersion.VERSION + "\"";
private static final String VERSION_PREFIX = "version=\"";
private void checkForUpdates() {

View File

@ -4,6 +4,7 @@ import java.io.File;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.util.RFC822Date;
import net.i2p.util.EepGet;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
@ -101,7 +102,7 @@ public class UnsignedUpdateHandler extends UpdateHandler {
String lastmod = _get.getLastModified();
long modtime = 0;
if (lastmod != null)
modtime = NewsFetcher.parse822Date(lastmod);
modtime = RFC822Date.parse822Date(lastmod);
if (modtime <= 0)
modtime = _context.clock().now();
_context.router().setConfigSetting(PROP_LAST_UPDATE_TIME, "" + modtime);

View File

@ -15,6 +15,7 @@ import net.i2p.data.DataHelper;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
import net.i2p.router.util.RFC822Date;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
@ -271,7 +272,7 @@ public class UpdateHandler {
String lastmod = _get.getLastModified();
long modtime = 0;
if (lastmod != null)
modtime = NewsFetcher.parse822Date(lastmod);
modtime = RFC822Date.parse822Date(lastmod);
if (modtime <= 0)
modtime = _context.clock().now();
_context.router().setConfigSetting(PROP_LAST_UPDATE_TIME, "" + modtime);

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.concurrent.CopyOnWriteArrayList;
import net.i2p.I2PAppContext;
import net.i2p.util.I2PThread;
@ -56,7 +57,7 @@ public class Timestamper implements Runnable {
public Timestamper(I2PAppContext ctx, UpdateListener lsnr, boolean daemon) {
// moved here to prevent problems with synchronized statements.
_servers = new ArrayList(3);
_listeners = new ArrayList(1);
_listeners = new CopyOnWriteArrayList();
// Don't bother starting a thread if we are disabled.
// This means we no longer check every 5 minutes to see if we got enabled,
// so the property must be set at startup.
@ -92,25 +93,17 @@ public class Timestamper implements Runnable {
public boolean getIsDisabled() { return _disabled; }
public void addListener(UpdateListener lsnr) {
synchronized (_listeners) {
_listeners.add(lsnr);
}
}
public void removeListener(UpdateListener lsnr) {
synchronized (_listeners) {
_listeners.remove(lsnr);
}
}
public int getListenerCount() {
synchronized (_listeners) {
return _listeners.size();
}
}
public UpdateListener getListener(int index) {
synchronized (_listeners) {
return _listeners.get(index);
}
}
private void startTimestamper() {
I2PThread t = new I2PThread(this, "Timestamper");
@ -257,12 +250,9 @@ public class Timestamper implements Runnable {
*/
private void stampTime(long now, int stratum) {
long before = _context.clock().now();
synchronized (_listeners) {
for (int i = 0; i < _listeners.size(); i++) {
UpdateListener lsnr = _listeners.get(i);
for (UpdateListener lsnr : _listeners) {
lsnr.setNow(now, stratum);
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Stamped the time as " + now + " (delta=" + (now-before) + ")");
}

View File

@ -49,14 +49,22 @@ public class Clock implements Timestamper.UpdateListener {
/** if the clock skewed changes by less than this, ignore the update (so we don't slide all over the place) */
public final static long MIN_OFFSET_CHANGE = 5 * 1000;
/**
* Specify how far away from the "correct" time the computer is - a positive
* value means that the system time is slow, while a negative value means the system time is fast.
*
* @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
*/
public void setOffset(long offsetMs) {
setOffset(offsetMs, false);
}
/**
* Specify how far away from the "correct" time the computer is - a positive
* value means that we are slow, while a negative value means we are fast.
* value means that the system time is slow, while a negative value means the system time is fast.
* Warning - overridden in RouterClock
*
* @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
*/
public void setOffset(long offsetMs, boolean force) {
if (false) return;
@ -101,6 +109,9 @@ public class Clock implements Timestamper.UpdateListener {
fireOffsetChanged(delta);
}
/*
* @return the current delta from System.currentTimeMillis() in milliseconds
*/
public long getOffset() {
return _offset;
}

View File

@ -28,21 +28,21 @@ import net.i2p.util.InternalSocket;
* Bug: a malformed url http://example.i2p (no trailing '/') fails cryptically
*/
public class EepGet {
protected I2PAppContext _context;
protected Log _log;
protected boolean _shouldProxy;
private String _proxyHost;
private int _proxyPort;
protected int _numRetries;
private long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
private long _maxSize; // applied both against whole responses and chunks
protected String _outputFile;
protected OutputStream _outputStream;
protected final I2PAppContext _context;
protected final Log _log;
protected final boolean _shouldProxy;
private final String _proxyHost;
private final int _proxyPort;
protected final int _numRetries;
private final long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
private final long _maxSize; // applied both against whole responses and chunks
protected final String _outputFile;
protected final OutputStream _outputStream;
/** url we were asked to fetch */
protected String _url;
protected final String _url;
/** the URL we actually fetch from (may differ from the _url in case of redirect) */
protected String _actualURL;
private String _postData;
private final String _postData;
private boolean _allowCaching;
protected final List<StatusListener> _listeners;
@ -106,7 +106,7 @@ public class EepGet {
String outputFile, OutputStream outputStream, String url, boolean allowCaching,
String etag, String lastModified, String postData) {
_context = ctx;
_log = ctx.logManager().getLog(EepGet.class);
_log = ctx.logManager().getLog(getClass());
_shouldProxy = (proxyHost != null) && (proxyHost.length() > 0) && (proxyPort > 0) && shouldProxy;
_proxyHost = proxyHost;
_proxyPort = proxyPort;
@ -118,13 +118,7 @@ public class EepGet {
_url = url;
_actualURL = url;
_postData = postData;
_alreadyTransferred = 0;
_bytesTransferred = 0;
_bytesRemaining = -1;
_currentAttempt = 0;
_transferFailed = false;
_headersRead = false;
_aborted = false;
_fetchHeaderTimeout = CONNECT_TIMEOUT;
_listeners = new ArrayList(1);
_etag = etag;
@ -255,9 +249,9 @@ public class EepGet {
public void attempting(String url);
}
protected class CLIStatusListener implements StatusListener {
private int _markSize;
private int _lineSize;
private long _startedOn;
private final int _markSize;
private final int _lineSize;
private final long _startedOn;
private long _written;
private long _previousWritten;
private long _discarded;
@ -271,9 +265,6 @@ public class EepGet {
public CLIStatusListener(int markSize, int lineSize) {
_markSize = markSize;
_lineSize = lineSize;
_written = 0;
_previousWritten = 0;
_discarded = 0;
_lastComplete = _context.clock().now();
_startedOn = _lastComplete;
_firstTime = true;

View File

@ -24,7 +24,7 @@ public class RouterClock extends Clock {
* All of this is @since 0.7.12
*/
private static final long MAX_SLEW = 50;
private static final int DEFAULT_STRATUM = 8;
public static final int DEFAULT_STRATUM = 8;
private static final int WORST_STRATUM = 16;
/** the max NTP Timestamper delay is 30m right now, make this longer than that */
private static final long MIN_DELAY_FOR_WORSE_STRATUM = 45*60*1000;
@ -44,20 +44,27 @@ public class RouterClock extends Clock {
/**
* Specify how far away from the "correct" time the computer is - a positive
* value means that we are slow, while a negative value means we are fast.
* value means that the system time is slow, while a negative value means the system time is fast.
*
* @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
*/
@Override
public void setOffset(long offsetMs, boolean force) {
setOffset(offsetMs, force, DEFAULT_STRATUM);
}
/** @since 0.7.12 */
/**
* @since 0.7.12
* @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
*/
private void setOffset(long offsetMs, int stratum) {
setOffset(offsetMs, false, stratum);
}
/** @since 0.7.12 */
/**
* @since 0.7.12
* @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
*/
private void setOffset(long offsetMs, boolean force, int stratum) {
long delta = offsetMs - _offset;
if (!force) {
@ -91,7 +98,7 @@ public class RouterClock extends Clock {
}
// If so configured, check sanity of proposed clock offset
if (Boolean.valueOf(_contextRC.getProperty("router.clockOffsetSanityCheck","true")).booleanValue() &&
if (_contextRC.getBooleanPropertyDefaultTrue("router.clockOffsetSanityCheck") &&
_alreadyChanged) {
// Try calculating peer clock skew
@ -192,6 +199,7 @@ public class RouterClock extends Clock {
/*
* How far we still have to slew, for diagnostics
* @since 0.7.12
* @deprecated for debugging only
*/
public long getDeltaOffset() {
return _desiredOffset - _offset;

View File

@ -14,7 +14,10 @@ import java.util.Set;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterClock;
import net.i2p.router.RouterContext;
import net.i2p.router.util.RFC822Date;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
@ -34,6 +37,7 @@ 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;
@ -46,6 +50,10 @@ public class Reseeder {
/**
* NOTE - URLs in both the standard and SSL groups should use the same hostname and path,
* so the reseed process will not download from both.
*
* NOTE - Each seedURL must be a directory, it must end with a '/',
* it can't end with 'index.html', for example. Both because of how individual file
* URLs are constructed, and because SSLEepGet doesn't follow redirects.
*/
public static final String DEFAULT_SEED_URL =
"http://a.netdb.i2p2.de/,http://c.netdb.i2p2.de/," +
@ -98,13 +106,13 @@ public class Reseeder {
private String _proxyHost;
private int _proxyPort;
private SSLEepGet.SSLState _sslState;
private int _gotDate;
private long _attemptStarted;
private static final int MAX_DATE_SETS = 2;
public ReseedRunner() {
_isRunning = false;
System.clearProperty(PROP_ERROR);
System.setProperty(PROP_STATUS, _("Reseeding"));
System.setProperty(PROP_INPROGRESS, "true");
}
public boolean isRunning() { return _isRunning; }
/*
@ -113,6 +121,11 @@ public class Reseeder {
*/
public void run() {
_isRunning = true;
System.clearProperty(PROP_ERROR);
System.setProperty(PROP_STATUS, _("Reseeding"));
System.setProperty(PROP_INPROGRESS, "true");
_attemptStarted = 0;
_gotDate = 0;
_sslState = null; // start fresh
if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
_proxyHost = _context.getProperty(PROP_PROXY_HOST);
@ -152,8 +165,48 @@ public class Reseeder {
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {}
public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
/**
* Use the Date header as a backup time source
*/
public void headerReceived(String url, int attemptNum, String key, String val) {
// We do this more than once, because
// the first SSL handshake may take a while, and it may take the server
// a while to render the index page.
if (_gotDate < MAX_DATE_SETS && "date".equalsIgnoreCase(key) && _attemptStarted > 0) {
long timeRcvd = System.currentTimeMillis();
long serverTime = RFC822Date.parse822Date(val);
if (serverTime > 0) {
// add 500ms since it's 1-sec resolution, and add half the RTT
long now = serverTime + 500 + ((timeRcvd - _attemptStarted) / 2);
long offset = now - _context.clock().now();
if (_context.clock().getUpdatedSuccessfully()) {
// 2nd time better than the first
if (_gotDate > 0)
_context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 2);
else
_context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Reseed adjusting clock by " +
DataHelper.formatDuration(Math.abs(offset)));
} else {
// No peers or NTP yet, this is probably better than the peer average will be for a while
// default stratum - 1, so the peer average is a worse stratum
_context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 1);
_log.logAlways(Log.WARN, "NTP failure, Reseed adjusting clock by " +
DataHelper.formatDuration(Math.abs(offset)));
}
_gotDate++;
}
}
}
/** save the start time */
public void attempting(String url) {
if (_gotDate < MAX_DATE_SETS)
_attemptStarted = System.currentTimeMillis();
}
// End of EepGet status listeners
/**
@ -235,7 +288,8 @@ public class Reseeder {
**/
private int reseedOne(String seedURL, boolean echoStatus) {
try {
final long timeLimit = _context.clock().now() + MAX_TIME_PER_HOST;
// 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."));
System.err.println("Reseeding from " + seedURL);
URL dir = new URL(seedURL);
@ -275,7 +329,7 @@ public class Reseeder {
int errors = 0;
// 200 max from one URL
for (Iterator<String> iter = urlList.iterator();
iter.hasNext() && fetched < 200 && _context.clock().now() < timeLimit; ) {
iter.hasNext() && fetched < 200 && System.currentTimeMillis() < timeLimit; ) {
try {
System.setProperty(PROP_STATUS,
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));

View File

@ -509,7 +509,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
buf.append("<tt>");
boolean found = _context.netDb().lookupRouterInfoLocally(peer) != null;
if (found)
buf.append("<a title=\"").append(_("NetDb entry")).append("\" href=\"netdb.jsp?r=").append(h).append("\">");
buf.append("<a title=\"").append(_("NetDb entry")).append("\" href=\"netdb?r=").append(h).append("\">");
buf.append(h);
if (found)
buf.append("</a>");

View File

@ -399,7 +399,7 @@ class EstablishState {
_context.clock().setOffset(1000 * (_tsB - _tsA), true);
_tsA = _tsB;
if (diff != 0)
_log.error("NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
_log.logAlways(Log.WARN, "NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
} else if (diff >= Router.CLOCK_FUDGE_FACTOR) {
_context.statManager().addRateData("ntcp.invalidOutboundSkew", diff, 0);
_transport.markReachable(_con.getRemotePeer().calculateHash(), false);
@ -617,7 +617,7 @@ class EstablishState {
_context.clock().setOffset(1000 * (_tsB - tsA), true);
tsA = _tsB;
if (diff != 0)
_log.error("NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
_log.logAlways(Log.WARN, "NTP failure, NTCP adjusting clock by " + DataHelper.formatDuration(diff));
} else if (diff >= Router.CLOCK_FUDGE_FACTOR) {
_context.statManager().addRateData("ntcp.invalidInboundSkew", diff, 0);
_transport.markReachable(alice.calculateHash(), true);

View File

@ -473,7 +473,7 @@ class PacketHandler {
// so we have to wait for NTCP to do it
_context.clock().setOffset(0 - skew, true);
if (skew != 0)
_log.error("NTP failure, UDP adjusting clock by " + DataHelper.formatDuration(Math.abs(skew)));
_log.logAlways(Log.WARN, "NTP failure, UDP adjusting clock by " + DataHelper.formatDuration(Math.abs(skew)));
}
if (skew > GRACE_PERIOD) {

View File

@ -0,0 +1,51 @@
package net.i2p.router.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Moved from NewsFetcher
* @since 0.8.5
*/
public abstract class RFC822Date {
/**
* http://jimyjoshi.com/blog/2007/08/rfc822dateparsinginjava.html
* Apparently public domain
* Probably don't need all of these...
*/
private static final SimpleDateFormat rfc822DateFormats[] = new SimpleDateFormat[] {
new SimpleDateFormat("EEE, d MMM yy HH:mm:ss z", Locale.US),
new SimpleDateFormat("EEE, d MMM yy HH:mm z", Locale.US),
new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US),
new SimpleDateFormat("EEE, d MMM yyyy HH:mm z", Locale.US),
new SimpleDateFormat("d MMM yy HH:mm z", Locale.US),
new SimpleDateFormat("d MMM yy HH:mm:ss z", Locale.US),
new SimpleDateFormat("d MMM yyyy HH:mm z", Locale.US),
new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)
};
/**
* new Date(String foo) is deprecated, so let's do this the hard way
*
* @param s non-null
* @return -1 on failure
*/
public static long parse822Date(String s) {
for (int i = 0; i < rfc822DateFormats.length; i++) {
try {
Date date = rfc822DateFormats[i].parse(s);
if (date != null)
return date.getTime();
} catch (ParseException pe) {}
}
return -1;
}
/** @since 0.8.2 */
public static String to822Date(long t) {
return (new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)).format(new Date(t));
}
}