propagate from branch 'i2p.i2p.zzz.test' (head efc35e19029b7d92265f25eb024114737b6545e1)

to branch 'i2p.i2p' (head bbf149917e39bdd2f53246c238d440369498ae09)
This commit is contained in:
zzz
2009-10-20 16:01:13 +00:00
39 changed files with 2008 additions and 665 deletions

View File

@ -73,11 +73,26 @@
<delete dir="./tmpextract" />
<ant target="war" />
<!-- Update the messages_*.po files.
We need to supply the bat file for windows, and then change the fail property to true -->
<exec executable="sh" osfamily="unix" failifexecutionfails="false" >
<arg value="./bundle-messages.sh" />
</exec>
<exec executable="sh" osfamily="mac" failifexecutionfails="false" >
<arg value="./bundle-messages.sh" />
</exec>
<exec executable="cmd" osfamily="windows" failifexecutionfails="false" >
<arg value="/c" />
<arg value="bundle-messages.bat" />
</exec>
<!-- jar again to get the latest messages_*.class files -->
<jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class" update="true" />
</target>
<target name="war" depends="precompilejsp">
<!-- Don't include the css in the war, the main build.xml will copy it to docs/themes/console/ -->
<war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml"
basedir="../jsp/" excludes="web.xml, *.css, *.java, *.jsp, web-fragment.xml">
basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, web-fragment.xml">
</war>
</target>
<target name="precompilejsp" unless="precompilejsp.uptodate">
@ -115,6 +130,7 @@
</java>
<javac debug="true" deprecation="on" source="1.5" target="1.5"
encoding="UTF-8"
destdir="../jsp/WEB-INF/classes/"
srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
<compilerarg line="${javac.compilerargs}" />
@ -133,10 +149,13 @@
<pathelement location="../../../core/java/build/i2p.jar" />
</classpath>
</javac>
<!-- save these so we can run gettext on the generated java files later
<delete>
<fileset dir="../jsp/WEB-INF/" includes="**/*.java" />
<fileset dir="../jsp/WEB-INF/" includes="**/*.jsp" />
</delete>
-->
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
<replace file="../jsp/web-out.xml">

View File

@ -0,0 +1,67 @@
#
# Update messages_xx.po and messages_xx.class files,
# from both java and jsp sources.
# Requires installed programs xgettext, msgfmt, msgmerge, and find.
# zzz - public domain
#
CLASS=net.i2p.router.web.messages
TMPFILE=build/javafiles.txt
export TZ=UTC
for i in ../locale/messages_*.po
do
# get language
LG=${i#../locale/messages_}
LG=${LG%.po}
# make list of java files newer than the .po file
find src ../jsp/WEB-INF -name *.java -newer $i > $TMPFILE
if [ -s build/obj/net/i2p/router/web/messages_$LG.class -a ! -s $TMPFILE ]
then
continue
fi
echo "Generating ${CLASS}_$LG ResourceBundle..."
# extract strings from java and jsp files, and update messages.po files
# translate calls must be one of the forms:
# _("foo")
# cssHelper._("foo")
# cssHelper.title("foo")
# handler._("foo")
# formhandler._("foo")
# In a jsp, you must use a helper or handler that has the context set.
# To start a new translation, copy the header from an old translation to the new .po file,
# then ant distclean updater.
find src ../jsp/WEB-INF -name *.java > $TMPFILE
xgettext -f $TMPFILE -F -L java \
--keyword=_ --keyword=cssHelper._ --keyword=cssHelper.title --keyword=handler._ --keyword=formhandler._ \
-o ${i}t
if [ $? -ne 0 ]
then
echo 'Warning - xgettext failed, not updating translations'
rm -f ${i}t
break
fi
msgmerge -U --backup=none $i ${i}t
if [ $? -ne 0 ]
then
echo 'Warning - msgmerge failed, not updating translations'
rm -f ${i}t
break
fi
rm -f ${i}t
# so we don't do this again
touch $i
# convert to class files in build/obj
msgfmt --java -r $CLASS -l $LG -d build/obj $i
if [ $? -ne 0 ]
then
echo 'Warning - xgettext failed, not updating translations'
break
fi
done
rm -f $TMPFILE
# todo: return failure
exit 0

View File

@ -22,4 +22,21 @@ public class CSSHelper extends HelperBase {
}
return url;
}
/** change default language for the router but don't save it */
public void setLang(String lang) {
if (lang != null && lang.length() > 0)
_context.router().setConfigSetting(Messages.PROP_LANG, lang);
}
/** translate the title and display consistently */
public String title(String s) {
StringBuilder buf = new StringBuilder(128);
buf.append("<title>")
.append(_("I2P Router Console"))
.append(" - ")
.append(_(s))
.append("</title>");
return buf.toString();
}
}

