* EepGet: Fix byte count for bytesTransferred status listeners

(fixes command line status)
    * UpdateHandler:
       - Fix byte count display
       - Display final status on router console
       - Don't allow multiple update jobs to queue up
       - Increase max retries
       - Code cleanup
       - Don't show 'check for update' button when update in progress
       - Enhance error messages
This commit is contained in:
zzz
2008-03-10 16:27:40 +00:00
parent cb41bf6023
commit e6a0c2f4f0
6 changed files with 109 additions and 48 deletions

View File

@ -33,6 +33,7 @@ public class UpdateHandler {
private DecimalFormat _pct = new DecimalFormat("00.0%"); private DecimalFormat _pct = new DecimalFormat("00.0%");
private static final String SIGNED_UPDATE_FILE = "i2pupdate.sud"; private static final String SIGNED_UPDATE_FILE = "i2pupdate.sud";
private static final String PROP_UPDATE_IN_PROGRESS = "net.i2p.router.web.UpdateHandler.updateInProgress";
public UpdateHandler() { public UpdateHandler() {
this(ContextHelper.getContext(null)); this(ContextHelper.getContext(null));
@ -67,21 +68,27 @@ public class UpdateHandler {
} }
public void update() { public void update() {
// don't block waiting for the other one to finish
if ("true".equals(System.getProperty(PROP_UPDATE_IN_PROGRESS, "false"))) {
_log.error("Update already running");
return;
}
synchronized (UpdateHandler.class) { synchronized (UpdateHandler.class) {
if (_updateRunner == null) if (_updateRunner == null)
_updateRunner = new UpdateRunner(); _updateRunner = new UpdateRunner();
if (_updateRunner.isRunning()) { if (_updateRunner.isRunning()) {
return; return;
} else { } else {
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "true"); System.setProperty(PROP_UPDATE_IN_PROGRESS, "true");
I2PThread update = new I2PThread(_updateRunner, "Update"); I2PThread update = new I2PThread(_updateRunner, "Update");
update.start(); update.start();
} }
} }
} }
public String getStatus() { public String getStatus() {
if (_updateRunner == null)
return "";
return _updateRunner.getStatus(); return _updateRunner.getStatus();
} }
@ -89,22 +96,21 @@ public class UpdateHandler {
private boolean _isRunning; private boolean _isRunning;
private String _status; private String _status;
private long _startedOn; private long _startedOn;
private long _written;
public UpdateRunner() { public UpdateRunner() {
_isRunning = false; _isRunning = false;
_status = "<b>Updating</b><br />"; _status = "<b>Updating</b>";
} }
public boolean isRunning() { return _isRunning; } public boolean isRunning() { return _isRunning; }
public String getStatus() { return _status; } public String getStatus() { return _status; }
public void run() { public void run() {
_isRunning = true; _isRunning = true;
update(); update();
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"); System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
_isRunning = false; _isRunning = false;
} }
private void update() { private void update() {
_startedOn = -1; _startedOn = -1;
_status = "<b>Updating</b><br />"; _status = "<b>Updating</b>";
String updateURL = selectUpdateURL(); String updateURL = selectUpdateURL();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Selected update URL: " + updateURL); _log.debug("Selected update URL: " + updateURL);
@ -115,64 +121,65 @@ public class UpdateHandler {
try { try {
proxyPort = Integer.parseInt(port); proxyPort = Integer.parseInt(port);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"); System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
return; return;
} }
try { try {
EepGet get = null; EepGet get = null;
if (shouldProxy) if (shouldProxy)
get = new EepGet(_context, proxyHost, proxyPort, 10, SIGNED_UPDATE_FILE, updateURL, false); get = new EepGet(_context, proxyHost, proxyPort, 20, SIGNED_UPDATE_FILE, updateURL, false);
else else
get = new EepGet(_context, 10, SIGNED_UPDATE_FILE, updateURL, false); get = new EepGet(_context, 1, SIGNED_UPDATE_FILE, updateURL, false);
get.addStatusListener(UpdateRunner.this); get.addStatusListener(UpdateRunner.this);
_startedOn = _context.clock().now(); _startedOn = _context.clock().now();
get.fetch(); get.fetch();
} catch (Throwable t) { } catch (Throwable t) {
_context.logManager().getLog(UpdateHandler.class).error("Error updating", t); _context.logManager().getLog(UpdateHandler.class).error("Error updating", t);
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"); System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
} }
} }
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) { public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Attempt failed on " + url, cause); _log.debug("Attempt failed on " + url, cause);
_written = 0;
// ignored // ignored
} }
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) { public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
_written += currentWrite;
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("<b>Updating</b> "); buf.append("<b>Updating</b> ");
double pct = ((double)alreadyTransferred + (double)_written) / ((double)alreadyTransferred + (double)bytesRemaining); double pct = ((double)alreadyTransferred + (double)currentWrite) /
((double)alreadyTransferred + (double)currentWrite + (double)bytesRemaining);
synchronized (_pct) { synchronized (_pct) {
buf.append(_pct.format(pct)); buf.append(_pct.format(pct));
} }
buf.append(":<br />\n").append(_written+alreadyTransferred); buf.append(":<br />\n" + (currentWrite + alreadyTransferred));
buf.append(" transferred<br />"); buf.append(" transferred");
_status = buf.toString(); _status = buf.toString();
} }
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
_status = "<b>Update downloaded</b><br />"; _status = "<b>Update downloaded</b>";
TrustedUpdate up = new TrustedUpdate(_context); TrustedUpdate up = new TrustedUpdate(_context);
boolean ok = up.migrateVerified(RouterVersion.VERSION, SIGNED_UPDATE_FILE, "i2pupdate.zip"); boolean ok = up.migrateVerified(RouterVersion.VERSION, SIGNED_UPDATE_FILE, "i2pupdate.zip");
File f = new File(SIGNED_UPDATE_FILE); File f = new File(SIGNED_UPDATE_FILE);
f.delete(); f.delete();
if (ok) { if (ok) {
_log.log(Log.CRIT, "Update was VERIFIED, restarting to install it"); _log.log(Log.CRIT, "Update was VERIFIED, restarting to install it");
_status = "<b>Update verified</b><br />Restarting<br />"; _status = "<b>Update verified</b><br />Restarting";
restart(); restart();
} else { } else {
_log.log(Log.CRIT, "Update was INVALID - signing key is not trusted!"); // One other possibility, the version in the file is not sufficient
_status = "<b>Update signing key invalid</b><br />"; // Perhaps need an int return code - but version problem unlikely?
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"); _log.log(Log.CRIT, "Update was INVALID - signing key is not trusted or file is corrupt from " + url);
_status = "<b>Update signing key invalid or file is corrupt from " + url + "</b>";
System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
} }
} }
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) { public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_log.log(Log.CRIT, "Update from " + url + " did not download completely (" + bytesTransferred + " with " _log.log(Log.CRIT, "Update from " + url + " did not download completely (" + bytesTransferred + " with "
+ bytesRemaining + " after " + currentAttempt + " tries)"); + bytesRemaining + " after " + currentAttempt + " tries)");
_status = "<b>Transfer failed</b><br />"; _status = "<b>Transfer failed</b>";
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"); System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
} }
public void headerReceived(String url, int attemptNum, String key, String val) {} public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {} public void attempting(String url) {}

