* updated stats:

- sendsPerFailure: how many partial sends we make when they all fail
- timeoutCongestionInbound: describes how much faster than our average speed we were receiving data when each partial send timed out (in Bps)
- timeoutCongestionMessage: our send processing time when each partial send timed out (in ms)
- timeoutCongestionTunnel: our tunnel test time when each partial send timed out (in ms)
- participatingMessagesProcessedActive: # of messages more than the (most recent) average that a tunnel we were participating in transmitted (for tunnels with more than the average)
* updated to use Writer for rendering the console, so we can do partial writes (and hopefully help debug some kooky threading bugs on kaffe)
This commit is contained in:
jrandom
2004-09-29 22:49:19 +00:00
committed by zzz
parent 24966c812f
commit 62ed6c6a58
35 changed files with 355 additions and 241 deletions

View File

@ -2,11 +2,14 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
public class NetDbHelper { public class NetDbHelper {
private RouterContext _context; private RouterContext _context;
private Writer _out;
/** /**
* Configure this bean to query a particular router context * Configure this bean to query a particular router context
* *
@ -23,13 +26,21 @@ public class NetDbHelper {
public NetDbHelper() {} public NetDbHelper() {}
public void setWriter(Writer writer) { _out = writer; }
public String getNetDbSummary() { public String getNetDbSummary() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
try { try {
_context.netDb().renderStatusHTML(baos); if (_out != null) {
_context.netDb().renderStatusHTML(_out);
return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
_context.netDb().renderStatusHTML(new OutputStreamWriter(baos));
return new String(baos.toByteArray());
}
} catch (IOException ioe) { } catch (IOException ioe) {
ioe.printStackTrace(); ioe.printStackTrace();
return "";
} }
return new String(baos.toByteArray());
} }
} }

View File

@ -2,6 +2,8 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List; import java.util.List;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
@ -9,6 +11,7 @@ import net.i2p.router.admin.StatsGenerator;
public class OldConsoleHelper { public class OldConsoleHelper {
private RouterContext _context; private RouterContext _context;
private Writer _out;
/** /**
* Configure this bean to query a particular router context * Configure this bean to query a particular router context
* *
@ -25,11 +28,20 @@ public class OldConsoleHelper {
public OldConsoleHelper() {} public OldConsoleHelper() {}
public void setWriter(Writer writer) {
_out = writer;
}
public String getConsole() { public String getConsole() {
try { try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128*1024); if (_out != null) {
_context.router().renderStatusHTML(baos); _context.router().renderStatusHTML(_out);
return baos.toString(); return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128*1024);
_context.router().renderStatusHTML(new OutputStreamWriter(baos));
return baos.toString();
}
} catch (IOException ioe) { } catch (IOException ioe) {
return "<b>Error rending the console</b>"; return "<b>Error rending the console</b>";
} }
@ -38,9 +50,14 @@ public class OldConsoleHelper {
public String getStats() { public String getStats() {
StatsGenerator gen = new StatsGenerator(_context); StatsGenerator gen = new StatsGenerator(_context);
try { try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024); if (_out != null) {
gen.generateStatsPage(baos); gen.generateStatsPage(_out);
return baos.toString(); return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
gen.generateStatsPage(new OutputStreamWriter(baos));
return baos.toString();
}
} catch (IOException ioe) { } catch (IOException ioe) {
return "<b>Error rending the console</b>"; return "<b>Error rending the console</b>";
} }

View File

@ -2,6 +2,7 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
@ -26,7 +27,7 @@ public class ProfilesHelper {
public String getProfileSummary() { public String getProfileSummary() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(16*1024); ByteArrayOutputStream baos = new ByteArrayOutputStream(16*1024);
try { try {
_context.profileOrganizer().renderStatusHTML(baos); _context.profileOrganizer().renderStatusHTML(new OutputStreamWriter(baos));
} catch (IOException ioe) { } catch (IOException ioe) {
ioe.printStackTrace(); ioe.printStackTrace();
} }
@ -36,7 +37,7 @@ public class ProfilesHelper {
public String getShitlistSummary() { public String getShitlistSummary() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024); ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
try { try {
_context.shitlist().renderStatusHTML(baos); _context.shitlist().renderStatusHTML(new OutputStreamWriter(baos));
} catch (IOException ioe) { } catch (IOException ioe) {
ioe.printStackTrace(); ioe.printStackTrace();
} }

View File

@ -2,6 +2,7 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
@ -334,7 +335,7 @@ public class SummaryHelper {
public String getDestinations() { public String getDestinations() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try { try {
_context.clientManager().renderStatusHTML(baos); _context.clientManager().renderStatusHTML(new OutputStreamWriter(baos));
return new String(baos.toByteArray()); return new String(baos.toByteArray());
} catch (IOException ioe) { } catch (IOException ioe) {
_context.logManager().getLog(SummaryHelper.class).error("Error rendering client info", ioe); _context.logManager().getLog(SummaryHelper.class).error("Error rendering client info", ioe);
@ -397,8 +398,7 @@ public class SummaryHelper {
if (_context == null) if (_context == null)
return "0ms"; return "0ms";
Rate delayRate = _context.statManager().getRate("transport.sendProcessingTime").getRate(60*1000); return _context.throttle().getMessageDelay() + "ms";
return ((int)delayRate.getAverageValue()) + "ms";
} }
/** /**
@ -410,7 +410,6 @@ public class SummaryHelper {
if (_context == null) if (_context == null)
return "0ms"; return "0ms";
Rate lagRate = _context.statManager().getRate("tunnel.testSuccessTime").getRate(10*60*1000); return _context.throttle().getTunnelLag() + "ms";
return ((int)lagRate.getAverageValue()) + "ms";
} }
} }

View File

@ -13,6 +13,7 @@
<div class="main" id="main"> <div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />
<jsp:setProperty name="netdbHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="netdbHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="netdbHelper" property="writer" value="<%=out%>" />
<jsp:getProperty name="netdbHelper" property="netDbSummary" /> <jsp:getProperty name="netdbHelper" property="netDbSummary" />
</div> </div>

View File

@ -12,6 +12,7 @@
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="conhelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="conhelper" scope="request" />
<jsp:setProperty name="conhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="conhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="conhelper" property="writer" value="<%=out%>" />
<div class="main" id="main"> <div class="main" id="main">
<jsp:getProperty name="conhelper" property="console" /> <jsp:getProperty name="conhelper" property="console" />

View File

@ -12,6 +12,7 @@
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" /> <jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" />
<jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> <jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="oldhelper" property="writer" value="<%=out%>" />
<div class="main" id="main"> <div class="main" id="main">
<jsp:getProperty name="oldhelper" property="stats" /> <jsp:getProperty name="oldhelper" property="stats" />

View File

@ -9,7 +9,7 @@ package net.i2p.router;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.data.Hash; import net.i2p.data.Hash;
@ -70,7 +70,7 @@ public abstract class ClientManagerFacade implements Service {
* *
*/ */
public abstract SessionConfig getClientSessionConfig(Destination dest); public abstract SessionConfig getClientSessionConfig(Destination dest);
public void renderStatusHTML(OutputStream out) throws IOException { } public void renderStatusHTML(Writer out) throws IOException { }
} }
class DummyClientManagerFacade extends ClientManagerFacade { class DummyClientManagerFacade extends ClientManagerFacade {

View File

@ -9,7 +9,7 @@ package net.i2p.router;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -23,7 +23,7 @@ import java.util.Set;
public abstract class CommSystemFacade implements Service { public abstract class CommSystemFacade implements Service {
public abstract void processMessage(OutNetMessage msg); public abstract void processMessage(OutNetMessage msg);
public void renderStatusHTML(OutputStream out) throws IOException { } public void renderStatusHTML(Writer out) throws IOException { }
/** Create the set of RouterAddress structures based on the router's config */ /** Create the set of RouterAddress structures based on the router's config */
public Set createAddresses() { return new HashSet(); } public Set createAddresses() { return new HashSet(); }

View File

@ -9,7 +9,7 @@ package net.i2p.router;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -558,18 +558,18 @@ public class JobQueue {
// the remainder are utility methods for dumping status info // the remainder are utility methods for dumping status info
//// ////
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
ArrayList readyJobs = null; ArrayList readyJobs = null;
ArrayList timedJobs = null; ArrayList timedJobs = null;
ArrayList activeJobs = new ArrayList(1); ArrayList activeJobs = new ArrayList(1);
ArrayList justFinishedJobs = new ArrayList(4); ArrayList justFinishedJobs = new ArrayList(4);
out.write("<!-- jobQueue rendering -->\n".getBytes()); out.write("<!-- jobQueue rendering -->\n");
out.flush(); out.flush();
synchronized (_readyJobs) { readyJobs = new ArrayList(_readyJobs); } synchronized (_readyJobs) { readyJobs = new ArrayList(_readyJobs); }
out.write("<!-- jobQueue rendering: after readyJobs sync -->\n".getBytes()); out.write("<!-- jobQueue rendering: after readyJobs sync -->\n");
out.flush(); out.flush();
synchronized (_timedJobs) { timedJobs = new ArrayList(_timedJobs); } synchronized (_timedJobs) { timedJobs = new ArrayList(_timedJobs); }
out.write("<!-- jobQueue rendering: after timedJobs sync -->\n".getBytes()); out.write("<!-- jobQueue rendering: after timedJobs sync -->\n");
out.flush(); out.flush();
int numRunners = 0; int numRunners = 0;
synchronized (_queueRunners) { synchronized (_queueRunners) {
@ -586,7 +586,7 @@ public class JobQueue {
numRunners = _queueRunners.size(); numRunners = _queueRunners.size();
} }
out.write("<!-- jobQueue rendering: after queueRunners sync -->\n".getBytes()); out.write("<!-- jobQueue rendering: after queueRunners sync -->\n");
out.flush(); out.flush();
StringBuffer buf = new StringBuffer(32*1024); StringBuffer buf = new StringBuffer(32*1024);
@ -631,15 +631,15 @@ public class JobQueue {
} }
buf.append("</ol>\n"); buf.append("</ol>\n");
out.write("<!-- jobQueue rendering: after main buffer, before stats -->\n".getBytes()); out.write("<!-- jobQueue rendering: after main buffer, before stats -->\n");
out.flush(); out.flush();
getJobStats(buf); getJobStats(buf);
out.write("<!-- jobQueue rendering: after stats -->\n".getBytes()); out.write("<!-- jobQueue rendering: after stats -->\n");
out.flush(); out.flush();
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
/** render the HTML for the job stats */ /** render the HTML for the job stats */

View File

@ -9,7 +9,7 @@ package net.i2p.router;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -95,5 +95,5 @@ class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade {
public Set findNearestRouters(Hash key, int maxNumRouters, Set peersToIgnore) { return new HashSet(_routers.values()); } public Set findNearestRouters(Hash key, int maxNumRouters, Set peersToIgnore) { return new HashSet(_routers.values()); }
public void renderStatusHTML(OutputStream out) throws IOException {} public void renderStatusHTML(Writer out) throws IOException {}
} }

View File

@ -12,7 +12,7 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@ -315,8 +315,8 @@ public class Router {
_context.inNetMessagePool().registerHandlerJobBuilder(TunnelMessage.MESSAGE_TYPE, new TunnelMessageHandler(_context)); _context.inNetMessagePool().registerHandlerJobBuilder(TunnelMessage.MESSAGE_TYPE, new TunnelMessageHandler(_context));
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
out.write(("<h1>Router console</h1>\n" + out.write("<h1>Router console</h1>\n" +
"<i><a href=\"/oldconsole.jsp\">console</a> | <a href=\"/oldstats.jsp\">stats</a></i><br>\n" + "<i><a href=\"/oldconsole.jsp\">console</a> | <a href=\"/oldstats.jsp\">stats</a></i><br>\n" +
"<form action=\"/oldconsole.jsp\">" + "<form action=\"/oldconsole.jsp\">" +
"<select name=\"go\" onChange='location.href=this.value'>" + "<select name=\"go\" onChange='location.href=this.value'>" +
@ -331,7 +331,7 @@ public class Router {
"<option value=\"/oldconsole.jsp#netdb\">Network Database</option>\n" + "<option value=\"/oldconsole.jsp#netdb\">Network Database</option>\n" +
"<option value=\"/oldconsole.jsp#logs\">Log messages</option>\n" + "<option value=\"/oldconsole.jsp#logs\">Log messages</option>\n" +
"</select> <input type=\"submit\" value=\"GO\" /> </form>" + "</select> <input type=\"submit\" value=\"GO\" /> </form>" +
"<hr />\n").getBytes()); "<hr />\n");
StringBuffer buf = new StringBuffer(32*1024); StringBuffer buf = new StringBuffer(32*1024);
@ -453,39 +453,39 @@ public class Router {
buf.append("trying to transfer data. Lifetime averages count how many elephants there are on the moon [like anyone reads this text]</i>"); buf.append("trying to transfer data. Lifetime averages count how many elephants there are on the moon [like anyone reads this text]</i>");
buf.append("\n"); buf.append("\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
_context.bandwidthLimiter().renderStatusHTML(out); _context.bandwidthLimiter().renderStatusHTML(out);
out.write("<hr /><a name=\"clients\"> </a>\n".getBytes()); out.write("<hr /><a name=\"clients\"> </a>\n");
_context.clientManager().renderStatusHTML(out); _context.clientManager().renderStatusHTML(out);
out.write("\n<hr /><a name=\"transports\"> </a>\n".getBytes()); out.write("\n<hr /><a name=\"transports\"> </a>\n");
_context.commSystem().renderStatusHTML(out); _context.commSystem().renderStatusHTML(out);
out.write("\n<hr /><a name=\"profiles\"> </a>\n".getBytes()); out.write("\n<hr /><a name=\"profiles\"> </a>\n");
_context.peerManager().renderStatusHTML(out); _context.peerManager().renderStatusHTML(out);
out.write("\n<hr /><a name=\"tunnels\"> </a>\n".getBytes()); out.write("\n<hr /><a name=\"tunnels\"> </a>\n");
_context.tunnelManager().renderStatusHTML(out); _context.tunnelManager().renderStatusHTML(out);
out.write("\n<hr /><a name=\"jobs\"> </a>\n".getBytes()); out.write("\n<hr /><a name=\"jobs\"> </a>\n");
_context.jobQueue().renderStatusHTML(out); _context.jobQueue().renderStatusHTML(out);
out.write("\n<hr /><a name=\"shitlist\"> </a>\n".getBytes()); out.write("\n<hr /><a name=\"shitlist\"> </a>\n");
_context.shitlist().renderStatusHTML(out); _context.shitlist().renderStatusHTML(out);
out.write("\n<hr /><a name=\"pending\"> </a>\n".getBytes()); out.write("\n<hr /><a name=\"pending\"> </a>\n");
_context.messageRegistry().renderStatusHTML(out); _context.messageRegistry().renderStatusHTML(out);
out.write("\n<hr /><a name=\"netdb\"> </a>\n".getBytes()); out.write("\n<hr /><a name=\"netdb\"> </a>\n");
_context.netDb().renderStatusHTML(out); _context.netDb().renderStatusHTML(out);
@ -500,7 +500,7 @@ public class Router {
buf.append("</pre></td></tr>\n"); buf.append("</pre></td></tr>\n");
} }
buf.append("</table>\n"); buf.append("</table>\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
private static int MAX_MSG_LENGTH = 120; private static int MAX_MSG_LENGTH = 120;

View File

@ -31,4 +31,15 @@ public interface RouterThrottle {
* *
*/ */
public boolean acceptNetDbLookupRequest(Hash key); public boolean acceptNetDbLookupRequest(Hash key);
/** How backed up we are at the moment processing messages (in milliseconds) */
public long getMessageDelay();
/** How backed up our tunnels are at the moment (in milliseconds) */
public long getTunnelLag();
/**
* How much faster (or if negative, slower) we are receiving data as
* opposed to our longer term averages?
*
*/
public double getInboundRateDelta();
} }

View File

@ -154,5 +154,35 @@ class RouterThrottleImpl implements RouterThrottle {
return true; return true;
} }
public long getMessageDelay() {
Rate delayRate = _context.statManager().getRate("transport.sendProcessingTime").getRate(60*1000);
return (long)delayRate.getAverageValue();
}
public long getTunnelLag() {
Rate lagRate = _context.statManager().getRate("tunnel.testSuccessTime").getRate(10*60*1000);
return (long)lagRate.getAverageValue();
}
public double getInboundRateDelta() {
RateStat receiveRate = _context.statManager().getRate("transport.sendMessageSize");
double nowBps = getBps(receiveRate.getRate(60*1000));
double fiveMinBps = getBps(receiveRate.getRate(5*60*1000));
double hourBps = getBps(receiveRate.getRate(60*60*1000));
double dailyBps = getBps(receiveRate.getRate(24*60*60*1000));
if (nowBps < 0) return 0;
if (dailyBps > 0) return nowBps - dailyBps;
if (hourBps > 0) return nowBps - hourBps;
if (fiveMinBps > 0) return nowBps - fiveMinBps;
return 0;
}
private double getBps(Rate rate) {
if (rate == null) return -1;
double bytes = rate.getLastTotalValue();
return (bytes*1000.0d)/rate.getPeriod();
}
protected RouterContext getContext() { return _context; } protected RouterContext getContext() { return _context; }
} }

View File

@ -9,7 +9,7 @@ package net.i2p.router;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
/** /**
* Define the manageable service interface for the subsystems in the I2P router * Define the manageable service interface for the subsystems in the I2P router
@ -37,5 +37,5 @@ public interface Service {
*/ */
public void restart(); public void restart();
public void renderStatusHTML(OutputStream out) throws IOException; public void renderStatusHTML(Writer out) throws IOException;
} }

View File

@ -4,7 +4,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import net.i2p.crypto.PersistentSessionKeyManager; import net.i2p.crypto.PersistentSessionKeyManager;
import net.i2p.crypto.SessionKeyManager; import net.i2p.crypto.SessionKeyManager;
@ -97,7 +97,7 @@ public class SessionKeyPersistenceHelper implements Service {
} }
} }
public void renderStatusHTML(OutputStream out) { } public void renderStatusHTML(Writer out) { }
private class SessionKeyWriterJob extends JobImpl { private class SessionKeyWriterJob extends JobImpl {
public SessionKeyWriterJob() { public SessionKeyWriterJob() {

View File

@ -9,7 +9,7 @@ package net.i2p.router;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -123,7 +123,7 @@ public class Shitlist {
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
StringBuffer buf = new StringBuffer(1024); StringBuffer buf = new StringBuffer(1024);
buf.append("<h2>Shitlist</h2>"); buf.append("<h2>Shitlist</h2>");
Map shitlist = null; Map shitlist = null;
@ -152,6 +152,6 @@ public class Shitlist {
buf.append("</li>\n"); buf.append("</li>\n");
} }
buf.append("</ul>\n"); buf.append("</ul>\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
} }

View File

@ -8,7 +8,7 @@ package net.i2p.router;
* *
*/ */
import java.io.OutputStream; import java.io.Writer;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.Locale; import java.util.Locale;
@ -107,32 +107,33 @@ public class StatisticsManager implements Service {
includeRate("jobQueue.jobLag", stats, new long[] { 60*1000, 60*60*1000 }); includeRate("jobQueue.jobLag", stats, new long[] { 60*1000, 60*60*1000 });
includeRate("jobQueue.jobRun", stats, new long[] { 60*1000, 60*60*1000 }); includeRate("jobQueue.jobRun", stats, new long[] { 60*1000, 60*60*1000 });
includeRate("crypto.elGamal.encrypt", stats, new long[] { 60*1000, 60*60*1000 }); includeRate("crypto.elGamal.encrypt", stats, new long[] { 60*1000, 60*60*1000 });
includeRate("crypto.garlic.decryptFail", stats, new long[] { 60*60*1000, 24*60*60*1000 }); //includeRate("crypto.garlic.decryptFail", stats, new long[] { 60*60*1000, 24*60*60*1000 });
includeRate("tunnel.unknownTunnelTimeLeft", stats, new long[] { 60*60*1000, 24*60*60*1000 }); includeRate("tunnel.unknownTunnelTimeLeft", stats, new long[] { 60*60*1000, 24*60*60*1000 });
includeRate("jobQueue.readyJobs", stats, new long[] { 60*1000, 60*60*1000 }); includeRate("jobQueue.readyJobs", stats, new long[] { 60*1000, 60*60*1000 });
//includeRate("jobQueue.droppedJobs", stats, new long[] { 60*60*1000, 24*60*60*1000 }); //includeRate("jobQueue.droppedJobs", stats, new long[] { 60*60*1000, 24*60*60*1000 });
includeRate("inNetPool.dropped", stats, new long[] { 60*60*1000, 24*60*60*1000 }); includeRate("inNetPool.dropped", stats, new long[] { 60*60*1000, 24*60*60*1000 });
includeRate("tunnel.participatingTunnels", stats, new long[] { 5*60*1000, 60*60*1000 }); includeRate("tunnel.participatingTunnels", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("tunnel.testSuccessTime", stats, new long[] { 60*60*1000l, 24*60*60*1000l }); includeRate("tunnel.testSuccessTime", stats, new long[] { 60*60*1000l, 24*60*60*1000l });
includeRate("tunnel.outboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 }); //includeRate("tunnel.outboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 });
includeRate("tunnel.inboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 }); //includeRate("tunnel.inboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 });
includeRate("tunnel.participatingMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 }); includeRate("tunnel.participatingMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 });
includeRate("tunnel.expiredAfterAcceptTime", stats, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l }); includeRate("tunnel.participatingMessagesProcessedActive", stats, new long[] { 10*60*1000, 60*60*1000 });
//includeRate("tunnel.expiredAfterAcceptTime", stats, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
includeRate("tunnel.bytesAllocatedAtAccept", stats, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l }); includeRate("tunnel.bytesAllocatedAtAccept", stats, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
includeRate("netDb.lookupsReceived", stats, new long[] { 5*60*1000, 60*60*1000 }); includeRate("netDb.lookupsReceived", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("netDb.lookupsHandled", stats, new long[] { 5*60*1000, 60*60*1000 }); includeRate("netDb.lookupsHandled", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("netDb.lookupsMatched", stats, new long[] { 5*60*1000, 60*60*1000 }); includeRate("netDb.lookupsMatched", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("netDb.storeSent", stats, new long[] { 5*60*1000, 60*60*1000 }); //includeRate("netDb.storeSent", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("netDb.successPeers", stats, new long[] { 60*60*1000 }); includeRate("netDb.successPeers", stats, new long[] { 60*60*1000 });
includeRate("netDb.failedPeers", stats, new long[] { 60*60*1000 }); includeRate("netDb.failedPeers", stats, new long[] { 60*60*1000 });
includeRate("router.throttleNetDbDoSSend", stats, new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); //includeRate("router.throttleNetDbDoSSend", stats, new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
includeRate("router.throttleNetDbDoS", stats, new long[] { 10*60*1000, 60*60*1000 }); //includeRate("router.throttleNetDbDoS", stats, new long[] { 10*60*1000, 60*60*1000 });
//includeRate("netDb.searchCount", stats, new long[] { 3*60*60*1000}); //includeRate("netDb.searchCount", stats, new long[] { 3*60*60*1000});
//includeRate("netDb.searchMessageCount", stats, new long[] { 5*60*1000, 10*60*1000, 60*60*1000 }); //includeRate("netDb.searchMessageCount", stats, new long[] { 5*60*1000, 10*60*1000, 60*60*1000 });
//includeRate("inNetMessage.timeToDiscard", stats, new long[] { 5*60*1000, 10*60*1000, 60*60*1000 }); //includeRate("inNetMessage.timeToDiscard", stats, new long[] { 5*60*1000, 10*60*1000, 60*60*1000 });
//includeRate("outNetMessage.timeToDiscard", stats, new long[] { 5*60*1000, 10*60*1000, 60*60*1000 }); //includeRate("outNetMessage.timeToDiscard", stats, new long[] { 5*60*1000, 10*60*1000, 60*60*1000 });
includeRate("router.throttleNetworkCause", stats, new long[] { 10*60*1000, 60*60*1000 }); //includeRate("router.throttleNetworkCause", stats, new long[] { 10*60*1000, 60*60*1000 });
includeRate("transport.receiveMessageSize", stats, new long[] { 5*60*1000, 60*60*1000 }); //includeRate("transport.receiveMessageSize", stats, new long[] { 5*60*1000, 60*60*1000 });
//includeRate("transport.sendMessageSize", stats, new long[] { 5*60*1000, 60*60*1000 }); //includeRate("transport.sendMessageSize", stats, new long[] { 5*60*1000, 60*60*1000 });
//includeRate("transport.sendMessageSmall", stats, new long[] { 5*60*1000, 60*60*1000 }); //includeRate("transport.sendMessageSmall", stats, new long[] { 5*60*1000, 60*60*1000 });
//includeRate("transport.sendMessageMedium", stats, new long[] { 5*60*1000, 60*60*1000 }); //includeRate("transport.sendMessageMedium", stats, new long[] { 5*60*1000, 60*60*1000 });
@ -141,6 +142,10 @@ public class StatisticsManager implements Service {
//includeRate("transport.receiveMessageMedium", stats, new long[] { 5*60*1000, 60*60*1000 }); //includeRate("transport.receiveMessageMedium", stats, new long[] { 5*60*1000, 60*60*1000 });
//includeRate("transport.receiveMessageLarge", stats, new long[] { 5*60*1000, 60*60*1000 }); //includeRate("transport.receiveMessageLarge", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("client.sendAckTime", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true); includeRate("client.sendAckTime", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
includeRate("client.sendsPerFailure", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
includeRate("client.timeoutCongestionTunnel", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
includeRate("client.timeoutCongestionMessage", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
includeRate("client.timeoutCongestionInbound", stats, new long[] { 60*60*1000, 24*60*60*1000l }, true);
stats.setProperty("stat_uptime", DataHelper.formatDuration(_context.router().getUptime())); stats.setProperty("stat_uptime", DataHelper.formatDuration(_context.router().getUptime()));
stats.setProperty("stat__rateKey", "avg;maxAvg;pctLifetime;[sat;satLim;maxSat;maxSatLim;][num;lifetimeFreq;maxFreq]"); stats.setProperty("stat__rateKey", "avg;maxAvg;pctLifetime;[sat;satLim;maxSat;maxSatLim;][num;lifetimeFreq;maxFreq]");
_log.debug("Publishing peer rankings"); _log.debug("Publishing peer rankings");
@ -266,5 +271,5 @@ public class StatisticsManager implements Service {
private final String num(double num) { synchronized (_fmt) { return _fmt.format(num); } } private final String num(double num) { synchronized (_fmt) { return _fmt.format(num); } }
private final String pct(double num) { synchronized (_pct) { return _pct.format(num); } } private final String pct(double num) { synchronized (_pct) { return _pct.format(num); } }
public void renderStatusHTML(OutputStream out) { } public void renderStatusHTML(Writer out) { }
} }

View File

@ -1,6 +1,6 @@
package net.i2p.router.admin; package net.i2p.router.admin;
import java.io.OutputStream; import java.io.Writer;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.router.Service; import net.i2p.router.Service;
@ -20,7 +20,7 @@ public class AdminManager implements Service {
_log = context.logManager().getLog(AdminManager.class); _log = context.logManager().getLog(AdminManager.class);
} }
public void renderStatusHTML(OutputStream out) { } public void renderStatusHTML(Writer out) { }
public void shutdown() { public void shutdown() {
if (_listener != null) { if (_listener != null) {

View File

@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket; import java.net.Socket;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
@ -47,7 +48,7 @@ class AdminRunner implements Runnable {
} else if ( (command.indexOf("routerStats.html") >= 0) || (command.indexOf("oldstats.jsp") >= 0) ) { } else if ( (command.indexOf("routerStats.html") >= 0) || (command.indexOf("oldstats.jsp") >= 0) ) {
try { try {
out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes()); out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes());
_generator.generateStatsPage(out); _generator.generateStatsPage(new OutputStreamWriter(out));
out.close(); out.close();
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
@ -61,7 +62,7 @@ class AdminRunner implements Runnable {
} else if (true || command.indexOf("routerConsole.html") > 0) { } else if (true || command.indexOf("routerConsole.html") > 0) {
try { try {
out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes()); out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes());
_context.router().renderStatusHTML(out); _context.router().renderStatusHTML(new OutputStreamWriter(out));
out.close(); out.close();
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))

View File

@ -2,6 +2,7 @@ package net.i2p.router.admin;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Writer;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
@ -27,13 +28,13 @@ public class StatsGenerator {
_log = context.logManager().getLog(StatsGenerator.class); _log = context.logManager().getLog(StatsGenerator.class);
} }
public void generateStatsPage(OutputStream out) throws IOException { public void generateStatsPage(Writer out) throws IOException {
StringBuffer buf = new StringBuffer(16*1024); StringBuffer buf = new StringBuffer(16*1024);
buf.append("<h1>Router statistics</h1>"); buf.append("<h1>Router statistics</h1>");
buf.append("<i><a href=\"/oldconsole.jsp\">console</a> | <a href=\"/oldstats.jsp\">stats</a></i><hr />"); buf.append("<i><a href=\"/oldconsole.jsp\">console</a> | <a href=\"/oldstats.jsp\">stats</a></i><hr />");
buf.append("<form action=\"/oldstats.jsp\">"); buf.append("<form action=\"/oldstats.jsp\">");
buf.append("<select name=\"go\" onChange='location.href=this.value'>"); buf.append("<select name=\"go\" onChange='location.href=this.value'>");
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
Map groups = _context.statManager().getStatsByGroup(); Map groups = _context.statManager().getStatsByGroup();
@ -50,7 +51,7 @@ public class StatsGenerator {
buf.append(stat); buf.append(stat);
buf.append("</option>\n"); buf.append("</option>\n");
} }
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
} }
buf.append("</select> <input type=\"submit\" value=\"GO\" />"); buf.append("</select> <input type=\"submit\" value=\"GO\" />");
@ -61,7 +62,7 @@ public class StatsGenerator {
buf.append(DataHelper.formatDuration(uptime)); buf.append(DataHelper.formatDuration(uptime));
buf.append("). The data gathered is quantized over a 1 minute period, so should just be used as an estimate<p />"); buf.append("). The data gathered is quantized over a 1 minute period, so should just be used as an estimate<p />");
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = groups.keySet().iterator(); iter.hasNext(); ) {
@ -73,7 +74,7 @@ public class StatsGenerator {
buf.append(group); buf.append(group);
buf.append("</a></h2>"); buf.append("</a></h2>");
buf.append("<ul>"); buf.append("<ul>");
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
for (Iterator statIter = stats.iterator(); statIter.hasNext(); ) { for (Iterator statIter = stats.iterator(); statIter.hasNext(); ) {
String stat = (String)statIter.next(); String stat = (String)statIter.next();
@ -86,10 +87,10 @@ public class StatsGenerator {
renderFrequency(stat, buf); renderFrequency(stat, buf);
else else
renderRate(stat, buf); renderRate(stat, buf);
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
} }
out.write("</ul><hr />".getBytes()); out.write("</ul><hr />");
} }
} }

View File

@ -9,7 +9,7 @@ package net.i2p.router.client;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -332,7 +332,7 @@ public class ClientManager {
} }
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
StringBuffer buf = new StringBuffer(8*1024); StringBuffer buf = new StringBuffer(8*1024);
buf.append("<u><b>Local destinations</b></u><br />"); buf.append("<u><b>Local destinations</b></u><br />");
@ -376,7 +376,7 @@ public class ClientManager {
} }
buf.append("\n<hr />\n"); buf.append("\n<hr />\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
public void messageReceived(ClientMessage msg) { public void messageReceived(ClientMessage msg) {

View File

@ -9,7 +9,7 @@ package net.i2p.router.client;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.data.Hash; import net.i2p.data.Hash;
@ -158,7 +158,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
} }
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
if (_manager != null) if (_manager != null)
_manager.renderStatusHTML(out); _manager.renderStatusHTML(out);
} }

View File

@ -104,7 +104,11 @@ public class OutboundClientMessageJob extends JobImpl {
ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "Client Messages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "Client Messages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendAttemptAverage", "How many different tunnels do we have to try when sending a client message?", "Client Messages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); ctx.statManager().createRateStat("client.sendAttemptAverage", "How many different tunnels do we have to try when sending a client message?", "Client Messages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendAckTime", "How long does it take to get an ACK back from a message?", "Client Messages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); ctx.statManager().createRateStat("client.sendAckTime", "How long does it take to get an ACK back from a message?", "Client Messages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendsPerFailure", "How many send attempts do we make when they all fail?", "Client Messages", new long[] { 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "Client Messages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "Client Messages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "Client Messages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT; long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM); String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
@ -122,7 +126,7 @@ public class OutboundClientMessageJob extends JobImpl {
} }
_overallExpiration = timeoutMs + getContext().clock().now(); _overallExpiration = timeoutMs + getContext().clock().now();
_status = new OutboundClientMessageStatus(msg); _status = new OutboundClientMessageStatus(ctx, msg);
_nextStep = new NextStepJob(); _nextStep = new NextStepJob();
_lookupLeaseSetFailed = new LookupLeaseSetFailedJob(); _lookupLeaseSetFailed = new LookupLeaseSetFailedJob();
_shouldBundle = getShouldBundle(); _shouldBundle = getShouldBundle();
@ -423,6 +427,7 @@ public class OutboundClientMessageJob extends JobImpl {
getContext().clientManager().messageDeliveryStatusUpdate(msg.getFromDestination(), msg.getMessageId(), false); getContext().clientManager().messageDeliveryStatusUpdate(msg.getFromDestination(), msg.getMessageId(), false);
getContext().statManager().updateFrequency("client.sendMessageFailFrequency"); getContext().statManager().updateFrequency("client.sendMessageFailFrequency");
getContext().statManager().addRateData("client.sendAttemptAverage", _status.getNumSent(), sendTime); getContext().statManager().addRateData("client.sendAttemptAverage", _status.getNumSent(), sendTime);
getContext().statManager().addRateData("client.sendsPerFailure", _status.getNumSent(), sendTime);
} }
/** build the payload clove that will be used for all of the messages, placing the clove in the status structure */ /** build the payload clove that will be used for all of the messages, placing the clove in the status structure */
@ -455,126 +460,6 @@ public class OutboundClientMessageJob extends JobImpl {
_log.debug(getJobId() + ": Built payload clove with id " + clove.getId()); _log.debug(getJobId() + ": Built payload clove with id " + clove.getId());
} }
/**
* Good ol' fashioned struct with the send status
*
*/
private class OutboundClientMessageStatus {
private ClientMessage _msg;
private PayloadGarlicConfig _clove;
private LeaseSet _leaseSet;
private Set _sent;
private int _numLookups;
private boolean _success;
private boolean _failure;
private long _start;
private int _previousSent;
public OutboundClientMessageStatus(ClientMessage msg) {
_msg = msg;
_clove = null;
_leaseSet = null;
_sent = new HashSet(4);
_success = false;
_failure = false;
_numLookups = 0;
_previousSent = 0;
_start = getContext().clock().now();
}
/** raw payload */
public Payload getPayload() { return _msg.getPayload(); }
/** clove, if we've built it */
public PayloadGarlicConfig getClove() { return _clove; }
public void setClove(PayloadGarlicConfig clove) { _clove = clove; }
public ClientMessage getMessage() { return _msg; }
/** date we started the process on */
public long getStart() { return _start; }
public int getNumLookups() { return _numLookups; }
public void incrementLookups() { _numLookups++; }
public void clearAlreadySent() {
synchronized (_sent) {
_previousSent += _sent.size();
_sent.clear();
}
}
/** who sent the message? */
public Destination getFrom() { return _msg.getFromDestination(); }
/** who is the message going to? */
public Destination getTo() { return _msg.getDestination(); }
/** what is the target's current leaseSet (or null if we don't know yet) */
public LeaseSet getLeaseSet() { return _leaseSet; }
public void setLeaseSet(LeaseSet ls) { _leaseSet = ls; }
/** have we already sent the message down this tunnel? */
public boolean alreadySent(Hash gateway, TunnelId tunnelId) {
Tunnel t = new Tunnel(gateway, tunnelId);
synchronized (_sent) {
return _sent.contains(t);
}
}
public void sent(Hash gateway, TunnelId tunnelId) {
Tunnel t = new Tunnel(gateway, tunnelId);
synchronized (_sent) {
_sent.add(t);
}
}
/** how many messages have we sent through various leases? */
public int getNumSent() {
synchronized (_sent) {
return _sent.size() + _previousSent;
}
}
/** did we totally fail? */
public boolean getFailure() { return _failure; }
/** we failed. returns true if we had already failed before */
public boolean failed() {
boolean already = _failure;
_failure = true;
return already;
}
/** have we totally succeeded? */
public boolean getSuccess() { return _success; }
/** we succeeded. returns true if we had already succeeded before */
public boolean success() {
boolean already = _success;
_success = true;
return already;
}
/** represent a unique tunnel at any given time */
private class Tunnel {
private Hash _gateway;
private TunnelId _tunnel;
public Tunnel(Hash tunnelGateway, TunnelId tunnel) {
_gateway = tunnelGateway;
_tunnel = tunnel;
}
public Hash getGateway() { return _gateway; }
public TunnelId getTunnel() { return _tunnel; }
public int hashCode() {
int rv = 0;
if (_gateway != null)
rv += _gateway.hashCode();
if (_tunnel != null)
rv += 7*_tunnel.getTunnelId();
return rv;
}
public boolean equals(Object o) {
if (o == null) return false;
if (o.getClass() != Tunnel.class) return false;
Tunnel t = (Tunnel)o;
return (getTunnel() == t.getTunnel()) &&
getGateway().equals(t.getGateway());
}
}
}
/** /**
* Keep an eye out for any of the delivery status message tokens that have been * Keep an eye out for any of the delivery status message tokens that have been
* sent down the various tunnels to deliver this message * sent down the various tunnels to deliver this message
@ -712,6 +597,14 @@ public class OutboundClientMessageJob extends JobImpl {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug(OutboundClientMessageJob.this.getJobId() _log.debug(OutboundClientMessageJob.this.getJobId()
+ ": Soft timeout through the lease " + _lease); + ": Soft timeout through the lease " + _lease);
long messageDelay = getContext().throttle().getMessageDelay();
long tunnelLag = getContext().throttle().getTunnelLag();
long inboundDelta = (long)getContext().throttle().getInboundRateDelta();
getContext().statManager().addRateData("client.timeoutCongestionTunnel", tunnelLag, 1);
getContext().statManager().addRateData("client.timeoutCongestionMessage", messageDelay, 1);
getContext().statManager().addRateData("client.timeoutCongestionInbound", inboundDelta, 1);
_lease.setNumFailure(_lease.getNumFailure()+1); _lease.setNumFailure(_lease.getNumFailure()+1);
sendNext(); sendNext();
} }

View File

@ -0,0 +1,133 @@
package net.i2p.router.message;
import java.util.HashSet;
import java.util.Set;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.Payload;
import net.i2p.data.TunnelId;
import net.i2p.router.ClientMessage;
import net.i2p.router.RouterContext;
/**
* Good ol' fashioned struct with the send status
*
*/
class OutboundClientMessageStatus {
private RouterContext _context;
private ClientMessage _msg;
private PayloadGarlicConfig _clove;
private LeaseSet _leaseSet;
private Set _sent;
private int _numLookups;
private boolean _success;
private boolean _failure;
private long _start;
private int _previousSent;
public OutboundClientMessageStatus(RouterContext ctx, ClientMessage msg) {
_context = ctx;
_msg = msg;
_clove = null;
_leaseSet = null;
_sent = new HashSet(4);
_success = false;
_failure = false;
_numLookups = 0;
_previousSent = 0;
_start = ctx.clock().now();
}
/** raw payload */
public Payload getPayload() { return _msg.getPayload(); }
/** clove, if we've built it */
public PayloadGarlicConfig getClove() { return _clove; }
public void setClove(PayloadGarlicConfig clove) { _clove = clove; }
public ClientMessage getMessage() { return _msg; }
/** date we started the process on */
public long getStart() { return _start; }
public int getNumLookups() { return _numLookups; }
public void incrementLookups() { _numLookups++; }
public void clearAlreadySent() {
synchronized (_sent) {
_previousSent += _sent.size();
_sent.clear();
}
}
/** who sent the message? */
public Destination getFrom() { return _msg.getFromDestination(); }
/** who is the message going to? */
public Destination getTo() { return _msg.getDestination(); }
/** what is the target's current leaseSet (or null if we don't know yet) */
public LeaseSet getLeaseSet() { return _leaseSet; }
public void setLeaseSet(LeaseSet ls) { _leaseSet = ls; }
/** have we already sent the message down this tunnel? */
public boolean alreadySent(Hash gateway, TunnelId tunnelId) {
Tunnel t = new Tunnel(gateway, tunnelId);
synchronized (_sent) {
return _sent.contains(t);
}
}
public void sent(Hash gateway, TunnelId tunnelId) {
Tunnel t = new Tunnel(gateway, tunnelId);
synchronized (_sent) {
_sent.add(t);
}
}
/** how many messages have we sent through various leases? */
public int getNumSent() {
synchronized (_sent) {
return _sent.size() + _previousSent;
}
}
/** did we totally fail? */
public boolean getFailure() { return _failure; }
/** we failed. returns true if we had already failed before */
public boolean failed() {
boolean already = _failure;
_failure = true;
return already;
}
/** have we totally succeeded? */
public boolean getSuccess() { return _success; }
/** we succeeded. returns true if we had already succeeded before */
public boolean success() {
boolean already = _success;
_success = true;
return already;
}
/** represent a unique tunnel at any given time */
private class Tunnel {
private Hash _gateway;
private TunnelId _tunnel;
public Tunnel(Hash tunnelGateway, TunnelId tunnel) {
_gateway = tunnelGateway;
_tunnel = tunnel;
}
public Hash getGateway() { return _gateway; }
public TunnelId getTunnel() { return _tunnel; }
public int hashCode() {
int rv = 0;
if (_gateway != null)
rv += _gateway.hashCode();
if (_tunnel != null)
rv += 7*_tunnel.getTunnelId();
return rv;
}
public boolean equals(Object o) {
if (o == null) return false;
if (o.getClass() != Tunnel.class) return false;
Tunnel t = (Tunnel)o;
return (getTunnel() == t.getTunnel()) &&
getGateway().equals(t.getGateway());
}
}
}

View File

@ -11,7 +11,7 @@ package net.i2p.router.networkdb.kademlia;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
@ -810,17 +810,17 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
return routers; return routers;
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
StringBuffer buf = new StringBuffer(10*1024); StringBuffer buf = new StringBuffer(10*1024);
buf.append("<h2>Kademlia Network DB Contents</h2>\n"); buf.append("<h2>Kademlia Network DB Contents</h2>\n");
if (!_initialized) { if (!_initialized) {
buf.append("<i>Not initialized</i>\n"); buf.append("<i>Not initialized</i>\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
return; return;
} }
Set leases = getLeases(); Set leases = getLeases();
buf.append("<h3>Leases</h3>\n"); buf.append("<h3>Leases</h3>\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
long now = _context.clock().now(); long now = _context.clock().now();
for (Iterator iter = leases.iterator(); iter.hasNext(); ) { for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
@ -838,17 +838,17 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
buf.append("</i> tunnelId <i>").append(ls.getLease(i).getTunnelId().getTunnelId()).append("</i><br />\n"); buf.append("</i> tunnelId <i>").append(ls.getLease(i).getTunnelId().getTunnelId()).append("</i><br />\n");
} }
buf.append("<hr />\n"); buf.append("<hr />\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
} }
Hash us = _context.routerHash(); Hash us = _context.routerHash();
Set routers = getRouters(); Set routers = getRouters();
out.write("<h3>Routers</h3>\n".getBytes()); out.write("<h3>Routers</h3>\n");
RouterInfo ourInfo = _context.router().getRouterInfo(); RouterInfo ourInfo = _context.router().getRouterInfo();
renderRouterInfo(buf, ourInfo, true); renderRouterInfo(buf, ourInfo, true);
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
/* coreVersion to Map of routerVersion to Integer */ /* coreVersion to Map of routerVersion to Integer */
@ -860,7 +860,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
boolean isUs = key.equals(us); boolean isUs = key.equals(us);
if (!isUs) { if (!isUs) {
renderRouterInfo(buf, ri, false); renderRouterInfo(buf, ri, false);
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
String coreVersion = ri.getOptions().getProperty("coreVersion"); String coreVersion = ri.getOptions().getProperty("coreVersion");
String routerVersion = ri.getOptions().getProperty("router.version"); String routerVersion = ri.getOptions().getProperty("router.version");
@ -895,7 +895,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
} }
buf.append("</table>\n"); buf.append("</table>\n");
} }
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
private void renderRouterInfo(StringBuffer buf, RouterInfo info, boolean isUs) { private void renderRouterInfo(StringBuffer buf, RouterInfo info, boolean isUs) {

View File

@ -9,7 +9,7 @@ package net.i2p.router.peermanager;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -116,7 +116,7 @@ class PeerManager {
return new ArrayList(peers); return new ArrayList(peers);
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
_organizer.renderStatusHTML(out); _organizer.renderStatusHTML(out);
} }
} }

