package net.i2p.router.web; import java.io.IOException; import java.io.Writer; import java.text.DecimalFormat; import java.util.Comparator; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.RouterInfo; import net.i2p.router.RouterContext; import net.i2p.router.peermanager.DBHistory; import net.i2p.router.peermanager.PeerProfile; import net.i2p.router.peermanager.ProfileOrganizer; import net.i2p.stat.Rate; import net.i2p.stat.RateStat; /** * Helper class to refactor the HTML rendering from out of the ProfileOrganizer * */ class ProfileOrganizerRenderer { private RouterContext _context; private ProfileOrganizer _organizer; private ProfileComparator _comparator; public ProfileOrganizerRenderer(ProfileOrganizer organizer, RouterContext context) { _context = context; _organizer = organizer; _comparator = new ProfileComparator(); } public void renderStatusHTML(Writer out) throws IOException { Set peers = _organizer.selectAllPeers(); long now = _context.clock().now(); long hideBefore = now - 90*60*1000; TreeSet order = new TreeSet(_comparator); TreeSet integratedPeers = new TreeSet(_comparator); for (Iterator iter = peers.iterator(); iter.hasNext();) { Hash peer = (Hash)iter.next(); if (_organizer.getUs().equals(peer)) continue; PeerProfile prof = _organizer.getProfile(peer); if (_organizer.isWellIntegrated(peer)) { integratedPeers.add(prof); } else { RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); if (info != null && info.getCapabilities().indexOf("f") >= 0) integratedPeers.add(prof); } if (prof.getLastSendSuccessful() <= hideBefore) continue; order.add(prof); } int fast = 0; int reliable = 0; int integrated = 0; int failing = 0; StringBuilder buf = new StringBuilder(16*1024); buf.append("

Peer Profiles

\n"); buf.append("

Showing ").append(order.size()).append(" recent profiles, hiding ").append(peers.size()-order.size()).append(" older profiles

"); buf.append("" + "" + "" + "" + "" + "" + "" + "" + "" + ""); int prevTier = 1; for (Iterator iter = order.iterator(); iter.hasNext();) { PeerProfile prof = (PeerProfile)iter.next(); Hash peer = prof.getPeer(); int tier = 0; boolean isIntegrated = false; if (_organizer.isFast(peer)) { tier = 1; fast++; reliable++; } else if (_organizer.isHighCapacity(peer)) { tier = 2; reliable++; } else if (_organizer.isFailing(peer)) { failing++; } else { tier = 3; } if (_organizer.isWellIntegrated(peer)) { isIntegrated = true; integrated++; } if (tier != prevTier) buf.append("\n"); prevTier = tier; buf.append(""); buf.append("\n"); buf.append(""); // let's not build the whole page in memory (~500 bytes per peer) out.write(buf.toString()); buf.setLength(0); } buf.append("
PeerGroups (Caps)SpeedCapacityIntegrationStatus 

"); buf.append(_context.commSystem().renderPeerHTML(peer)); buf.append(""); switch (tier) { case 1: buf.append("Fast, High Capacity"); break; case 2: buf.append("High Capacity"); break; case 3: buf.append("Not Failing"); break; default: buf.append("Failing"); break; } if (isIntegrated) buf.append(", Integrated"); RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); if (info != null) { // prevent HTML injection in the caps and version buf.append(" (").append(DataHelper.stripHTML(info.getCapabilities())); String v = info.getOption("router.version"); if (v != null) buf.append(' ').append(DataHelper.stripHTML(v)); buf.append(')'); } buf.append("").append(num(prof.getSpeedValue())); long bonus = prof.getSpeedBonus(); if (bonus != 0) { if (bonus > 0) buf.append(" (+"); else buf.append(" ("); buf.append(bonus).append(')'); } buf.append("").append(num(prof.getCapacityValue())); bonus = prof.getCapacityBonus(); if (bonus != 0) { if (bonus > 0) buf.append(" (+"); else buf.append(" ("); buf.append(bonus).append(')'); } buf.append("").append(num(prof.getIntegrationValue())); buf.append(""); if (_context.shitlist().isShitlisted(peer)) buf.append("Banned"); if (prof.getIsFailing()) buf.append(" Failing"); if (_context.commSystem().wasUnreachable(peer)) buf.append(" Unreachable"); Rate failed = prof.getTunnelHistory().getFailedRate().getRate(30*60*1000); long fails = failed.getCurrentEventCount() + failed.getLastEventCount(); if (fails > 0) { Rate accepted = prof.getTunnelCreateResponseTime().getRate(30*60*1000); long total = fails + accepted.getCurrentEventCount() + accepted.getLastEventCount(); if (total / fails <= 10) // hide if < 10% buf.append(' ').append(fails).append('/').append(total).append(" Test Fails"); } buf.append(" profile"); buf.append(" +-
"); buf.append("

Floodfill and Integrated Peers

\n" + "" + "" + "" + "" + "" + "" + "" + // "" + "" + // "" + "" + "" + "" + "" + // "" + "" + // "" + "" + "" + "" + "" + "" + ""); for (Iterator iter = integratedPeers.iterator(); iter.hasNext();) { PeerProfile prof = (PeerProfile)iter.next(); Hash peer = prof.getPeer(); buf.append(""); RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); if (info != null) buf.append(""); else buf.append(""); buf.append(""); buf.append(""); long time; time = now - prof.getLastHeardAbout(); buf.append(""); time = now - prof.getLastHeardFrom(); buf.append(""); time = now - prof.getLastSendSuccessful(); buf.append(""); time = now - prof.getLastSendFailed(); buf.append(""); buf.append(""); buf.append(""); buf.append(""); DBHistory dbh = prof.getDBHistory(); if (dbh != null) { buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); } } buf.append("
PeerCapsInteg. ValueLast Heard AboutLast Heard FromLast Successful SendLast Good SendLast Failed SendLast Bad Send10m Resp. Time1h Resp. Time1d Resp. TimeSuccessful LookupsGood LookupsFailed LookupsBad LookupsNew StoresOld Stores1h Fail Rate1d Fail Rate
"); buf.append(_context.commSystem().renderPeerHTML(peer)); buf.append("").append(DataHelper.stripHTML(info.getCapabilities())).append(" ").append(num(prof.getIntegrationValue())).append("").append(DataHelper.formatDuration(time)).append("").append(DataHelper.formatDuration(time)).append("").append(DataHelper.formatDuration(time)).append("").append(DataHelper.formatDuration(time)).append("").append(avg(prof, 10*60*1000l)).append("").append(avg(prof, 60*60*1000l)).append("").append(avg(prof, 24*60*60*1000l)).append("").append(dbh.getSuccessfulLookups()).append("").append(dbh.getFailedLookups()).append("").append(dbh.getUnpromptedDbStoreNew()).append("").append(dbh.getUnpromptedDbStoreOld()).append("").append(davg(dbh, 60*60*1000l)).append("").append(davg(dbh, 24*60*60*1000l)).append("
"); buf.append("

Thresholds:

"); buf.append("Speed: ").append(num(_organizer.getSpeedThreshold())).append(" (").append(fast).append(" fast peers)
"); buf.append("Capacity: ").append(num(_organizer.getCapacityThreshold())).append(" (").append(reliable).append(" high capacity peers)
"); buf.append("Integration: ").append(num(_organizer.getIntegrationThreshold())).append(" (").append(integrated).append(" well integrated peers)"); buf.append("

Definitions:

"); out.write(buf.toString()); out.flush(); } private class ProfileComparator implements Comparator { public int compare(Object lhs, Object rhs) { if ( (lhs == null) || (rhs == null) ) throw new NullPointerException("lhs=" + lhs + " rhs=" + rhs); if ( !(lhs instanceof PeerProfile) || !(rhs instanceof PeerProfile) ) throw new ClassCastException("lhs=" + lhs.getClass().getName() + " rhs=" + rhs.getClass().getName()); PeerProfile left = (PeerProfile)lhs; PeerProfile right = (PeerProfile)rhs; if (_context.profileOrganizer().isFast(left.getPeer())) { if (_context.profileOrganizer().isFast(right.getPeer())) { return compareHashes(left, right); } else { return -1; // fast comes first } } else if (_context.profileOrganizer().isHighCapacity(left.getPeer())) { if (_context.profileOrganizer().isFast(right.getPeer())) { return 1; } else if (_context.profileOrganizer().isHighCapacity(right.getPeer())) { return compareHashes(left, right); } else { return -1; } } else if (_context.profileOrganizer().isFailing(left.getPeer())) { if (_context.profileOrganizer().isFailing(right.getPeer())) { return compareHashes(left, right); } else { return 1; } } else { // left is not failing if (_context.profileOrganizer().isFast(right.getPeer())) { return 1; } else if (_context.profileOrganizer().isHighCapacity(right.getPeer())) { return 1; } else if (_context.profileOrganizer().isFailing(right.getPeer())) { return -1; } else { return compareHashes(left, right); } } } private int compareHashes(PeerProfile left, PeerProfile right) { return left.getPeer().toBase64().compareTo(right.getPeer().toBase64()); } } private final static DecimalFormat _fmt = new DecimalFormat("###,##0.00"); private final static String num(double num) { synchronized (_fmt) { return _fmt.format(num); } } private final static String na = "n/a"; private static String avg (PeerProfile prof, long rate) { RateStat rs = prof.getDbResponseTime(); if (rs == null) return na; Rate r = rs.getRate(rate); if (r == null) return na; long c = r.getCurrentEventCount() + r.getLastEventCount(); if (c == 0) return na; double d = r.getCurrentTotalValue() + r.getLastTotalValue(); return Math.round(d/c) + "ms"; } private static String davg (DBHistory dbh, long rate) { RateStat rs = dbh.getFailedLookupRate(); if (rs == null) return na; Rate r = rs.getRate(rate); if (r == null) return na; long c = r.getCurrentEventCount() + r.getLastEventCount(); return "" + c; } }