diff --git a/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java index b605cbf361bc1efd8da3485395cc69d39bbd8f4c..db64f77c2588763efd7ba9f52de273bdbb68875d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java @@ -9,13 +9,14 @@ public class TunnelHelper extends HelperBase { public TunnelHelper() {} public String getTunnelSummary() { + TunnelRenderer renderer = new TunnelRenderer(_context); try { if (_out != null) { - _context.tunnelManager().renderStatusHTML(_out); + renderer.renderStatusHTML(_out); return ""; } else { ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024); - _context.tunnelManager().renderStatusHTML(new OutputStreamWriter(baos)); + renderer.renderStatusHTML(new OutputStreamWriter(baos)); return new String(baos.toByteArray()); } } catch (IOException ioe) { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..656068f11cd62db3c25349667b7dcb793a4a5ac9 --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java @@ -0,0 +1,313 @@ +package net.i2p.router.web; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.i2p.data.DataHelper; +import net.i2p.data.Destination; +import net.i2p.data.Hash; +import net.i2p.data.RouterInfo; +import net.i2p.data.TunnelId; +import net.i2p.router.Router; +import net.i2p.router.RouterContext; +import net.i2p.router.TunnelInfo; +import net.i2p.router.TunnelPoolSettings; +import net.i2p.router.tunnel.HopConfig; +import net.i2p.router.tunnel.pool.TunnelPool; +import net.i2p.stat.RateStat; +import net.i2p.util.ObjectCounter; + +/** + * + */ +public class TunnelRenderer { + private RouterContext _context; + + public TunnelRenderer(RouterContext ctx) { + _context = ctx; + } + + public void renderStatusHTML(Writer out) throws IOException { + out.write("<div class=\"wideload\"><h2><a name=\"exploratory\" ></a>Exploratory tunnels (<a href=\"/configtunnels.jsp#exploratory\">config</a>):</h2>\n"); + renderPool(out, _context.tunnelManager().getInboundExploratoryPool(), _context.tunnelManager().getOutboundExploratoryPool()); + + List<Hash> destinations = null; + Map<Hash, TunnelPool> clientInboundPools = _context.tunnelManager().getInboundClientPools(); + Map<Hash, TunnelPool> clientOutboundPools = _context.tunnelManager().getOutboundClientPools(); + destinations = new ArrayList(clientInboundPools.keySet()); + for (int i = 0; i < destinations.size(); i++) { + Hash client = destinations.get(i); + TunnelPool in = null; + TunnelPool outPool = null; + in = clientInboundPools.get(client); + outPool = clientOutboundPools.get(client); + String name = (in != null ? in.getSettings().getDestinationNickname() : null); + if ( (name == null) && (outPool != null) ) + name = outPool.getSettings().getDestinationNickname(); + if (name == null) + name = client.toBase64().substring(0,4); + out.write("<h2><a name=\"" + client.toBase64().substring(0,4) + + "\" ></a>Client tunnels for " + name); + if (_context.clientManager().isLocal(client)) + out.write(" (<a href=\"/configtunnels.jsp#" + client.toBase64().substring(0,4) +"\">config</a>):</h2>\n"); + else + out.write(" (dead):</h2>\n"); + renderPool(out, in, outPool); + } + + List participating = _context.tunnelDispatcher().listParticipatingTunnels(); + Collections.sort(participating, new TunnelComparator()); + out.write("<h2><a name=\"participating\"></a>Participating tunnels:</h2><table>\n"); + out.write("<tr><th>Receive on</th><th>From</th><th>" + + "Send on</th><th>To</th><th>Expiration</th>" + + "<th>Usage</th><th>Rate</th><th>Role</th></tr>\n"); + long processed = 0; + RateStat rs = _context.statManager().getRate("tunnel.participatingMessageCount"); + if (rs != null) + processed = (long)rs.getRate(10*60*1000).getLifetimeTotalValue(); + int inactive = 0; + for (int i = 0; i < participating.size(); i++) { + HopConfig cfg = (HopConfig)participating.get(i); + if (cfg.getProcessedMessagesCount() <= 0) { + inactive++; + continue; + } + out.write("<tr>"); + if (cfg.getReceiveTunnel() != null) + out.write(" <td class=\"cells\" align=\"center\">" + cfg.getReceiveTunnel().getTunnelId() +"</td>"); + else + out.write(" <td class=\"cells\" align=\"center\">n/a</td>"); + if (cfg.getReceiveFrom() != null) + out.write(" <td class=\"cells\" align=\"right\">" + netDbLink(cfg.getReceiveFrom()) +"</td>"); + else + out.write(" <td class=\"cells\" align=\"center\"> </td>"); + if (cfg.getSendTunnel() != null) + out.write(" <td class=\"cells\" align=\"center\">" + cfg.getSendTunnel().getTunnelId() +"</td>"); + else + out.write(" <td class=\"cells\" align=\"center\"> </td>"); + if (cfg.getSendTo() != null) + out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(cfg.getSendTo()) +"</td>"); + else +// out.write(" <td class=\"cells\" align=\"center\"> </td>"); + out.write(" <td class=\"cells\" align=\"center\"> </td>"); + long timeLeft = cfg.getExpiration()-_context.clock().now(); + if (timeLeft > 0) + out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>"); + else + out.write(" <td class=\"cells\" align=\"center\">(grace period)</td>"); + out.write(" <td class=\"cells\" align=\"center\">" + cfg.getProcessedMessagesCount() + "KB</td>"); + int lifetime = (int) ((_context.clock().now() - cfg.getCreation()) / 1000); + if (lifetime <= 0) + lifetime = 1; + if (lifetime > 10*60) + lifetime = 10*60; + int bps = 1024 * (int) cfg.getProcessedMessagesCount() / lifetime; + out.write(" <td class=\"cells\" align=\"center\">" + bps + "Bps</td>"); + if (cfg.getSendTo() == null) + out.write(" <td class=\"cells\" align=\"center\">Outbound Endpoint</td>"); + else if (cfg.getReceiveFrom() == null) + out.write(" <td class=\"cells\" align=\"center\">Inbound Gateway</td>"); + else + out.write(" <td class=\"cells\" align=\"center\">Participant</td>"); + out.write("</tr>\n"); + processed += cfg.getProcessedMessagesCount(); + } + out.write("</table>\n"); + out.write("<div class=\"statusnotes\"><b>Inactive participating tunnels: " + inactive + "</b></div>\n"); + out.write("<div class=\"statusnotes\"><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B</b></div>\n"); + renderPeers(out); + } + + private static class TunnelComparator implements Comparator { + public int compare(Object l, Object r) { + return (int) (((HopConfig)r).getProcessedMessagesCount() - ((HopConfig)l).getProcessedMessagesCount()); + } + } + + private void renderPool(Writer out, TunnelPool in, TunnelPool outPool) throws IOException { + List<TunnelInfo> tunnels = null; + if (in == null) + tunnels = new ArrayList(); + else + tunnels = in.listTunnels(); + if (outPool != null) + tunnels.addAll(outPool.listTunnels()); + + long processedIn = (in != null ? in.getLifetimeProcessed() : 0); + long processedOut = (outPool != null ? outPool.getLifetimeProcessed() : 0); + + int live = 0; + int maxLength = 1; + for (int i = 0; i < tunnels.size(); i++) { + TunnelInfo info = tunnels.get(i); + if (info.getLength() > maxLength) + maxLength = info.getLength(); + } + out.write("<table><tr><th>In/Out</th><th>Expiry</th><th>Usage</th><th>Gateway</th>"); + if (maxLength > 3) { + out.write("<th align=\"center\" colspan=\"" + (maxLength - 2)); + out.write("\">Participants</th>"); + } + else if (maxLength == 3) { + out.write("<th>Participant</th>"); + } + if (maxLength > 1) { + out.write("<th>Endpoint</th>"); + } + out.write("</tr>\n"); + for (int i = 0; i < tunnels.size(); i++) { + TunnelInfo info = tunnels.get(i); + long timeLeft = info.getExpiration()-_context.clock().now(); + if (timeLeft <= 0) + continue; // don't display tunnels in their grace period + live++; + if (info.isInbound()) + out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/inbound.png\" alt=\"Inbound\" title=\"Inbound\"></td>"); + else + out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/outbound.png\" alt=\"Outbound\" title=\"Outbound\"></td>"); + out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>\n"); + out.write(" <td class=\"cells\" align=\"center\">" + info.getProcessedMessagesCount() + "KB</td>\n"); + for (int j = 0; j < info.getLength(); j++) { + Hash peer = info.getPeer(j); + TunnelId id = (info.isInbound() ? info.getReceiveTunnelId(j) : info.getSendTunnelId(j)); + if (_context.routerHash().equals(peer)) { + out.write(" <td class=\"cells\" align=\"center\">" + (id == null ? "" : "" + id) + "</td>"); + } else { + String cap = getCapacity(peer); + out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(peer) + (id == null ? "" : " " + id) + cap + "</td>"); + } + if (info.getLength() < maxLength && (info.getLength() == 1 || j == info.getLength() - 2)) { + for (int k = info.getLength(); k < maxLength; k++) + out.write(" <td class=\"cells\" align=\"center\"> </td>"); + } + } + out.write("</tr>\n"); + + if (info.isInbound()) + processedIn += info.getProcessedMessagesCount(); + else + processedOut += info.getProcessedMessagesCount(); + } + out.write("</table>\n"); + if (in != null) { + List pending = in.listPending(); + if (pending.size() > 0) + out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " inbound</b></center></div>\n"); + live += pending.size(); + } + if (outPool != null) { + List pending = outPool.listPending(); + if (pending.size() > 0) + out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " outbound</b></center></div>\n"); + live += pending.size(); + } + if (live <= 0) + out.write("<div class=\"statusnotes\"><center><b>No tunnels; waiting for the grace period to end.</center></b></div>\n"); + out.write("<div class=\"statusnotes\"><center><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processedIn*1024) + "B in, " + + DataHelper.formatSize(processedOut*1024) + "B out</b></center></div>"); + } + + private void renderPeers(Writer out) throws IOException { + // count up the peers in the local pools + ObjectCounter<Hash> lc = new ObjectCounter(); + int tunnelCount = countTunnelsPerPeer(lc); + + // count up the peers in the participating tunnels + ObjectCounter<Hash> pc = new ObjectCounter(); + int partCount = countParticipatingPerPeer(pc); + + Set<Hash> peers = new HashSet(lc.objects()); + peers.addAll(pc.objects()); + List<Hash> peerList = new ArrayList(peers); + Collections.sort(peerList, new HashComparator()); + + out.write("<h2><a name=\"peers\"></a>Tunnel Counts By Peer:</h2>\n"); + out.write("<table><tr><th>Peer</th><th>Expl. + Client</th><th>% of total</th><th>Part. from + to</th><th>% of total</th></tr>\n"); + for (Hash h : peerList) { + out.write("<tr> <td class=\"cells\" align=\"center\">"); + out.write(netDbLink(h)); + out.write(" <td class=\"cells\" align=\"center\">" + lc.count(h)); + out.write(" <td class=\"cells\" align=\"center\">"); + if (tunnelCount > 0) + out.write("" + (lc.count(h) * 100 / tunnelCount)); + else + out.write('0'); + out.write(" <td class=\"cells\" align=\"center\">" + pc.count(h)); + out.write(" <td class=\"cells\" align=\"center\">"); + if (partCount > 0) + out.write("" + (pc.count(h) * 100 / partCount)); + else + out.write('0'); + out.write('\n'); + } + out.write("<tr class=\"tablefooter\"> <td align=\"center\"><b>Tunnels</b> <td align=\"center\"><b>" + tunnelCount); + out.write("</b> <td> </td> <td align=\"center\"><b>" + partCount); + out.write("</b> <td> </td></tr></table></div>\n"); + } + + /* duplicate of that in tunnelPoolManager for now */ + /** @return total number of non-fallback expl. + client tunnels */ + private int countTunnelsPerPeer(ObjectCounter<Hash> lc) { + List<TunnelPool> pools = new ArrayList(); + _context.tunnelManager().listPools(pools); + int tunnelCount = 0; + for (TunnelPool tp : pools) { + for (TunnelInfo info : tp.listTunnels()) { + if (info.getLength() > 1) { + tunnelCount++; + for (int j = 0; j < info.getLength(); j++) { + Hash peer = info.getPeer(j); + if (!_context.routerHash().equals(peer)) + lc.increment(peer); + } + } + } + } + return tunnelCount; + } + + /** @return total number of part. tunnels */ + private int countParticipatingPerPeer(ObjectCounter<Hash> pc) { + List<HopConfig> participating = _context.tunnelDispatcher().listParticipatingTunnels(); + for (HopConfig cfg : participating) { + Hash from = cfg.getReceiveFrom(); + if (from != null) + pc.increment(from); + Hash to = cfg.getSendTo(); + if (to != null) + pc.increment(to); + } + return participating.size(); + } + + private static class HashComparator implements Comparator { + public int compare(Object l, Object r) { + return ((Hash)l).toBase64().compareTo(((Hash)r).toBase64()); + } + } + + private String getCapacity(Hash peer) { + RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); + if (info != null) { + String caps = info.getCapabilities(); + for (char c = Router.CAPABILITY_BW12; c <= Router.CAPABILITY_BW256; c++) { + if (caps.indexOf(c) >= 0) + return " " + c; + } + } + return ""; + } + + private String netDbLink(Hash peer) { + return _context.commSystem().renderPeerHTML(peer); + } +} diff --git a/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java b/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java index 3a19cd1178946638d7023e10b9e466719663bfe0..4b8fb486e45f7e395c333fdb0cf0309319ad6ef3 100644 --- a/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java +++ b/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java @@ -10,11 +10,14 @@ package net.i2p.router; import java.io.IOException; import java.io.Writer; +import java.util.List; +import java.util.Map; import java.util.Set; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.TunnelId; +import net.i2p.router.tunnel.pool.TunnelPool; /** * Build and maintain tunnels throughout the network. @@ -50,4 +53,10 @@ class DummyTunnelManagerFacade implements TunnelManagerFacade { public void restart() {} public void shutdown() {} public void startup() {} + + public void listPools(List<TunnelPool> out) {} + public Map<Hash, TunnelPool> getInboundClientPools() { return null; } + public Map<Hash, TunnelPool> getOutboundClientPools() { return null; } + public TunnelPool getInboundExploratoryPool() { return null; } + public TunnelPool getOutboundExploratoryPool() { return null; } } diff --git a/router/java/src/net/i2p/router/TunnelManagerFacade.java b/router/java/src/net/i2p/router/TunnelManagerFacade.java index e8bdc31d380460596c721d9c710207ca6f350770..148499a43d4991ed7bddeba3485837be105c6336 100644 --- a/router/java/src/net/i2p/router/TunnelManagerFacade.java +++ b/router/java/src/net/i2p/router/TunnelManagerFacade.java @@ -8,11 +8,14 @@ package net.i2p.router; * */ +import java.util.List; +import java.util.Map; import java.util.Set; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.TunnelId; +import net.i2p.router.tunnel.pool.TunnelPool; /** * Build and maintain tunnels throughout the network. @@ -74,4 +77,14 @@ public interface TunnelManagerFacade extends Service { public void setOutboundSettings(TunnelPoolSettings settings); public void setInboundSettings(Hash client, TunnelPoolSettings settings); public void setOutboundSettings(Hash client, TunnelPoolSettings settings); + /** for TunnelRenderer in router console */ + public void listPools(List<TunnelPool> out); + /** for TunnelRenderer in router console */ + public Map<Hash, TunnelPool> getInboundClientPools(); + /** for TunnelRenderer in router console */ + public Map<Hash, TunnelPool> getOutboundClientPools(); + /** for TunnelRenderer in router console */ + public TunnelPool getInboundExploratoryPool(); + /** for TunnelRenderer in router console */ + public TunnelPool getOutboundExploratoryPool(); } diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index f7c752c8c981ed1d61f2271888d4d8a2120cc81c..d879750359ba08992cb4effd9c856040c1dea5f4 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -391,7 +391,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { } /** list of TunnelPool instances currently in play */ - void listPools(List<TunnelPool> out) { + public void listPools(List<TunnelPool> out) { synchronized (_clientInboundPools) { out.addAll(_clientInboundPools.values()); } @@ -409,227 +409,8 @@ public class TunnelPoolManager implements TunnelManagerFacade { public int getInboundBuildQueueSize() { return _executor.getInboundBuildQueueSize(); } - + /** @deprecated moved to routerconsole */ public void renderStatusHTML(Writer out) throws IOException { - out.write("<div class=\"wideload\"><h2><a name=\"exploratory\" ></a>Exploratory tunnels (<a href=\"/configtunnels.jsp#exploratory\">config</a>):</h2>\n"); - renderPool(out, _inboundExploratory, _outboundExploratory); - - List<Hash> destinations = null; - synchronized (_clientInboundPools) { - destinations = new ArrayList(_clientInboundPools.keySet()); - } - for (int i = 0; i < destinations.size(); i++) { - Hash client = destinations.get(i); - TunnelPool in = null; - TunnelPool outPool = null; - synchronized (_clientInboundPools) { - in = _clientInboundPools.get(client); - } - synchronized (_clientOutboundPools) { - outPool = _clientOutboundPools.get(client); - } - String name = (in != null ? in.getSettings().getDestinationNickname() : null); - if ( (name == null) && (outPool != null) ) - name = outPool.getSettings().getDestinationNickname(); - if (name == null) - name = client.toBase64().substring(0,4); - out.write("<h2><a name=\"" + client.toBase64().substring(0,4) - + "\" ></a>Client tunnels for " + name); - if (_context.clientManager().isLocal(client)) - out.write(" (<a href=\"/configtunnels.jsp#" + client.toBase64().substring(0,4) +"\">config</a>):</h2>\n"); - else - out.write(" (dead):</h2>\n"); - renderPool(out, in, outPool); - } - - List participating = _context.tunnelDispatcher().listParticipatingTunnels(); - Collections.sort(participating, new TunnelComparator()); - out.write("<h2><a name=\"participating\"></a>Participating tunnels:</h2><table>\n"); - out.write("<tr><th>Receive on</th><th>From</th><th>" - + "Send on</th><th>To</th><th>Expiration</th>" - + "<th>Usage</th><th>Rate</th><th>Role</th></tr>\n"); - long processed = 0; - RateStat rs = _context.statManager().getRate("tunnel.participatingMessageCount"); - if (rs != null) - processed = (long)rs.getRate(10*60*1000).getLifetimeTotalValue(); - int inactive = 0; - for (int i = 0; i < participating.size(); i++) { - HopConfig cfg = (HopConfig)participating.get(i); - if (cfg.getProcessedMessagesCount() <= 0) { - inactive++; - continue; - } - out.write("<tr>"); - if (cfg.getReceiveTunnel() != null) - out.write(" <td class=\"cells\" align=\"center\">" + cfg.getReceiveTunnel().getTunnelId() +"</td>"); - else - out.write(" <td class=\"cells\" align=\"center\">n/a</td>"); - if (cfg.getReceiveFrom() != null) - out.write(" <td class=\"cells\" align=\"right\">" + netDbLink(cfg.getReceiveFrom()) +"</td>"); - else - out.write(" <td class=\"cells\" align=\"center\"> </td>"); - if (cfg.getSendTunnel() != null) - out.write(" <td class=\"cells\" align=\"center\">" + cfg.getSendTunnel().getTunnelId() +"</td>"); - else - out.write(" <td class=\"cells\" align=\"center\"> </td>"); - if (cfg.getSendTo() != null) - out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(cfg.getSendTo()) +"</td>"); - else -// out.write(" <td class=\"cells\" align=\"center\"> </td>"); - out.write(" <td class=\"cells\" align=\"center\"> </td>"); - long timeLeft = cfg.getExpiration()-_context.clock().now(); - if (timeLeft > 0) - out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>"); - else - out.write(" <td class=\"cells\" align=\"center\">(grace period)</td>"); - out.write(" <td class=\"cells\" align=\"center\">" + cfg.getProcessedMessagesCount() + "KB</td>"); - int lifetime = (int) ((_context.clock().now() - cfg.getCreation()) / 1000); - if (lifetime <= 0) - lifetime = 1; - if (lifetime > 10*60) - lifetime = 10*60; - int bps = 1024 * (int) cfg.getProcessedMessagesCount() / lifetime; - out.write(" <td class=\"cells\" align=\"center\">" + bps + "Bps</td>"); - if (cfg.getSendTo() == null) - out.write(" <td class=\"cells\" align=\"center\">Outbound Endpoint</td>"); - else if (cfg.getReceiveFrom() == null) - out.write(" <td class=\"cells\" align=\"center\">Inbound Gateway</td>"); - else - out.write(" <td class=\"cells\" align=\"center\">Participant</td>"); - out.write("</tr>\n"); - processed += cfg.getProcessedMessagesCount(); - } - out.write("</table>\n"); - out.write("<div class=\"statusnotes\"><b>Inactive participating tunnels: " + inactive + "</b></div>\n"); - out.write("<div class=\"statusnotes\"><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B</b></div>\n"); - renderPeers(out); - } - - class TunnelComparator implements Comparator { - public int compare(Object l, Object r) { - return (int) (((HopConfig)r).getProcessedMessagesCount() - ((HopConfig)l).getProcessedMessagesCount()); - } - } - - private void renderPool(Writer out, TunnelPool in, TunnelPool outPool) throws IOException { - List<TunnelInfo> tunnels = null; - if (in == null) - tunnels = new ArrayList(); - else - tunnels = in.listTunnels(); - if (outPool != null) - tunnels.addAll(outPool.listTunnels()); - - long processedIn = (in != null ? in.getLifetimeProcessed() : 0); - long processedOut = (outPool != null ? outPool.getLifetimeProcessed() : 0); - - int live = 0; - int maxLength = 1; - for (int i = 0; i < tunnels.size(); i++) { - TunnelInfo info = tunnels.get(i); - if (info.getLength() > maxLength) - maxLength = info.getLength(); - } - out.write("<table><tr><th>In/Out</th><th>Expiry</th><th>Usage</th><th>Gateway</th>"); - if (maxLength > 3) { - out.write("<th align=\"center\" colspan=\"" + (maxLength - 2)); - out.write("\">Participants</th>"); - } - else if (maxLength == 3) { - out.write("<th>Participant</th>"); - } - if (maxLength > 1) { - out.write("<th>Endpoint</th>"); - } - out.write("</tr>\n"); - for (int i = 0; i < tunnels.size(); i++) { - TunnelInfo info = tunnels.get(i); - long timeLeft = info.getExpiration()-_context.clock().now(); - if (timeLeft <= 0) - continue; // don't display tunnels in their grace period - live++; - if (info.isInbound()) - out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/inbound.png\" alt=\"Inbound\" title=\"Inbound\"></td>"); - else - out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/outbound.png\" alt=\"Outbound\" title=\"Outbound\"></td>"); - out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>\n"); - out.write(" <td class=\"cells\" align=\"center\">" + info.getProcessedMessagesCount() + "KB</td>\n"); - for (int j = 0; j < info.getLength(); j++) { - Hash peer = info.getPeer(j); - TunnelId id = (info.isInbound() ? info.getReceiveTunnelId(j) : info.getSendTunnelId(j)); - if (_context.routerHash().equals(peer)) { - out.write(" <td class=\"cells\" align=\"center\">" + (id == null ? "" : "" + id) + "</td>"); - } else { - String cap = getCapacity(peer); - out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(peer) + (id == null ? "" : " " + id) + cap + "</td>"); - } - if (info.getLength() < maxLength && (info.getLength() == 1 || j == info.getLength() - 2)) { - for (int k = info.getLength(); k < maxLength; k++) - out.write(" <td class=\"cells\" align=\"center\"> </td>"); - } - } - out.write("</tr>\n"); - - if (info.isInbound()) - processedIn += info.getProcessedMessagesCount(); - else - processedOut += info.getProcessedMessagesCount(); - } - out.write("</table>\n"); - if (in != null) { - List pending = in.listPending(); - if (pending.size() > 0) - out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " inbound</b></center></div>\n"); - live += pending.size(); - } - if (outPool != null) { - List pending = outPool.listPending(); - if (pending.size() > 0) - out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " outbound</b></center></div>\n"); - live += pending.size(); - } - if (live <= 0) - out.write("<div class=\"statusnotes\"><center><b>No tunnels; waiting for the grace period to end.</center></b></div>\n"); - out.write("<div class=\"statusnotes\"><center><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processedIn*1024) + "B in, " + - DataHelper.formatSize(processedOut*1024) + "B out</b></center></div>"); - } - - private void renderPeers(Writer out) throws IOException { - // count up the peers in the local pools - ObjectCounter<Hash> lc = new ObjectCounter(); - int tunnelCount = countTunnelsPerPeer(lc); - - // count up the peers in the participating tunnels - ObjectCounter<Hash> pc = new ObjectCounter(); - int partCount = countParticipatingPerPeer(pc); - - Set<Hash> peers = new HashSet(lc.objects()); - peers.addAll(pc.objects()); - List<Hash> peerList = new ArrayList(peers); - Collections.sort(peerList, new HashComparator()); - - out.write("<h2><a name=\"peers\"></a>Tunnel Counts By Peer:</h2>\n"); - out.write("<table><tr><th>Peer</th><th>Expl. + Client</th><th>% of total</th><th>Part. from + to</th><th>% of total</th></tr>\n"); - for (Hash h : peerList) { - out.write("<tr> <td class=\"cells\" align=\"center\">"); - out.write(netDbLink(h)); - out.write(" <td class=\"cells\" align=\"center\">" + lc.count(h)); - out.write(" <td class=\"cells\" align=\"center\">"); - if (tunnelCount > 0) - out.write("" + (lc.count(h) * 100 / tunnelCount)); - else - out.write('0'); - out.write(" <td class=\"cells\" align=\"center\">" + pc.count(h)); - out.write(" <td class=\"cells\" align=\"center\">"); - if (partCount > 0) - out.write("" + (pc.count(h) * 100 / partCount)); - else - out.write('0'); - out.write('\n'); - } - out.write("<tr class=\"tablefooter\"> <td align=\"center\"><b>Tunnels</b> <td align=\"center\"><b>" + tunnelCount); - out.write("</b> <td> </td> <td align=\"center\"><b>" + partCount); - out.write("</b> <td> </td></tr></table></div>\n"); } /** @return total number of non-fallback expl. + client tunnels */ @@ -682,39 +463,27 @@ public class TunnelPoolManager implements TunnelManagerFacade { return rv; } - /** @return total number of part. tunnels */ - private int countParticipatingPerPeer(ObjectCounter<Hash> pc) { - List<HopConfig> participating = _context.tunnelDispatcher().listParticipatingTunnels(); - for (HopConfig cfg : participating) { - Hash from = cfg.getReceiveFrom(); - if (from != null) - pc.increment(from); - Hash to = cfg.getSendTo(); - if (to != null) - pc.increment(to); + /** for TunnelRenderer in router console */ + public Map<Hash, TunnelPool> getInboundClientPools() { + synchronized (_clientInboundPools) { + return new HashMap(_clientInboundPools); } - return participating.size(); } - class HashComparator implements Comparator { - public int compare(Object l, Object r) { - return ((Hash)l).toBase64().compareTo(((Hash)r).toBase64()); + /** for TunnelRenderer in router console */ + public Map<Hash, TunnelPool> getOutboundClientPools() { + synchronized (_clientOutboundPools) { + return new HashMap(_clientOutboundPools); } } - private String getCapacity(Hash peer) { - RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); - if (info != null) { - String caps = info.getCapabilities(); - for (char c = Router.CAPABILITY_BW12; c <= Router.CAPABILITY_BW256; c++) { - if (caps.indexOf(c) >= 0) - return " " + c; - } - } - return ""; + /** for TunnelRenderer in router console */ + public TunnelPool getInboundExploratoryPool() { + return _inboundExploratory; } - private String netDbLink(Hash peer) { - return _context.commSystem().renderPeerHTML(peer); + /** for TunnelRenderer in router console */ + public TunnelPool getOutboundExploratoryPool() { + return _outboundExploratory; } }