View File

@ -9,7 +9,7 @@ package net.i2p.router.peermanager;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -59,7 +59,7 @@ public class PeerManagerFacadeImpl implements PeerManagerFacade {
return _manager.selectPeers(criteria); return _manager.selectPeers(criteria);
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
_manager.renderStatusHTML(out); _manager.renderStatusHTML(out);
} }
} }

View File

@ -2,6 +2,7 @@ package net.i2p.router.peermanager;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Writer;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.ArrayList; import java.util.ArrayList;
@ -203,7 +204,7 @@ public class ProfileOrganizer {
_persistenceHelper.writeProfile(prof, out); _persistenceHelper.writeProfile(prof, out);
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
ProfileOrganizerRenderer rend = new ProfileOrganizerRenderer(this, _context); ProfileOrganizerRenderer rend = new ProfileOrganizerRenderer(this, _context);
rend.renderStatusHTML(out); rend.renderStatusHTML(out);
} }

View File

@ -1,7 +1,7 @@
package net.i2p.router.peermanager; package net.i2p.router.peermanager;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
@ -27,7 +27,7 @@ class ProfileOrganizerRenderer {
_context = context; _context = context;
_organizer = organizer; _organizer = organizer;
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
Set peers = _organizer.selectAllPeers(); Set peers = _organizer.selectAllPeers();
long hideBefore = _context.clock().now() - 3*60*60*1000; long hideBefore = _context.clock().now() - 3*60*60*1000;
@ -126,7 +126,7 @@ class ProfileOrganizerRenderer {
buf.append("<b>Speed:</b> ").append(num(_organizer.getSpeedThreshold())).append(" (").append(fast).append(" fast peers)<br />"); buf.append("<b>Speed:</b> ").append(num(_organizer.getSpeedThreshold())).append(" (").append(fast).append(" fast peers)<br />");
buf.append("<b>Capacity:</b> ").append(num(_organizer.getCapacityThreshold())).append(" (").append(reliable).append(" high capacity peers)<br />"); buf.append("<b>Capacity:</b> ").append(num(_organizer.getCapacityThreshold())).append(" (").append(reliable).append(" high capacity peers)<br />");
buf.append("<b>Integration:</b> ").append(num(_organizer.getIntegrationThreshold())).append(" (").append(integrated).append(" well integrated peers)<br />"); buf.append("<b>Integration:</b> ").append(num(_organizer.getIntegrationThreshold())).append(" (").append(integrated).append(" well integrated peers)<br />");
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
private final static DecimalFormat _fmt = new DecimalFormat("###,##0.00", new DecimalFormatSymbols(Locale.UK)); private final static DecimalFormat _fmt = new DecimalFormat("###,##0.00", new DecimalFormatSymbols(Locale.UK));

View File

@ -1,7 +1,7 @@
package net.i2p.router.transport; package net.i2p.router.transport;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -407,7 +407,7 @@ public class FIFOBandwidthLimiter {
return satisfied; return satisfied;
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
long now = _context.clock().now(); long now = _context.clock().now();
StringBuffer buf = new StringBuffer(4096); StringBuffer buf = new StringBuffer(4096);
buf.append("<br /><b>Pending bandwidth requests (with "); buf.append("<br /><b>Pending bandwidth requests (with ");
@ -436,7 +436,7 @@ public class FIFOBandwidthLimiter {
} }
} }
buf.append("</ol></li></ul>\n"); buf.append("</ol></li></ul>\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
private static long __requestId = 0; private static long __requestId = 0;

View File

@ -9,7 +9,7 @@ package net.i2p.router.transport;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
@ -275,7 +275,7 @@ public class OutboundMessageRegistry {
} }
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
StringBuffer buf = new StringBuffer(8192); StringBuffer buf = new StringBuffer(8192);
buf.append("<h2>Pending messages</h2>\n"); buf.append("<h2>Pending messages</h2>\n");
Map msgs = null; Map msgs = null;
@ -295,7 +295,7 @@ public class OutboundMessageRegistry {
buf.append("</li>\n"); buf.append("</li>\n");
} }
buf.append("</ul>"); buf.append("</ul>");
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
/** /**

View File

@ -9,7 +9,7 @@ package net.i2p.router.transport;
*/ */
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -265,7 +265,7 @@ public class TransportManager implements TransportEventListener {
return rv; return rv;
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
StringBuffer buf = new StringBuffer(8*1024); StringBuffer buf = new StringBuffer(8*1024);
buf.append("<h2>Transport Manager</h2>\n"); buf.append("<h2>Transport Manager</h2>\n");
buf.append("Listening on: <br /><pre>\n"); buf.append("Listening on: <br /><pre>\n");
@ -283,6 +283,6 @@ public class TransportManager implements TransportEventListener {
if (str != null) if (str != null)
buf.append(str); buf.append(str);
} }
out.write(buf.toString().getBytes()); out.write(buf.toString());
} }
} }