View File

@ -87,7 +87,7 @@ public class ConfigUpdateHelper extends HelperBase {
buf.append("<select name=\"updatePolicy\">");
if ("notify".equals(policy))
buf.append("<option value=\"notify\" selected=\"true\">Notify only</option>");
buf.append("<option value=\"notify\" selected=\"true\">").append(_("Notify only")).append("</option>");
else
buf.append("<option value=\"notify\">Notify only</option>");

View File

@ -62,7 +62,8 @@ public class ContentHelper extends HelperBase {
/**
* Convert file.ext to file_lang.ext if it exists.
* Get lang from either the cgi lang param or from the default locale.
* Get lang from the cgi lang param, then properties, then from the default locale.
* _context must be set to check the property.
*/
private String filename() {
int lastdot = _page.lastIndexOf('.');
@ -70,9 +71,13 @@ public class ContentHelper extends HelperBase {
return _page;
String lang = _lang;
if (lang == null || lang.length() <= 0) {
lang = Locale.getDefault().getLanguage();
if (lang == null || lang.length() <= 0)
return _page;
if (_context != null)
lang = _context.getProperty(Messages.PROP_LANG);
if (lang == null || lang.length() <= 0) {
lang = Locale.getDefault().getLanguage();
if (lang == null || lang.length() <= 0)
return _page;
}
}
if (lang.equals("en"))
return _page;

View File

@ -190,4 +190,8 @@ public class FormHandler {
}
}
/** translate a string */
public String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -29,4 +29,9 @@ public abstract class HelperBase {
//public RouterContext getContext() { return _context; }
public void setWriter(Writer out) { _out = out; }
/** translate a string */
public String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -0,0 +1,69 @@
package net.i2p.router.web;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import net.i2p.I2PAppContext;
import org.mortbay.http.HttpRequest;
import org.mortbay.http.HttpResponse;
import org.mortbay.jetty.servlet.WebApplicationHandler;
/**
* Convert foo.jsp to foo_xx.jsp for language xx.
* This is appropriate for jsps with large amounts of text.
* This does not work for included jsps (e.g. summary*)
*
* @author zzz
*/
public class LocaleWebAppHandler extends WebApplicationHandler
{
private I2PAppContext _context;
public LocaleWebAppHandler(I2PAppContext ctx) {
super();
_context = ctx;
}
/**
* Handle foo.jsp by converting to foo_xx.jsp
* for language xx, where xx is the language for the default locale,
* or as specified in the routerconsole.lang property.
* Unless language==="en".
*/
public void handle(String pathInContext,
String pathParams,
HttpRequest httpRequest,
HttpResponse httpResponse)
throws IOException
{
//System.err.println("Path: " + pathInContext);
String newPath = pathInContext;
if (pathInContext.endsWith(".jsp")) {
int len = pathInContext.length();
// ...but leave foo_xx.jsp alone
if (len < 8 || pathInContext.charAt(len - 7) != '_') {
String lang = _context.getProperty(Messages.PROP_LANG);
if (lang == null || lang.length() <= 0)
lang = Locale.getDefault().getLanguage();
if (lang != null && lang.length() > 0 && !lang.equals("en")) {
String testPath = pathInContext.substring(0, len - 4) + '_' + lang + ".jsp";
// Do we have a servlet for the new path that isn't the catchall *.jsp?
Map.Entry servlet = getHolderEntry(testPath);
if (servlet != null) {
String servletPath = (String) servlet.getKey();
if (servletPath != null && !servletPath.startsWith("*")) {
// success!!
//System.err.println("Servlet is: " + servletPath);
newPath = testPath;
}
}
}
}
}
//System.err.println("New path: " + newPath);
super.handle(newPath, pathParams, httpRequest, httpResponse);
//System.err.println("Was handled? " + httpRequest.isHandled());
}
}

View File

@ -0,0 +1,80 @@
package net.i2p.router.web;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import net.i2p.util.ConcurrentHashSet;
/**
* Translate strings efficiently.
* We don't include an English or default ResourceBundle, we simply check
* for "en" and return the original string.
* Support real-time language changing with the routerconsole.lang property.
*
* @author zzz, from a base generated by eclipse.
*/
public class Messages {
public static final String PROP_LANG = "routerconsole.lang";
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
private static final String _localeLang = Locale.getDefault().getLanguage();
private static final Map<String, ResourceBundle> _bundles = new ConcurrentHashMap(2);
private static final Set<String> _missing = new ConcurrentHashSet(2);
/** current locale **/
public static String getString(String key) {
if (_localeLang.equals("en"))
return key;
ResourceBundle bundle = findBundle(_localeLang);
if (bundle == null)
return key;
try {
return bundle.getString(key);
} catch (MissingResourceException e) {
return key;
}
}
/** lang in routerconsole.lang property, else current locale */
public static String getString(String key, I2PAppContext ctx) {
String lang = getLanguage(ctx);
if (lang.equals("en"))
return key;
ResourceBundle bundle = findBundle(lang);
if (bundle == null)
return key;
try {
return bundle.getString(key);
} catch (MissingResourceException e) {
return key;
}
}
private static String getLanguage(I2PAppContext ctx) {
String lang = ctx.getProperty(PROP_LANG);
if (lang == null || lang.length() <= 0)
lang = _localeLang;
return lang;
}
/** cache both found and not found for speed */
private static ResourceBundle findBundle(String lang) {
ResourceBundle rv = _bundles.get(lang);
if (rv == null && !_missing.contains(lang)) {
try {
// Would it be faster to specify a class loader?
// No matter we only do this once per lang.
rv = ResourceBundle.getBundle(BUNDLE_NAME, new Locale(lang));
if (rv != null)
_bundles.put(lang, rv);
} catch (MissingResourceException e) {
_missing.add(lang);
}
}
return rv;
}
}

View File

@ -17,23 +17,24 @@ public class NetDbHelper extends HelperBase {
public void setLease(String l) { _lease = "1".equals(l); }
public String getNetDbSummary() {
NetDbRenderer renderer = new NetDbRenderer(_context);
try {
if (_out != null) {
if (_routerPrefix != null)
_context.netDb().renderRouterInfoHTML(_out, _routerPrefix);
renderer.renderRouterInfoHTML(_out, _routerPrefix);
else if (_lease)
_context.netDb().renderLeaseSetHTML(_out);
renderer.renderLeaseSetHTML(_out);
else
_context.netDb().renderStatusHTML(_out, _full);
renderer.renderStatusHTML(_out, _full);
return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
if (_routerPrefix != null)
_context.netDb().renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
renderer.renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
else if (_lease)
_context.netDb().renderLeaseSetHTML(new OutputStreamWriter(baos));
renderer.renderLeaseSetHTML(new OutputStreamWriter(baos));
else
_context.netDb().renderStatusHTML(new OutputStreamWriter(baos), _full);
renderer.renderStatusHTML(new OutputStreamWriter(baos), _full);
return new String(baos.toByteArray());
}
} catch (IOException ioe) {

View File

@ -0,0 +1,275 @@
package net.i2p.router.web;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterAddress;
import net.i2p.data.RouterInfo;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
import net.i2p.util.ObjectCounter;
public class NetDbRenderer {
private RouterContext _context;
public NetDbRenderer (RouterContext ctx) {
_context = ctx;
}
private class LeaseSetComparator implements Comparator {
public int compare(Object l, Object r) {
Destination dl = ((LeaseSet)l).getDestination();
Destination dr = ((LeaseSet)r).getDestination();
boolean locall = _context.clientManager().isLocal(dl);
boolean localr = _context.clientManager().isLocal(dr);
if (locall && !localr) return -1;
if (localr && !locall) return 1;
return dl.calculateHash().toBase64().compareTo(dr.calculateHash().toBase64());
}
}
private static class RouterInfoComparator implements Comparator {
public int compare(Object l, Object r) {
return ((RouterInfo)l).getIdentity().getHash().toBase64().compareTo(((RouterInfo)r).getIdentity().getHash().toBase64());
}
}
public void renderRouterInfoHTML(Writer out, String routerPrefix) throws IOException {
StringBuilder buf = new StringBuilder(4*1024);
buf.append("<h2>Network Database RouterInfo Lookup</h2>\n");
if (".".equals(routerPrefix)) {
renderRouterInfo(buf, _context.router().getRouterInfo(), true, true);
} else {
boolean notFound = true;
Set routers = _context.netDb().getRouters();
for (Iterator iter = routers.iterator(); iter.hasNext(); ) {
RouterInfo ri = (RouterInfo)iter.next();
Hash key = ri.getIdentity().getHash();
if (key.toBase64().startsWith(routerPrefix)) {
renderRouterInfo(buf, ri, false, true);
notFound = false;
}
}
if (notFound)
buf.append("Router ").append(routerPrefix).append(" not found in network database");
}
out.write(buf.toString());
out.flush();
}
public void renderStatusHTML(Writer out) throws IOException {
renderStatusHTML(out, true);
}
public void renderLeaseSetHTML(Writer out) throws IOException {
StringBuilder buf = new StringBuilder(4*1024);
buf.append("<h2>Network Database Contents</h2>\n");
buf.append("<a href=\"netdb.jsp\">View RouterInfo</a>");
buf.append("<h3>LeaseSets</h3>\n");
Set leases = new TreeSet(new LeaseSetComparator());
leases.addAll(_context.netDb().getLeases());
long now = _context.clock().now();
for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
LeaseSet ls = (LeaseSet)iter.next();
Destination dest = ls.getDestination();
Hash key = dest.calculateHash();
buf.append("<b>LeaseSet: ").append(key.toBase64());
if (_context.clientManager().isLocal(dest)) {
buf.append(" (<a href=\"tunnels.jsp#" + key.toBase64().substring(0,4) + "\">Local</a> ");
if (! _context.clientManager().shouldPublishLeaseSet(key))
buf.append("Unpublished ");
buf.append("Destination ");
TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(key);
if (in != null && in.getDestinationNickname() != null)
buf.append(in.getDestinationNickname());
else
buf.append(dest.toBase64().substring(0, 6));
} else {
buf.append(" (Destination ");
String host = _context.namingService().reverseLookup(dest);
if (host != null)
buf.append(host);
else
buf.append(dest.toBase64().substring(0, 6));
}
buf.append(")</b><br>\n");
long exp = ls.getEarliestLeaseDate()-now;
if (exp > 0)
buf.append("Expires in ").append(DataHelper.formatDuration(exp)).append("<br>\n");
else
buf.append("Expired ").append(DataHelper.formatDuration(0-exp)).append(" ago<br>\n");
for (int i = 0; i < ls.getLeaseCount(); i++) {
buf.append("Lease ").append(i + 1).append(": Gateway ");
buf.append(_context.commSystem().renderPeerHTML(ls.getLease(i).getGateway()));
buf.append(" Tunnel ").append(ls.getLease(i).getTunnelId().getTunnelId()).append("<br>\n");
}
buf.append("<hr>\n");
out.write(buf.toString());
buf.setLength(0);
}
out.write(buf.toString());
out.flush();
}
public void renderStatusHTML(Writer out, boolean full) throws IOException {
int size = _context.netDb().getKnownRouters() * 512;
if (full)
size *= 4;
StringBuilder buf = new StringBuilder(size);
out.write("<h2>Network Database Contents (<a href=\"netdb.jsp?l=1\">View LeaseSets</a>)</h2>\n");
if (!_context.netDb().isInitialized()) {
buf.append("Not initialized\n");
out.write(buf.toString());
out.flush();
return;
}
Hash us = _context.routerHash();
out.write("<a name=\"routers\" ></a><h3>Routers (<a href=\"netdb.jsp");
if (full)
out.write("#routers\" >view without");
else
out.write("?f=1#routers\" >view with");
out.write(" stats</a>)</h3>\n");
RouterInfo ourInfo = _context.router().getRouterInfo();
renderRouterInfo(buf, ourInfo, true, true);
out.write(buf.toString());
buf.setLength(0);
ObjectCounter<String> versions = new ObjectCounter();
ObjectCounter<String> countries = new ObjectCounter();
Set routers = new TreeSet(new RouterInfoComparator());
routers.addAll(_context.netDb().getRouters());
for (Iterator iter = routers.iterator(); iter.hasNext(); ) {
RouterInfo ri = (RouterInfo)iter.next();
Hash key = ri.getIdentity().getHash();
boolean isUs = key.equals(us);
if (!isUs) {
renderRouterInfo(buf, ri, false, full);
out.write(buf.toString());
buf.setLength(0);
String routerVersion = ri.getOption("router.version");
if (routerVersion != null)
versions.increment(routerVersion);
String country = _context.commSystem().getCountry(key);
if(country != null)
countries.increment(country);
}
}
buf.append("<table border=\"0\" cellspacing=\"30\"><tr><td>");
List<String> versionList = new ArrayList(versions.objects());
if (versionList.size() > 0) {
Collections.sort(versionList, Collections.reverseOrder());
buf.append("<table>\n");
buf.append("<tr><th>Version</th><th>Count</th></tr>\n");
for (String routerVersion : versionList) {
int num = versions.count(routerVersion);
buf.append("<tr><td align=\"center\">").append(DataHelper.stripHTML(routerVersion));
buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
}
buf.append("</table>\n");
}
buf.append("</td><td>");
out.write(buf.toString());
buf.setLength(0);
List<String> countryList = new ArrayList(countries.objects());
if (countryList.size() > 0) {
Collections.sort(countryList);
buf.append("<table>\n");
buf.append("<tr><th align=\"left\">Country</th><th>Count</th></tr>\n");
for (String country : countryList) {
int num = countries.count(country);
buf.append("<tr><td><img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\"");
buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> ");
buf.append(_context.commSystem().getCountryName(country));
buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
}
buf.append("</table>\n");
}
buf.append("</td></tr></table>");
out.write(buf.toString());
out.flush();
}
/**
* Be careful to use stripHTML for any displayed routerInfo data
* to prevent vulnerabilities
*/
private void renderRouterInfo(StringBuilder buf, RouterInfo info, boolean isUs, boolean full) {
String hash = info.getIdentity().getHash().toBase64();
buf.append("<table><tr><th><a name=\"").append(hash.substring(0, 6)).append("\" ></a>");
if (isUs) {
buf.append("<a name=\"our-info\" ></a><b>Our info: ").append(hash).append("</b></th></tr><tr><td>\n");
} else {
buf.append("<b>Peer info for:</b> ").append(hash).append("\n");
if (full) {
buf.append("[<a href=\"netdb.jsp\" >Back</a>]</th></tr><td>\n");
} else {
buf.append("[<a href=\"netdb.jsp?r=").append(hash.substring(0, 6)).append("\" >Full entry</a>]</th></tr><td>\n");
}
}
long age = _context.clock().now() - info.getPublished();
if (isUs && _context.router().isHidden())
buf.append("<b>Hidden, Updated:</b> ").append(DataHelper.formatDuration(age)).append(" ago<br>\n");
else if (age > 0)
buf.append("<b>Published:</b> ").append(DataHelper.formatDuration(age)).append(" ago<br>\n");
else
buf.append("<b>Published:</b> in ").append(DataHelper.formatDuration(0-age)).append("???<br>\n");
buf.append("<b>Address(es):</b> ");
String country = _context.commSystem().getCountry(info.getIdentity().getHash());
if(country != null) {
buf.append("<img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\"");
buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> ");
}
for (Iterator iter = info.getAddresses().iterator(); iter.hasNext(); ) {
RouterAddress addr = (RouterAddress)iter.next();
buf.append(DataHelper.stripHTML(addr.getTransportStyle())).append(": ");
for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) {
String name = (String)optIter.next();
String val = addr.getOptions().getProperty(name);
buf.append('[').append(DataHelper.stripHTML(name)).append('=').append(DataHelper.stripHTML(val)).append("] ");
}
}
buf.append("</td></tr>\n");
if (full) {
buf.append("<tr><td>Stats: <br><code>\n");
for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = info.getOption(key);
buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br>\n");
}
buf.append("</code></td></tr>\n");
} else {
}
buf.append("</td></tr>\n");
}
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -319,4 +319,9 @@ class ProfileOrganizerRenderer {
long c = r.getCurrentEventCount() + r.getLastEventCount();
return "" + c;
}
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -111,6 +111,7 @@ public class RouterConsoleRunner {
File tmpdir = new File(workDir, ROUTERCONSOLE + "-" + _listenPort);
tmpdir.mkdir();
wac.setTempDirectory(tmpdir);
wac.addHandler(0, new LocaleWebAppHandler(I2PAppContext.getGlobalContext()));
initialize(wac);
File dir = new File(_webAppsDir);
String fileNames[] = dir.list(WarFilenameFilter.instance());

View File

@ -235,4 +235,9 @@ public class StatsGenerator {
private final static DecimalFormat _pct = new DecimalFormat("#0.00%");
private final static String pct(double num) { synchronized (_pct) { return _pct.format(num); } }
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -0,0 +1,417 @@
package net.i2p.router.web;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import net.i2p.router.RouterContext;
/**
* Refactored from summarynoframe.jsp to save ~100KB
*
*/
public class SummaryBarRenderer {
private RouterContext _context;
private SummaryHelper _helper;
public SummaryBarRenderer(RouterContext context, SummaryHelper helper) {
_context = context;
_helper = helper;
}
public void renderSummaryHTML(Writer out) throws IOException {
StringBuilder buf = new StringBuilder(8*1024);
buf.append("<a href=\"index.jsp\" target=\"_top\"><img src=\"/themes/console/images/i2plogo.png\" alt=\"")
.append(_("I2P Router Console"))
.append("\" title=\"")
.append(_("I2P Router Console"))
.append("\"></a><hr>");
File lpath = new File(_context.getBaseDir(), "docs/toolbar.html");
// you better have target="_top" for the links in there...
if (lpath.exists()) {
ContentHelper linkhelper = new ContentHelper();
linkhelper.setPage(lpath.getAbsolutePath());
linkhelper.setMaxLines("100");
buf.append(linkhelper.getContent());
} else {
buf.append("<h3><a href=\"/configclients.jsp\" target=\"_top\" title=\"")
.append("Configure startup of clients and webapps (services); manually start dormant services")
.append("\">")
.append(_("I2P Services"))
.append("</a></h3>\n" +
"<hr><table>" +
"<tr><td><a href=\"susidns/index.jsp\" target=\"_blank\" title=\"")
.append(_("Manage your I2P hosts file here (I2P domain name resolution)"))
.append("\">")
.append(_("Addressbook"))
.append("</a>\n" +
"<a href=\"i2psnark/\" target=\"_blank\" title=\"")
.append(_("Built-in anonymous BitTorrent Client"))
.append("\">")
.append(_("Torrents"))
.append("</a>\n" +
"<a href=\"susimail/susimail\" target=\"blank\" title=\"")
.append(_("Anonymous webmail client"))
.append("\">")
.append(_("Webmail"))
.append("</a>\n" +
"<a href=\"http://127.0.0.1:7658/\" target=\"_blank\" title=\"")
.append(_("Anonymous resident webserver"))
.append("\">")
.append(_("Webserver"))
.append("</a></td></tr></table>\n" +
"<hr><h3><a href=\"config.jsp\" target=\"_top\" title=\"")
.append(_("Configure I2P Router"))
.append("\">")
.append(_("I2P Internals"))
.append("</a></h3><hr>\n" +
"<table><tr><td>\n" +
"<a href=\"tunnels.jsp\" target=\"_top\" title=\"")
.append(_("View existing tunnels and tunnel build status"))
.append("\">")
.append(_("Tunnels"))
.append("</a>\n" +
"<a href=\"peers.jsp\" target=\"_top\" title=\"")
.append(_("Show all current peer connections"))
.append("\">")
.append(_("Peers"))
.append("</a>\n" +
"<a href=\"profiles.jsp\" target=\"_top\" title=\"")
.append(_("Show recent peer performance profiles"))
.append("\">")
.append(_("Profiles"))
.append("</a>\n" +
"<a href=\"netdb.jsp\" target=\"_top\" title=\"")
.append(_("Show list of all known I2P routers"))
.append("\">")
.append(_("NetDB"))
.append("</a>\n" +
"<a href=\"logs.jsp\" target=\"_top\" title=\"")
.append(_("Health Report"))
.append("\">")
.append(_("Logs"))
.append("</a>\n" +
"<a href=\"jobs.jsp\" target=\"_top\" title=\"")
.append(_("Show the router's workload, and how it's performing"))
.append("\">")
.append(_("Jobs"))
.append("</a>\n" +
"<a href=\"graphs.jsp\" target=\"_top\" title=\"")
.append(_("Graph router performance"))
.append("\">")
.append(_("Graphs"))
.append("</a>\n" +
"<a href=\"oldstats.jsp\" target=\"_top\" title=\"")
.append(_("Textual router performance statistics"))
.append("\">")
.append(_("Stats"))
.append("</a></td></tr></table>\n");
out.write(buf.toString());
buf.setLength(0);
}
buf.append("<hr><h3><a href=\"help.jsp\" target=\"_top\" title=\"")
.append(_("I2P Router Help"))
.append("\">")
.append(_("General"))
.append("</a></h3><hr>" +
"<h4><a title=\"")
.append(_("Your unique I2P router identity is"))
.append(' ')
.append(_helper.getIdent())
.append(", ")
.append(_("never reveal it to anyone"))
.append("\" href=\"netdb.jsp?r=.\" target=\"_top\">")
.append(_("Local Identity"))
.append("<a></h4><hr>\n" +
"<table><tr><td align=\"left\">" +
"<b>")
.append(_("Version"))
.append(":</b></td>" +
"<td align=\"right\">")
.append(_helper.getVersion())
.append("</td></tr>\n" +
"<tr title=\"")
.append(_("How long we've been running for this session"))
.append("\">" +
"<td align=\"left\"><b>")
.append(_("Uptime"))
.append(":</b></td>" +
"<td align=\"right\">")
.append(_helper.getUptime())
.append("</td></tr></table>\n" +
"<hr><h4><a href=\"config.jsp#help\" target=\"_top\" title=\"")
.append(_("Help with configuring your firewall and router for optimal I2P performance"))
.append("\">")
.append(_helper.getReachability())
.append("</a></h4><hr>\n");
if (_helper.updateAvailable() || _helper.unsignedUpdateAvailable()) {
// display all the time so we display the final failure message
buf.append("<br>").append(UpdateHandler.getStatus());
if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress"))) {
// nothing
} else if(
// isDone() is always false for now, see UpdateHandler
// ((!update.isDone()) &&
_helper.getAction() == null &&
_helper.getUpdateNonce() == null &&
ConfigRestartBean.getRestartTimeRemaining() > 12*60*1000) {
long nonce = _context.random().nextLong();
String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");
if (prev != null)
System.setProperty("net.i2p.router.web.UpdateHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.UpdateHandler.nonce", nonce+"");
String uri = _helper.getRequestURI();
buf.append("<form action=\"").append(uri).append("\" method=\"GET\">\n");
buf.append("<input type=\"hidden\" name=\"updateNonce\" value=\"").append(nonce).append("\" >\n");
if (_helper.updateAvailable()) {
buf.append("<button type=\"submit\" name=\"updateAction\" value=\"signed\" >")
.append(_("Download"))
.append(' ')
.append(_helper.getUpdateVersion())
.append(' ')
.append(_("Update"))
.append("</button>\n");
}
if (_helper.unsignedUpdateAvailable()) {
buf.append("<button type=\"submit\" name=\"updateAction\" value=\"Unsigned\" >")
.append(_("Download Unsigned"))
.append("<br>")
.append(_helper.getUnsignedUpdateVersion())
.append(' ')
.append(_("Update"))
.append("</button>\n");
}
buf.append("</form>\n");
}
}
buf.append("<p>")
.append(ConfigRestartBean.renderStatus(_helper.getRequestURI(), _helper.getAction(), _helper.getConsoleNonce()))
.append("</p><hr><h3><a href=\"peers.jsp\" target=\"_top\" title=\"")
.append(_("Show all current peer connections"))
.append("\">")
.append(_("Peers"))
.append("</a></h3><hr>\n" +
"<table>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Active"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getActivePeers())
.append('/')
.append(_helper.getActiveProfiles())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Fast"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getFastPeers())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("High capacity"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getHighCapacityPeers())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Integrated"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getWellIntegratedPeers())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Known"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getAllPeers())
.append("</td></tr>\n" +
"</table><hr>\n");
out.write(buf.toString());
buf.setLength(0);
boolean anotherLine = false;
if (_helper.showFirewallWarning()) {
buf.append("<h4><a href=\"config.jsp\" target=\"_top\" title=\"")
.append(_("Help with firewall configuration"))
.append("\">")
.append(_("Check NAT/firewall"))
.append("</a></h4>");
anotherLine = true;
}
boolean reseedInProgress = Boolean.valueOf(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress")).booleanValue();
// If showing the reseed link is allowed
if (_helper.allowReseed()) {
if (reseedInProgress) {
// While reseed occurring, show status message instead
buf.append("<i>").append(System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","")).append("</i><br>");
} else {
// While no reseed occurring, show reseed link
long nonce = _context.random().nextLong();
String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
String uri = _helper.getRequestURI();
buf.append("<form action=\"").append(uri).append("\" method=\"GET\">\n");
buf.append("<input type=\"hidden\" name=\"reseedNonce\" value=\"").append(nonce).append("\" >\n");
buf.append("<button type=\"submit\" >Reseed</button></form>\n");
}
anotherLine = true;
}
// If a new reseed ain't running, and the last reseed had errors, show error message
if (!reseedInProgress) {
String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
if (reseedErrorMessage.length() > 0) {
buf.append("<i>").append(reseedErrorMessage).append("</i><br>");
anotherLine = true;
}
}
if (anotherLine)
buf.append("<hr>");
buf.append("<h3><a href=\"config.jsp\" title=\"")
.append(_("Configure router bandwidth allocation"))
.append("\" target=\"_top\">")
.append(_("Bandwidth in/out"))
.append("</a></h3><hr>" +
"<table>\n" +
"<tr><td align=\"left\"><b>1s:</b></td><td align=\"right\">")
.append(_helper.getInboundSecondKBps())
.append('/')
.append(_helper.getOutboundSecondKBps())
.append("K/s</td></tr>\n" +
"<tr><td align=\"left\"><b>5m:</b></td><td align=\"right\">")
.append(_helper.getInboundFiveMinuteKBps())
.append('/')
.append(_helper.getOutboundFiveMinuteKBps())
.append("K/s</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Total"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getInboundLifetimeKBps())
.append('/')
.append(_helper.getOutboundLifetimeKBps())
.append("K/s</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Used"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getInboundTransferred())
.append('/')
.append(_helper.getOutboundTransferred())
.append("</td></tr></table>\n" +
"<hr><h3><a href=\"tunnels.jsp\" target=\"_top\" title=\"")
.append(_("View existing tunnels and tunnel build status"))
.append("\">")
.append(_("Tunnels in/out"))
.append("</a></h3><hr>" +
"<table>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Exploratory"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getInboundTunnels())
.append('/')
.append(_helper.getOutboundTunnels())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Client"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getInboundClientTunnels())
.append('/')
.append(_helper.getOutboundClientTunnels())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Participating"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getParticipatingTunnels())
.append("</td></tr>\n" +
"</table><hr><h3><a href=\"/jobs.jsp\" target=\"_top\" title=\"")
.append(_("What's in the router's job queue?"))
.append("\">")
.append(_("Congestion"))
.append("</a></h3><hr>" +
"<table>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Job lag"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getJobLag())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Message delay"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getMessageDelay())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Tunnel lag"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getTunnelLag())
.append("</td></tr>\n" +
"<tr><td align=\"left\"><b>")
.append(_("Backlog"))
.append(":</b></td><td align=\"right\">")
.append(_helper.getInboundBacklog())
.append("</td></tr>\n" +
"</table><hr><h4>")
.append(_helper.getTunnelStatus())
.append("</h4><hr>\n")
.append(_helper.getDestinations());
out.write(buf.toString());
}
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}

View File

@ -1,5 +1,6 @@
package net.i2p.router.web;
import java.io.IOException;
import java.text.Collator;
import java.text.DecimalFormat;
import java.util.ArrayList;
@ -563,4 +564,28 @@ public class SummaryHelper extends HelperBase {
public String getUnsignedUpdateVersion() {
return NewsFetcher.getInstance(_context).unsignedUpdateVersion();
}
/** output the summary bar to _out */
public void renderSummaryBar() throws IOException {
SummaryBarRenderer renderer = new SummaryBarRenderer(_context, this);
renderer.renderSummaryHTML(_out);
}
/* below here is stuff we need to get from summarynoframe.jsp to SummaryBarRenderer */
private String _action;
public void setAction(String s) { _action = s; }
public String getAction() { return _action; }
private String _consoleNonce;
public void setConsoleNonce(String s) { _consoleNonce = s; }
public String getConsoleNonce() { return _consoleNonce; }
private String _updateNonce;
public void setUpdateNonce(String s) { _updateNonce = s; }
public String getUpdateNonce() { return _updateNonce; }
private String _requestURI;
public void setRequestURI(String s) { _requestURI = s; }
public String getRequestURI() { return _requestURI; }
}

View File

@ -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) {

View File

@ -0,0 +1,318 @@
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\">&nbsp;</td>");
if (cfg.getSendTunnel() != null)
out.write(" <td class=\"cells\" align=\"center\">" + cfg.getSendTunnel().getTunnelId() +"</td>");
else
out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
if (cfg.getSendTo() != null)
out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(cfg.getSendTo()) +"</td>");
else
// out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
out.write(" <td class=\"cells\" align=\"center\">&nbsp;</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\">&nbsp;</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>&nbsp;</td> <td align=\"center\"><b>" + partCount);
out.write("</b> <td>&nbsp;</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);
}
/** translate a string */
private String _(String s) {
return Messages.getString(s, _context);
}
}