* Timestamper:

- Move from core to router, leave stub in core
     so it doesn't break compatibility. This removes a
     thread in app context and prevents any app context from
     running NTP; external clients must use the time
     received from the router.
   - Increase query interval
This commit is contained in:
zzz
2012-05-30 20:03:30 +00:00
parent ddc329e8f1
commit f14ff31a20
12 changed files with 402 additions and 345 deletions

View File

@ -61,7 +61,6 @@ public class ConfigNetHandler extends FormHandler {
}
public void setSave(String moo) { _saveRequested = true; }
public void setEnabletimesync(String moo) { }
public void setRecheckReachability(String moo) { _recheckReachabilityRequested = true; }
public void setRequireIntroductions(String moo) { _requireIntroductions = true; }
public void setDynamicKeys(String moo) { _dynamicKeys = true; }
@ -289,10 +288,6 @@ public class ConfigNetHandler extends FormHandler {
removes.add(UDPTransport.PROP_FORCE_INTRODUCERS);
}
// Time sync enable, means NOT disabled
// Hmm router sets this at startup, not required here
//changes.put(Timestamper.PROP_DISABLED, "false");
// Hidden in the GUI
//LoadTestManager.setEnableLoadTesting(_context, _enableLoadTesting);
}

View File

@ -9,7 +9,6 @@ import net.i2p.router.Router;
import net.i2p.router.transport.TransportManager;
import net.i2p.router.transport.udp.UDPAddress;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.time.Timestamper;
import net.i2p.util.Addresses;
public class ConfigNetHelper extends HelperBase {
@ -65,14 +64,6 @@ public class ConfigNetHelper extends HelperBase {
return "" + _context.getProperty(UDPTransport.PROP_INTERNAL_PORT, UDPTransport.DEFAULT_INTERNAL_PORT);
}
public String getEnableTimeSyncChecked() {
boolean disabled = _context.getBooleanProperty(Timestamper.PROP_DISABLED);
if (disabled)
return "";
else
return CHECKED;
}
/** @param prop must default to false */
public String getChecked(String prop) {
if (_context.getBooleanProperty(prop))

View File

@ -1,336 +1,31 @@
package net.i2p.time;
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;
import net.i2p.util.Log;
/**
* Periodically query a series of NTP servers and update any associated
* listeners. It tries the NTP servers in order, contacting them using
* SNTP (UDP port 123). By default, it does this every 5 minutes,
* forever.
* Dummy. Real thing moved to net.i2p.router.time.RouterTimestamper.
* What remains here is essentially an interface,
* containing only what is needed to keep external apps
* compiled with old libs from breaking, since
* net.i2p.util.Clock returns a Timestamper in getTimestamper()
*
* Deprecated outside of the router.
*/
public class Timestamper implements Runnable {
private final I2PAppContext _context;
private Log _log;
private final List<String> _servers;
private List<String> _priorityServers;
private final List<UpdateListener> _listeners;
private int _queryFrequency;
private int _concurringServers;
private int _consecutiveFails;
private volatile boolean _disabled;
private final boolean _daemon;
private boolean _initialized;
private boolean _wellSynced;
private volatile boolean _isRunning;
private Thread _timestamperThread;
private static final int MIN_QUERY_FREQUENCY = 5*60*1000;
private static final int DEFAULT_QUERY_FREQUENCY = 5*60*1000;
private static final String DEFAULT_SERVER_LIST = "0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org";
private static final String DEFAULT_DISABLED = "true";
/** how many times do we have to query if we are changing the clock? */
private static final int DEFAULT_CONCURRING_SERVERS = 3;
private static final int MAX_CONSECUTIVE_FAILS = 10;
/** dummy */
public Timestamper() {}
public static final String PROP_QUERY_FREQUENCY = "time.queryFrequencyMs";
public static final String PROP_SERVER_LIST = "time.sntpServerList";
public static final String PROP_DISABLED = "time.disabled";
public static final String PROP_CONCURRING_SERVERS = "time.concurringServers";
public static final String PROP_IP_COUNTRY = "i2np.lastCountry";
/** if different SNTP servers differ by more than 10s, someone is b0rked */
private static final int MAX_VARIANCE = 10*1000;
public Timestamper(I2PAppContext ctx) {
this(ctx, null, true);
}
public Timestamper(I2PAppContext ctx, UpdateListener lsnr) {
this(ctx, lsnr, true);
}
public Timestamper(I2PAppContext ctx, UpdateListener lsnr, boolean daemon) {
// moved here to prevent problems with synchronized statements.
_servers = new ArrayList(3);
_listeners = new CopyOnWriteArrayList();
_context = ctx;
_daemon = daemon;
// DO NOT initialize _log here, stack overflow via LogManager init loop
// 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.
// We still need to be instantiated since the router calls clock().getTimestamper().waitForInitialization()
String disabled = ctx.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
if (Boolean.valueOf(disabled).booleanValue()) {
_initialized = true;
return;
}
if (lsnr != null)
_listeners.add(lsnr);
updateConfig();
startTimestamper();
}
public int getServerCount() {
synchronized (_servers) {
return _servers.size();
}
}
public String getServer(int index) {
synchronized (_servers) {
return _servers.get(index);
}
}
public int getQueryFrequencyMs() { return _queryFrequency; }
public boolean getIsDisabled() { return _disabled; }
public void addListener(UpdateListener lsnr) {
_listeners.add(lsnr);
}
public void removeListener(UpdateListener lsnr) {
_listeners.remove(lsnr);
}
public int getListenerCount() {
return _listeners.size();
}
public UpdateListener getListener(int index) {
return _listeners.get(index);
}
private void startTimestamper() {
_timestamperThread = new I2PThread(this, "Timestamper", _daemon);
_timestamperThread.setPriority(I2PThread.MIN_PRIORITY);
_isRunning = true;
_timestamperThread.start();
_context.addShutdownTask(new Shutdown());
}
public void waitForInitialization() {
try {
synchronized (this) {
if (!_initialized)
wait();
}
} catch (InterruptedException ie) {}
}
/** dummy */
public void waitForInitialization() {}
/**
* Update the time immediately.
* Dummy
* @since 0.8.8
*/
public void timestampNow() {
if (_initialized && _isRunning && (!_disabled) && _timestamperThread != null)
_timestamperThread.interrupt();
}
public void timestampNow() {}
/** @since 0.8.8 */
private class Shutdown implements Runnable {
public void run() {
_isRunning = false;
if (_timestamperThread != null)
_timestamperThread.interrupt();
}
}
public void run() {
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
_log = _context.logManager().getLog(Timestamper.class);
if (_log.shouldLog(Log.INFO))
_log.info("Starting timestamper");
boolean lastFailed = false;
try {
while (_isRunning) {
updateConfig();
if (!_disabled) {
// first the servers for our country, if we know what country we're in...
if (_priorityServers != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Querying servers " + _priorityServers);
try {
lastFailed = !queryTime(_priorityServers.toArray(new String[_priorityServers.size()]));
} catch (IllegalArgumentException iae) {
if ( (!lastFailed) && (_log.shouldLog(Log.WARN)) )
_log.warn("Unable to reach country-specific NTP servers");
lastFailed = true;
}
}
// ... and then the global list, if that failed
if (_priorityServers == null || lastFailed) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Querying servers " + _servers);
try {
lastFailed = !queryTime(_servers.toArray(new String[_servers.size()]));
} catch (IllegalArgumentException iae) {
if ( (!_initialized) && (_log.shouldLog(Log.ERROR)) ) {
List<String> all = new ArrayList();
if (_priorityServers != null)
all.addAll(_priorityServers);
all.addAll(_servers);
_log.error("Unable to reach any of the NTP servers " + all + " - network disconnected? Or set time.sntpServerList=myserver1.com,myserver2.com in advanced configuration.");
}
lastFailed = true;
}
}
}
_initialized = true;
synchronized (this) { notifyAll(); }
long sleepTime;
if (lastFailed) {
if (++_consecutiveFails >= MAX_CONSECUTIVE_FAILS)
sleepTime = 30*60*1000;
else
sleepTime = 30*1000;
} else {
_consecutiveFails = 0;
sleepTime = _context.random().nextInt(_queryFrequency) + _queryFrequency;
if (_wellSynced)
sleepTime *= 3;
}
try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {}
}
} catch (Throwable t) {
_log.log(Log.CRIT, "Timestamper died!", t);
synchronized (this) { notifyAll(); }
}
}
/**
* True if the time was queried successfully, false if it couldn't be
*/
private boolean queryTime(String serverList[]) throws IllegalArgumentException {
long found[] = new long[_concurringServers];
long now = -1;
int stratum = -1;
long expectedDelta = 0;
_wellSynced = false;
for (int i = 0; i < _concurringServers; i++) {
if (i > 0) {
// this delays startup when net is disconnected or the timeserver list is bad, don't make it too long
try { Thread.sleep(2*1000); } catch (InterruptedException ie) {}
}
long[] timeAndStratum = NtpClient.currentTimeAndStratum(serverList);
now = timeAndStratum[0];
stratum = (int) timeAndStratum[1];
long delta = now - _context.clock().now();
found[i] = delta;
if (i == 0) {
if (Math.abs(delta) < MAX_VARIANCE) {
if (_log.shouldLog(Log.INFO))
_log.info("a single SNTP query was within the tolerance (" + delta + "ms)");
// If less than a half second on the first try, we're in good shape
_wellSynced = Math.abs(delta) < 500;
break;
} else {
// outside the tolerance, lets iterate across the concurring queries
expectedDelta = delta;
}
} else {
if (Math.abs(delta - expectedDelta) > MAX_VARIANCE) {
if (_log.shouldLog(Log.ERROR)) {
StringBuilder err = new StringBuilder(96);
err.append("SNTP client variance exceeded at query ").append(i);
err.append(". expected = ");
err.append(expectedDelta);
err.append(", found = ");
err.append(delta);
err.append(" all deltas: ");
for (int j = 0; j < found.length; j++)
err.append(found[j]).append(' ');
_log.error(err.toString());
}
return false;
}
}
}
stampTime(now, stratum);
if (_log.shouldLog(Log.DEBUG)) {
StringBuilder buf = new StringBuilder(64);
buf.append("Deltas: ");
for (int i = 0; i < found.length; i++)
buf.append(found[i]).append(' ');
_log.debug(buf.toString());
}
return true;
}
/**
* Notify the listeners
*
* @since stratum param added in 0.7.12
*/
private void stampTime(long now, int stratum) {
long before = _context.clock().now();
for (UpdateListener lsnr : _listeners) {
lsnr.setNow(now, stratum);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Stamped the time as " + now + " (delta=" + (now-before) + ")");
}
/**
* Reload all the config elements from the appContext
*
*/
private void updateConfig() {
String serverList = _context.getProperty(PROP_SERVER_LIST);
if ( (serverList == null) || (serverList.trim().length() <= 0) ) {
serverList = DEFAULT_SERVER_LIST;
String country = _context.getProperty(PROP_IP_COUNTRY);
if (country == null) {
country = Locale.getDefault().getCountry();
if (country != null)
country = country.toLowerCase(Locale.US);
}
if (country != null && country.length() > 0) {
_priorityServers = new ArrayList(3);
for (int i = 0; i < 3; i++)
_priorityServers.add(i + "." + country + ".pool.ntp.org");
} else {
_priorityServers = null;
}
} else {
_priorityServers = null;
}
_servers.clear();
StringTokenizer tok = new StringTokenizer(serverList, ", ");
while (tok.hasMoreTokens()) {
String val = tok.nextToken();
val = val.trim();
if (val.length() > 0)
_servers.add(val);
}
_queryFrequency = Math.max(MIN_QUERY_FREQUENCY,
_context.getProperty(PROP_QUERY_FREQUENCY, DEFAULT_QUERY_FREQUENCY));
String disabled = _context.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
_disabled = Boolean.valueOf(disabled).booleanValue();
_concurringServers = Math.min(4, Math.max(1,
_context.getProperty(PROP_CONCURRING_SERVERS, DEFAULT_CONCURRING_SERVERS)));
}
/****
public static void main(String args[]) {
System.setProperty(PROP_DISABLED, "false");
System.setProperty(PROP_QUERY_FREQUENCY, "30000");
I2PAppContext.getGlobalContext();
for (int i = 0; i < 5*60*1000; i += 61*1000) {
try { Thread.sleep(61*1000); } catch (InterruptedException ie) {}
}
}
****/
/** dummy */
public void run() {}
/**
* Interface to receive update notifications for when we query the time

View File

@ -1,7 +1,8 @@
<html>
<body>
<p>
Provides classes for time synchronisation using NTP.
Provides a stub class for time synchronization.
Full implementation is now in net.i2p.router.time.
</p>
</body>
</html>

View File

@ -19,7 +19,6 @@ import net.i2p.time.Timestamper;
*/
public class Clock implements Timestamper.UpdateListener {
protected final I2PAppContext _context;
private final Timestamper _timestamper;
protected long _startedOn;
protected boolean _statCreated;
protected volatile long _offset;
@ -29,7 +28,6 @@ public class Clock implements Timestamper.UpdateListener {
public Clock(I2PAppContext context) {
_context = context;
_listeners = new CopyOnWriteArraySet();
_timestamper = new Timestamper(context, this);
_startedOn = System.currentTimeMillis();
}
@ -37,7 +35,10 @@ public class Clock implements Timestamper.UpdateListener {
return I2PAppContext.getGlobalContext().clock();
}
public Timestamper getTimestamper() { return _timestamper; }
/**
* This is a dummy, see RouterClock and RouterTimestamper for the real thing
*/
public Timestamper getTimestamper() { return new Timestamper(); }
/** we fetch it on demand to avoid circular dependencies (logging uses the clock) */
protected Log getLog() { return _context.logManager().getLog(Clock.class); }

View File

@ -1,3 +1,18 @@
2012-05-30 zzz
* Graphs: Reduce log EOF error to warn
* i2psnark:
- Increase max upload (ticket #645)
- Increase per-minute conn limit from 6 to 8
- Improve rarest-first behavior
- Handle URI encoding, UTF-8, and multiple trackers in magnet links
* Timestamper:
- Move from core to router, leave stub in core
so it doesn't break compatibility. This removes a
thread in app context and prevents any app context from
running NTP; external clients must use the time
received from the router.
- Increase query interval
2012-05-28 kytv
* i2prouter: Add support so that 'i2prouter install' will work in ArchLinux
* jbigi/jcpuid scripts: Improve support for ArchLinux

View File

@ -4,6 +4,8 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import net.i2p.data.DataHelper;
import net.i2p.router.time.RouterTimestamper;
import net.i2p.time.Timestamper;
import net.i2p.util.Clock;
import net.i2p.util.Log;
@ -38,6 +40,7 @@ public class RouterClock extends Clock {
/** use system time for this */
private long _lastChanged;
private int _lastStratum;
private final Timestamper _timeStamper;
/**
* If the system clock shifts by this much,
@ -56,8 +59,15 @@ public class RouterClock extends Clock {
_lastSlewed = System.currentTimeMillis();
_shiftListeners = new CopyOnWriteArraySet();
_lastShiftNanos = System.nanoTime();
_timeStamper = new RouterTimestamper(context, this);
}
/**
* The RouterTimestamper
*/
@Override
public Timestamper getTimestamper() { return _timeStamper; }
/**
* 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.

View File

@ -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 = 6;
public final static long BUILD = 7;
/** for example "-test" */
public final static String EXTRA = "";

View File

@ -1,4 +1,4 @@
package net.i2p.time;
package net.i2p.router.time;
/*
* Copyright (c) 2004, Adam Buckley
* All rights reserved.
@ -49,8 +49,9 @@ import java.util.Collections;
*
* @author Adam Buckley
* (minor refactoring by jrandom)
* @since 0.9.1 moved from net.i2p.time
*/
public class NtpClient {
class NtpClient {
/** difference between the unix epoch and jan 1 1900 (NTP uses that) */
private final static double SECONDS_1900_TO_EPOCH = 2208988800.0;
private final static int NTP_PORT = 123;

View File

@ -1,4 +1,4 @@
package net.i2p.time;
package net.i2p.router.time;
/*
* Copyright (c) 2004, Adam Buckley
* All rights reserved.
@ -72,8 +72,9 @@ import net.i2p.util.RandomSource;
* NTPMessage.java which is copyright (c) 2003 by Juliusz Chroboczek
*
* @author Adam Buckley
* @since 0.9.1 moved from net.i2p.time
*/
public class NtpMessage {
class NtpMessage {
/**
* This is a two-bit code warning of an impending leap second to be
* inserted/deleted in the last minute of the current day. It's values

View File

@ -0,0 +1,340 @@
package net.i2p.router.time;
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.time.Timestamper;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Periodically query a series of NTP servers and update any associated
* listeners. It tries the NTP servers in order, contacting them using
* SNTP (UDP port 123).
*
* @since 0.9.1 moved from net.i2p.time
*/
public class RouterTimestamper extends Timestamper {
private final I2PAppContext _context;
private Log _log;
private final List<String> _servers;
private List<String> _priorityServers;
private final List<UpdateListener> _listeners;
private int _queryFrequency;
private int _concurringServers;
private int _consecutiveFails;
private volatile boolean _disabled;
private final boolean _daemon;
private boolean _initialized;
private boolean _wellSynced;
private volatile boolean _isRunning;
private Thread _timestamperThread;
private static final int MIN_QUERY_FREQUENCY = 5*60*1000;
private static final int DEFAULT_QUERY_FREQUENCY = 11*60*1000;
private static final String DEFAULT_SERVER_LIST = "0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org";
private static final String DEFAULT_DISABLED = "true";
/** how many times do we have to query if we are changing the clock? */
private static final int DEFAULT_CONCURRING_SERVERS = 3;
private static final int MAX_CONSECUTIVE_FAILS = 10;
public static final String PROP_QUERY_FREQUENCY = "time.queryFrequencyMs";
public static final String PROP_SERVER_LIST = "time.sntpServerList";
public static final String PROP_DISABLED = "time.disabled";
public static final String PROP_CONCURRING_SERVERS = "time.concurringServers";
public static final String PROP_IP_COUNTRY = "i2np.lastCountry";
/** if different SNTP servers differ by more than 10s, someone is b0rked */
private static final int MAX_VARIANCE = 10*1000;
public RouterTimestamper(I2PAppContext ctx) {
this(ctx, null, true);
}
public RouterTimestamper(I2PAppContext ctx, UpdateListener lsnr) {
this(ctx, lsnr, true);
}
public RouterTimestamper(I2PAppContext ctx, UpdateListener lsnr, boolean daemon) {
super();
// moved here to prevent problems with synchronized statements.
_servers = new ArrayList(3);
_listeners = new CopyOnWriteArrayList();
_context = ctx;
_daemon = daemon;
// DO NOT initialize _log here, stack overflow via LogManager init loop
// 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.
// We still need to be instantiated since the router calls clock().getTimestamper().waitForInitialization()
String disabled = ctx.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
if (Boolean.valueOf(disabled).booleanValue()) {
_initialized = true;
return;
}
if (lsnr != null)
_listeners.add(lsnr);
updateConfig();
startTimestamper();
}
public int getServerCount() {
synchronized (_servers) {
return _servers.size();
}
}
public String getServer(int index) {
synchronized (_servers) {
return _servers.get(index);
}
}
public int getQueryFrequencyMs() { return _queryFrequency; }
public boolean getIsDisabled() { return _disabled; }
public void addListener(UpdateListener lsnr) {
_listeners.add(lsnr);
}
public void removeListener(UpdateListener lsnr) {
_listeners.remove(lsnr);
}
public int getListenerCount() {
return _listeners.size();
}
public UpdateListener getListener(int index) {
return _listeners.get(index);
}
private void startTimestamper() {
_timestamperThread = new I2PThread(this, "Timestamper", _daemon);
_timestamperThread.setPriority(I2PThread.MIN_PRIORITY);
_isRunning = true;
_timestamperThread.start();
_context.addShutdownTask(new Shutdown());
}
@Override
public void waitForInitialization() {
try {
synchronized (this) {
if (!_initialized)
wait();
}
} catch (InterruptedException ie) {}
}
/**
* Update the time immediately.
* @since 0.8.8
*/
@Override
public void timestampNow() {
if (_initialized && _isRunning && (!_disabled) && _timestamperThread != null)
_timestamperThread.interrupt();
}
/** @since 0.8.8 */
private class Shutdown implements Runnable {
public void run() {
_isRunning = false;
if (_timestamperThread != null)
_timestamperThread.interrupt();
}
}
@Override
public void run() {
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
_log = _context.logManager().getLog(Timestamper.class);
if (_log.shouldLog(Log.INFO))
_log.info("Starting timestamper");
boolean lastFailed = false;
try {
while (_isRunning) {
updateConfig();
if (!_disabled) {
// first the servers for our country, if we know what country we're in...
if (_priorityServers != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Querying servers " + _priorityServers);
try {
lastFailed = !queryTime(_priorityServers.toArray(new String[_priorityServers.size()]));
} catch (IllegalArgumentException iae) {
if ( (!lastFailed) && (_log.shouldLog(Log.WARN)) )
_log.warn("Unable to reach country-specific NTP servers");
lastFailed = true;
}
}
// ... and then the global list, if that failed
if (_priorityServers == null || lastFailed) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Querying servers " + _servers);
try {
lastFailed = !queryTime(_servers.toArray(new String[_servers.size()]));
} catch (IllegalArgumentException iae) {
if ( (!_initialized) && (_log.shouldLog(Log.ERROR)) ) {
List<String> all = new ArrayList();
if (_priorityServers != null)
all.addAll(_priorityServers);
all.addAll(_servers);
_log.error("Unable to reach any of the NTP servers " + all + " - network disconnected? Or set time.sntpServerList=myserver1.com,myserver2.com in advanced configuration.");
}
lastFailed = true;
}
}
}
_initialized = true;
synchronized (this) { notifyAll(); }
long sleepTime;
if (lastFailed) {
if (++_consecutiveFails >= MAX_CONSECUTIVE_FAILS)
sleepTime = 30*60*1000;
else
sleepTime = 30*1000;
} else {
_consecutiveFails = 0;
sleepTime = _context.random().nextInt(_queryFrequency / 2) + _queryFrequency;
if (_wellSynced)
sleepTime *= 3;
}
try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {}
}
} catch (Throwable t) {
_log.log(Log.CRIT, "Timestamper died!", t);
synchronized (this) { notifyAll(); }
}
}
/**
* True if the time was queried successfully, false if it couldn't be
*/
private boolean queryTime(String serverList[]) throws IllegalArgumentException {
long found[] = new long[_concurringServers];
long now = -1;
int stratum = -1;
long expectedDelta = 0;
_wellSynced = false;
for (int i = 0; i < _concurringServers; i++) {
if (i > 0) {
// this delays startup when net is disconnected or the timeserver list is bad, don't make it too long
try { Thread.sleep(2*1000); } catch (InterruptedException ie) {}
}
long[] timeAndStratum = NtpClient.currentTimeAndStratum(serverList);
now = timeAndStratum[0];
stratum = (int) timeAndStratum[1];
long delta = now - _context.clock().now();
found[i] = delta;
if (i == 0) {
if (Math.abs(delta) < MAX_VARIANCE) {
if (_log.shouldLog(Log.INFO))
_log.info("a single SNTP query was within the tolerance (" + delta + "ms)");
// If less than a half second on the first try, we're in good shape
_wellSynced = Math.abs(delta) < 500;
break;
} else {
// outside the tolerance, lets iterate across the concurring queries
expectedDelta = delta;
}
} else {
if (Math.abs(delta - expectedDelta) > MAX_VARIANCE) {
if (_log.shouldLog(Log.ERROR)) {
StringBuilder err = new StringBuilder(96);
err.append("SNTP client variance exceeded at query ").append(i);
err.append(". expected = ");
err.append(expectedDelta);
err.append(", found = ");
err.append(delta);
err.append(" all deltas: ");
for (int j = 0; j < found.length; j++)
err.append(found[j]).append(' ');
_log.error(err.toString());
}
return false;
}
}
}
stampTime(now, stratum);
if (_log.shouldLog(Log.DEBUG)) {
StringBuilder buf = new StringBuilder(64);
buf.append("Deltas: ");
for (int i = 0; i < found.length; i++)
buf.append(found[i]).append(' ');
_log.debug(buf.toString());
}
return true;
}
/**
* Notify the listeners
*
* @since stratum param added in 0.7.12
*/
private void stampTime(long now, int stratum) {
long before = _context.clock().now();
for (UpdateListener lsnr : _listeners) {
lsnr.setNow(now, stratum);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Stamped the time as " + now + " (delta=" + (now-before) + ")");
}
/**
* Reload all the config elements from the appContext
*
*/
private void updateConfig() {
String serverList = _context.getProperty(PROP_SERVER_LIST);
if ( (serverList == null) || (serverList.trim().length() <= 0) ) {
serverList = DEFAULT_SERVER_LIST;
String country = _context.getProperty(PROP_IP_COUNTRY);
if (country == null) {
country = Locale.getDefault().getCountry();
if (country != null)
country = country.toLowerCase(Locale.US);
}
if (country != null && country.length() > 0) {
_priorityServers = new ArrayList(3);
for (int i = 0; i < 3; i++)
_priorityServers.add(i + "." + country + ".pool.ntp.org");
} else {
_priorityServers = null;
}
} else {
_priorityServers = null;
}
_servers.clear();
StringTokenizer tok = new StringTokenizer(serverList, ", ");
while (tok.hasMoreTokens()) {
String val = tok.nextToken();
val = val.trim();
if (val.length() > 0)
_servers.add(val);
}
_queryFrequency = Math.max(MIN_QUERY_FREQUENCY,
_context.getProperty(PROP_QUERY_FREQUENCY, DEFAULT_QUERY_FREQUENCY));
String disabled = _context.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
_disabled = Boolean.valueOf(disabled).booleanValue();
_concurringServers = Math.min(4, Math.max(1,
_context.getProperty(PROP_CONCURRING_SERVERS, DEFAULT_CONCURRING_SERVERS)));
}
/****
public static void main(String args[]) {
System.setProperty(PROP_DISABLED, "false");
System.setProperty(PROP_QUERY_FREQUENCY, "30000");
I2PAppContext.getGlobalContext();
for (int i = 0; i < 5*60*1000; i += 61*1000) {
try { Thread.sleep(61*1000); } catch (InterruptedException ie) {}
}
}
****/
}

View File

@ -0,0 +1,7 @@
<html>
<body>
<p>
Provides classes for time synchronization using NTP.
</p>
</body>
</html>