View File

@ -1,7 +1,7 @@
package net.i2p.router.tunnelmanager; package net.i2p.router.tunnelmanager;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -229,7 +229,7 @@ public class PoolingTunnelManagerFacade implements TunnelManagerFacade {
* Aint she pretty? * Aint she pretty?
* *
*/ */
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
if (_pool != null) if (_pool != null)
_pool.renderStatusHTML(out); _pool.renderStatusHTML(out);
} }

View File

@ -1,7 +1,7 @@
package net.i2p.router.tunnelmanager; package net.i2p.router.tunnelmanager;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.Writer;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -66,7 +66,8 @@ class TunnelPool {
_context.statManager().createRateStat("tunnel.inboundMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l }); _context.statManager().createRateStat("tunnel.inboundMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
_context.statManager().createRateStat("tunnel.outboundMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l }); _context.statManager().createRateStat("tunnel.outboundMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
_context.statManager().createRateStat("tunnel.participatingMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l }); _context.statManager().createRateStat("tunnel.participatingMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
_context.statManager().createRateStat("tunnel.participatingMessagesProcessedActive", "How many messages beyond the average were processed in a more-than-average tunnel's lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
_isLive = true; _isLive = true;
_persistenceHelper = new TunnelPoolPersistenceHelper(_context); _persistenceHelper = new TunnelPoolPersistenceHelper(_context);
_tunnelBuilder = new TunnelBuilder(_context); _tunnelBuilder = new TunnelBuilder(_context);
@ -603,8 +604,8 @@ class TunnelPool {
public void shutdown() { public void shutdown() {
if (_log.shouldLog(Log.INFO)) _log.info("Shutting down tunnel pool"); if (_log.shouldLog(Log.INFO)) _log.info("Shutting down tunnel pool");
if (_persistenceHelper != null) //if (_persistenceHelper != null)
_persistenceHelper.writePool(this); // _persistenceHelper.writePool(this);
_isLive = false; // the subjobs [should] check getIsLive() on each run _isLive = false; // the subjobs [should] check getIsLive() on each run
_outboundTunnels = null; _outboundTunnels = null;
_freeInboundTunnels = null; _freeInboundTunnels = null;
@ -633,10 +634,17 @@ class TunnelPool {
info.getSettings().getCreated()); info.getSettings().getCreated());
break; break;
case TunnelId.TYPE_PARTICIPANT: case TunnelId.TYPE_PARTICIPANT:
long numMsgs = info.getMessagesProcessed();
long lastAvg = (long)_context.statManager().getRate("tunnel.participatingMessagesProcessed").getRate(10*60*1000l).getAverageValue();
_context.statManager().addRateData("tunnel.participatingMessagesProcessed", _context.statManager().addRateData("tunnel.participatingMessagesProcessed",
info.getMessagesProcessed(), numMsgs,
info.getSettings().getExpiration() - info.getSettings().getExpiration() -
info.getSettings().getCreated()); info.getSettings().getCreated());
if (numMsgs > lastAvg)
_context.statManager().addRateData("tunnel.participatingMessagesProcessedActive",
numMsgs-lastAvg,
info.getSettings().getExpiration() -
info.getSettings().getCreated());
break; break;
case TunnelId.TYPE_UNSPECIFIED: case TunnelId.TYPE_UNSPECIFIED:
default: default:
@ -651,9 +659,9 @@ class TunnelPool {
return settings; return settings;
} }
public void renderStatusHTML(OutputStream out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
if (!_isLive) return; if (!_isLive) return;
out.write("<h2>Tunnel Pool</h2>\n".getBytes()); out.write("<h2>Tunnel Pool</h2>\n");
StringBuffer buf = new StringBuffer(4096); StringBuffer buf = new StringBuffer(4096);
renderTunnels(out, buf, "Free inbound tunnels", getFreeTunnels()); renderTunnels(out, buf, "Free inbound tunnels", getFreeTunnels());
renderTunnels(out, buf, "Outbound tunnels", getOutboundTunnels()); renderTunnels(out, buf, "Outbound tunnels", getOutboundTunnels());
@ -665,19 +673,19 @@ class TunnelPool {
} }
} }
private void renderTunnels(OutputStream out, StringBuffer buf, String msg, Set tunnelIds) throws IOException { private void renderTunnels(Writer out, StringBuffer buf, String msg, Set tunnelIds) throws IOException {
buf.append("<b>").append(msg).append(":</b> <i>(").append(tunnelIds.size()).append(" tunnels)</i><ul>\n"); buf.append("<b>").append(msg).append(":</b> <i>(").append(tunnelIds.size()).append(" tunnels)</i><ul>\n");
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
for (Iterator iter = tunnelIds.iterator(); iter.hasNext(); ) { for (Iterator iter = tunnelIds.iterator(); iter.hasNext(); ) {
TunnelId id = (TunnelId)iter.next(); TunnelId id = (TunnelId)iter.next();
TunnelInfo tunnel = getTunnelInfo(id); TunnelInfo tunnel = getTunnelInfo(id);
renderTunnel(out, buf, id, tunnel); renderTunnel(out, buf, id, tunnel);
} }
out.write("</ul>\n".getBytes()); out.write("</ul>\n");
} }
private final void renderTunnel(OutputStream out, StringBuffer buf, TunnelId id, TunnelInfo tunnel) throws IOException { private final void renderTunnel(Writer out, StringBuffer buf, TunnelId id, TunnelInfo tunnel) throws IOException {
buf.setLength(0); buf.setLength(0);
if (tunnel == null) { if (tunnel == null) {
buf.append("<li>Tunnel: ").append(id.getTunnelId()).append(" is not known</li>\n"); buf.append("<li>Tunnel: ").append(id.getTunnelId()).append(" is not known</li>\n");
@ -713,7 +721,7 @@ class TunnelPool {
buf.append("\n</pre>"); buf.append("\n</pre>");
} }
out.write(buf.toString().getBytes()); out.write(buf.toString());
buf.setLength(0); buf.setLength(0);
} }