View File

@ -27,7 +27,11 @@
if (prev != null) System.setProperty("net.i2p.router.web.ConfigUpdateHandler.noncePrev", prev); if (prev != null) System.setProperty("net.i2p.router.web.ConfigUpdateHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.ConfigUpdateHandler.nonce", new java.util.Random().nextLong()+""); %> System.setProperty("net.i2p.router.web.ConfigUpdateHandler.nonce", new java.util.Random().nextLong()+""); %>
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce")%>" /> <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce")%>" />
<% if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) { %>
<i>Update In Progress</i><br /><br />
<% } else { %>
<input type="submit" name="action" value="Check for update now" /><br /><br /> <input type="submit" name="action" value="Check for update now" /><br /><br />
<% } %>
News URL: News URL:
<input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"><br /> <input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"><br />
Refresh frequency: Refresh frequency:

View File

@ -16,8 +16,9 @@
<b>Now:</b> <jsp:getProperty name="helper" property="time" /><!--<br /> <b>Now:</b> <jsp:getProperty name="helper" property="time" /><!--<br />
<b>Status:</b> <a href="config.jsp"><jsp:getProperty name="helper" property="reachability" /></a>--><% <b>Status:</b> <a href="config.jsp"><jsp:getProperty name="helper" property="reachability" /></a>--><%
if (helper.updateAvailable()) { if (helper.updateAvailable()) {
// display all the time so we display the final failure message
out.print("<br />" + update.getStatus());
if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) { if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) {
out.print("<br />" + update.getStatus());
} else { } else {
long nonce = new java.util.Random().nextLong(); long nonce = new java.util.Random().nextLong();
String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce"); String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");

View File

@ -215,6 +215,20 @@ public class EepGet {
} }
public static interface StatusListener { public static interface StatusListener {
/**
* alreadyTransferred - total of all attempts, not including currentWrite
* If nonzero on the first call, a partial file of that length was found
* To track _actual_ transfer if the output file could already exist,
* the listener should keep its own counter,
* or subtract the initial alreadyTransferred value.
* currentWrite - since last call to the listener
* bytesTransferred - includes headers, retries, redirects, ...
* bytesRemaining - on this attempt only, currentWrite already subtracted -
* or -1 if chunked encoding or server does not return a length
*
* Total length should be == alreadyTransferred + currentWrite + bytesRemaining for all calls
*
*/
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url); 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 transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified);
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause); public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause);
@ -227,7 +241,9 @@ public class EepGet {
private int _lineSize; private int _lineSize;
private long _startedOn; private long _startedOn;
private long _written; private long _written;
private long _previousWritten;
private long _lastComplete; private long _lastComplete;
private boolean _firstTime;
private DecimalFormat _pct = new DecimalFormat("00.0%"); private DecimalFormat _pct = new DecimalFormat("00.0%");
private DecimalFormat _kbps = new DecimalFormat("###,000.00"); private DecimalFormat _kbps = new DecimalFormat("###,000.00");
public CLIStatusListener() { public CLIStatusListener() {
@ -237,10 +253,19 @@ public class EepGet {
_markSize = markSize; _markSize = markSize;
_lineSize = lineSize; _lineSize = lineSize;
_written = 0; _written = 0;
_previousWritten = 0;
_lastComplete = _context.clock().now(); _lastComplete = _context.clock().now();
_startedOn = _lastComplete; _startedOn = _lastComplete;
_firstTime = true;
} }
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) { public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
if (_firstTime) {
if (alreadyTransferred > 0) {
_previousWritten = alreadyTransferred;
System.out.println("File found with length " + alreadyTransferred + ", resuming");
}
_firstTime = false;
}
for (int i = 0; i < currentWrite; i++) { for (int i = 0; i < currentWrite; i++) {
_written++; _written++;
if ( (_markSize > 0) && (_written % _markSize == 0) ) { if ( (_markSize > 0) && (_written % _markSize == 0) ) {
@ -253,13 +278,14 @@ public class EepGet {
StringBuffer buf = new StringBuffer(50); StringBuffer buf = new StringBuffer(50);
buf.append(" "); buf.append(" ");
if ( bytesRemaining > 0 ) { if ( bytesRemaining > 0 ) {
double pct = ((double)alreadyTransferred + (double)_written) / ((double)alreadyTransferred + (double)bytesRemaining); double pct = ((double)_written + _previousWritten) /
((double)alreadyTransferred + (double)currentWrite + (double)bytesRemaining);
synchronized (_pct) { synchronized (_pct) {
buf.append(_pct.format(pct)); buf.append(_pct.format(pct));
} }
buf.append(": "); buf.append(": ");
} }
buf.append(_written+alreadyTransferred); buf.append(_written);
buf.append(" @ "); buf.append(" @ ");
double lineKBytes = ((double)_markSize * (double)_lineSize)/1024.0d; double lineKBytes = ((double)_markSize * (double)_lineSize)/1024.0d;
double kbps = lineKBytes/((double)timeToSend/1000.0d); double kbps = lineKBytes/((double)timeToSend/1000.0d);
@ -270,7 +296,7 @@ public class EepGet {
buf.append(" / "); buf.append(" / ");
long lifetime = _context.clock().now() - _startedOn; long lifetime = _context.clock().now() - _startedOn;
double lifetimeKBps = (1000.0d*(double)(_written+alreadyTransferred)/((double)lifetime*1024.0d)); double lifetimeKBps = (1000.0d*(double)(_written)/((double)lifetime*1024.0d));
synchronized (_kbps) { synchronized (_kbps) {
buf.append(_kbps.format(lifetimeKBps)); buf.append(_kbps.format(lifetimeKBps));
} }
@ -283,32 +309,40 @@ public class EepGet {
} }
} }
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
long transferred;
if (_firstTime)
transferred = 0;
else
transferred = alreadyTransferred - _previousWritten;
System.out.println(); System.out.println();
System.out.println("== " + new Date()); System.out.println("== " + new Date());
if (notModified) { if (notModified) {
System.out.println("== Source not modified since last download"); System.out.println("== Source not modified since last download");
} else { } else {
if ( bytesRemaining > 0 ) { if ( bytesRemaining > 0 ) {
System.out.println("== Transfer of " + url + " completed with " + (alreadyTransferred+bytesTransferred) System.out.println("== Transfer of " + url + " completed with " + transferred
+ " and " + (bytesRemaining - bytesTransferred) + " remaining"); + " transferred and " + (bytesRemaining - bytesTransferred) + " remaining");
System.out.println("== Output saved to " + outputFile);
} else { } else {
System.out.println("== Transfer of " + url + " completed with " + (alreadyTransferred+bytesTransferred) System.out.println("== Transfer of " + url + " completed with " + transferred
+ " bytes transferred"); + " bytes transferred");
System.out.println("== Output saved to " + outputFile);
} }
if (transferred > 0)
System.out.println("== Output saved to " + outputFile + " (" + alreadyTransferred + " bytes)");
} }
long timeToSend = _context.clock().now() - _startedOn; long timeToSend = _context.clock().now() - _startedOn;
System.out.println("== Transfer time: " + DataHelper.formatDuration(timeToSend)); System.out.println("== Transfer time: " + DataHelper.formatDuration(timeToSend));
System.out.println("== ETag: " + _etag); if (_etag != null)
StringBuffer buf = new StringBuffer(50); System.out.println("== ETag: " + _etag);
buf.append("== Transfer rate: "); if (transferred > 0) {
double kbps = (1000.0d*(double)(_written)/((double)timeToSend*1024.0d)); StringBuffer buf = new StringBuffer(50);
synchronized (_kbps) { buf.append("== Transfer rate: ");
buf.append(_kbps.format(kbps)); double kbps = (1000.0d*(double)(transferred)/((double)timeToSend*1024.0d));
synchronized (_kbps) {
buf.append(_kbps.format(kbps));
}
buf.append("KBps");
System.out.println(buf.toString());
} }
buf.append("KBps");
System.out.println(buf.toString());
} }
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) { public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
System.out.println(); System.out.println();
@ -499,13 +533,8 @@ public class EepGet {
timeout.resetTimer(); timeout.resetTimer();
_out.write(buf, 0, read); _out.write(buf, 0, read);
_bytesTransferred += read; _bytesTransferred += read;
// This seems necessary to properly resume a partial download into a stream, if ((_maxSize > -1) && (_alreadyTransferred + read > _maxSize)) // could transfer a little over maxSize
// as nothing else increments _alreadyTransferred, and there's no file length to check. throw new IOException("Bytes transferred " + (_alreadyTransferred + read) + " violates maximum of " + _maxSize + " bytes");
// Hopefully this won't break compatibility with existing status listeners
// (cause them to behave weird, or show weird numbers).
_alreadyTransferred += read;
if ((_maxSize > -1) && (_alreadyTransferred > _maxSize)) // could transfer a little over maxSize
throw new IOException("Bytes transferred " + _alreadyTransferred + " violates maximum of " + _maxSize + " bytes");
remaining -= read; remaining -= read;
if (remaining==0 && _encodingChunked) { if (remaining==0 && _encodingChunked) {
int char1 = _proxyIn.read(); int char1 = _proxyIn.read();
@ -528,7 +557,9 @@ public class EepGet {
} }
} }
timeout.resetTimer(); timeout.resetTimer();
if (read > 0) if (_bytesRemaining >= read) // else chunked?
_bytesRemaining -= read;
if (read > 0) {
for (int i = 0; i < _listeners.size(); i++) for (int i = 0; i < _listeners.size(); i++)
((StatusListener)_listeners.get(i)).bytesTransferred( ((StatusListener)_listeners.get(i)).bytesTransferred(
_alreadyTransferred, _alreadyTransferred,
@ -536,6 +567,11 @@ public class EepGet {
_bytesTransferred, _bytesTransferred,
_encodingChunked?-1:_bytesRemaining, _encodingChunked?-1:_bytesRemaining,
_url); _url);
// This seems necessary to properly resume a partial download into a stream,
// as nothing else increments _alreadyTransferred, and there's no file length to check.
// Do this after calling the listeners to keep the total correct
_alreadyTransferred += read;
}
} }
if (_out != null) if (_out != null)

View File

@ -1,3 +1,16 @@
2008-03-10 zzz
* EepGet: Fix byte count for bytesTransferred status listeners
(fixes command line status)
* UpdateHandler:
- Fix byte count display
- Display final status on router console
- Don't allow multiple update jobs to queue up
- Increase max retries
- Code cleanup
- Don't show 'check for update' button when update in progress
- Enhance error messages
* NetDb: Comment out published netDb stats disabled for .32
2008-03-08 zzz 2008-03-08 zzz
* ClientPeerSelector: Implement strict ordering of peers, * ClientPeerSelector: Implement strict ordering of peers,
based on XOR distance from a random hash based on XOR distance from a random hash

View File

@ -17,7 +17,7 @@ import net.i2p.CoreVersion;
public class RouterVersion { public class RouterVersion {
public final static String ID = "$Revision: 1.548 $ $Date: 2008-02-10 15:00:00 $"; public final static String ID = "$Revision: 1.548 $ $Date: 2008-02-10 15:00:00 $";
public final static String VERSION = "0.6.1.32"; public final static String VERSION = "0.6.1.32";
public final static long BUILD = 2; public final static long BUILD = 3;
public static void main(String args[]) { public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
System.out.println("Router ID: " + RouterVersion.ID); System.out.println("Router ID: " + RouterVersion.ID);