From cf0d2197b808c79ac0c936c3bb6941c53bcbcbfa Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 18 Mar 2011 15:49:58 +0000 Subject: [PATCH] - Persistent option on GUI - Fix HTML errors - Refresh improvements - Fix Rate.equals() bug - More cleanups --- .../src/net/i2p/router/web/GraphHelper.java | 65 +++++++++++++------ .../net/i2p/router/web/StatSummarizer.java | 27 +++++--- .../net/i2p/router/web/SummaryListener.java | 6 +- .../net/i2p/router/web/SummaryRenderer.java | 12 ++-- apps/routerconsole/jsp/graphs.jsp | 23 ++++--- core/java/src/net/i2p/stat/Rate.java | 37 +++-------- 6 files changed, 97 insertions(+), 73 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/GraphHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/GraphHelper.java index 55a854900..ef81a92d2 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/GraphHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/GraphHelper.java @@ -17,6 +17,7 @@ public class GraphHelper extends FormHandler { private int _width; private int _height; private int _refreshDelaySeconds; + private boolean _persistent; private static final String PROP_X = "routerconsole.graphX"; private static final String PROP_Y = "routerconsole.graphY"; @@ -39,9 +40,21 @@ public class GraphHelper extends FormHandler { _height = _context.getProperty(PROP_Y, DEFAULT_Y); _periodCount = _context.getProperty(PROP_PERIODS, DEFAULT_PERIODS); _refreshDelaySeconds = _context.getProperty(PROP_REFRESH, DEFAULT_REFRESH); - _showEvents = Boolean.valueOf(_context.getProperty(PROP_EVENTS)).booleanValue(); + _showEvents = _context.getBooleanProperty(PROP_EVENTS); } + /** + * This must be output in the jsp since must be in the + * @since 0.8.6 + */ + public String getRefreshMeta() { + if (_refreshDelaySeconds <= 8 || + ConfigRestartBean.getRestartTimeRemaining() < (1000 * (_refreshDelaySeconds + 30))) + return ""; + // shorten the refresh by 3 seconds so we beat the iframe + return ""; + } + /** * This was a HelperBase but now it's a FormHandler * @since 0.8.2 @@ -51,13 +64,17 @@ public class GraphHelper extends FormHandler { public void setPeriodCount(String str) { try { _periodCount = Integer.parseInt(str); } catch (NumberFormatException nfe) {} } + public void setShowEvents(boolean b) { _showEvents = b; } + public void setHeight(String str) { try { _height = Math.min(Integer.parseInt(str), MAX_Y); } catch (NumberFormatException nfe) {} } + public void setWidth(String str) { try { _width = Math.min(Integer.parseInt(str), MAX_X); } catch (NumberFormatException nfe) {} } + public void setRefreshDelay(String str) { try { int rds = Integer.parseInt(str); @@ -67,6 +84,9 @@ public class GraphHelper extends FormHandler { _refreshDelaySeconds = -1; } catch (NumberFormatException nfe) {} } + + /** @since 0.8.6 */ + public void setPersistent(String foo) { _persistent = true; } public String getImages() { try { @@ -123,13 +143,9 @@ public class GraphHelper extends FormHandler { + "\" alt=\"" + title + "\" title=\"" + title + "\">\n"); } - // FIXME not allowed inside
, move to the .jsp - if (_refreshDelaySeconds > 0) - // shorten the refresh by 3 seconds so we beat the iframe - _out.write("\n"); // FIXME jrobin doesn't support setting the timezone, will have to mod TimeAxis.java - _out.write("" + _("All times are UTC.") + "

\n"); + _out.write("

" + _("All times are UTC.") + "

\n"); } catch (IOException ioe) { ioe.printStackTrace(); } @@ -148,11 +164,11 @@ public class GraphHelper extends FormHandler { _out.write("
\n" + "\n" + "\n"); - _out.write(_("Periods") + ":
\n"); + _out.write(_("Periods") + ":
\n"); _out.write(_("Plot averages") + ": "); _out.write(_("or")+ " " +_("plot events") + ":
\n"); - _out.write(_("Image sizes") + ": " + _("width") + ": " + _("pixels") + ", " + _("height") + ": " + _("pixels") + ", " + _("height") + ": " + _("pixels") + "
\n"); _out.write(_("Refresh delay") + ":
\n" + - "
"); + _("Store graph data on disk?") + + " " + + "
"); } catch (IOException ioe) { ioe.printStackTrace(); } @@ -194,26 +216,27 @@ public class GraphHelper extends FormHandler { _height != _context.getProperty(PROP_Y, DEFAULT_Y) || _periodCount != _context.getProperty(PROP_PERIODS, DEFAULT_PERIODS) || _refreshDelaySeconds != _context.getProperty(PROP_REFRESH, DEFAULT_REFRESH) || - _showEvents != Boolean.valueOf(_context.getProperty(PROP_EVENTS)).booleanValue()) { + _showEvents != _context.getBooleanProperty(PROP_EVENTS) || + _persistent != _context.getBooleanPropertyDefaultTrue(SummaryListener.PROP_PERSISTENT)) { _context.router().setConfigSetting(PROP_X, "" + _width); _context.router().setConfigSetting(PROP_Y, "" + _height); _context.router().setConfigSetting(PROP_PERIODS, "" + _periodCount); _context.router().setConfigSetting(PROP_REFRESH, "" + _refreshDelaySeconds); _context.router().setConfigSetting(PROP_EVENTS, "" + _showEvents); + _context.router().setConfigSetting(SummaryListener.PROP_PERSISTENT, "" + _persistent); _context.router().saveConfig(); addFormNotice(_("Graph settings saved")); } } -/** inner class, don't bother reindenting */ -private static class AlphaComparator implements Comparator { - public int compare(Object lhs, Object rhs) { - SummaryListener l = (SummaryListener)lhs; - SummaryListener r = (SummaryListener)rhs; - String lName = l.getRate().getRateStat().getName() + "." + l.getRate().getPeriod(); - String rName = r.getRate().getRateStat().getName() + "." + r.getRate().getPeriod(); - return lName.compareTo(rName); + private static class AlphaComparator implements Comparator { + public int compare(SummaryListener l, SummaryListener r) { + String lName = l.getRate().getRateStat().getName(); + String rName = r.getRate().getRateStat().getName(); + int rv = lName.compareTo(rName); + if (rv != 0) + return rv; + return (int) (l.getRate().getPeriod() - r.getRate().getPeriod()); + } } } - -} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java b/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java index ca009084a..d08bdc3a0 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java @@ -3,6 +3,7 @@ package net.i2p.router.web; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; @@ -18,6 +19,7 @@ import javax.imageio.stream.MemoryCacheImageOutputStream; import net.i2p.router.RouterContext; import net.i2p.stat.Rate; import net.i2p.stat.RateStat; +import net.i2p.util.FileUtil; import net.i2p.util.Log; import org.jrobin.core.RrdException; @@ -60,6 +62,9 @@ public class StatSummarizer implements Runnable { public static StatSummarizer instance() { return _instance; } public void run() { + boolean isPersistent = _context.getBooleanPropertyDefaultTrue(SummaryListener.PROP_PERSISTENT); + if (!isPersistent) + deleteOldRRDs(); _thread = Thread.currentThread(); String specs = ""; while (_isRunning && _context.router().isAlive()) { @@ -126,10 +131,10 @@ public class StatSummarizer implements Runnable { } private void removeDb(Rate r) { - for (int i = 0; i < _listeners.size(); i++) { - SummaryListener lsnr = _listeners.get(i); + for (SummaryListener lsnr : _listeners) { if (lsnr.getRate().equals(r)) { - _listeners.remove(i); + // no iter.remove() in COWAL + _listeners.remove(lsnr); lsnr.stopListening(); return; } @@ -178,8 +183,7 @@ public class StatSummarizer implements Runnable { height = GraphHelper.MAX_Y; else if (height <= 0) height = GraphHelper.DEFAULT_Y; - for (int i = 0; i < _listeners.size(); i++) { - SummaryListener lsnr = _listeners.get(i); + for (SummaryListener lsnr : _listeners) { if (lsnr.getRate().equals(rate)) { lsnr.renderPng(out, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, showCredit); return true; @@ -206,8 +210,7 @@ public class StatSummarizer implements Runnable { } private boolean locked_getXML(Rate rate, OutputStream out) throws IOException { - for (int i = 0; i < _listeners.size(); i++) { - SummaryListener lsnr = _listeners.get(i); + for (SummaryListener lsnr : _listeners) { if (lsnr.getRate().equals(rate)) { lsnr.getData().exportXml(out); out.write(("\n").getBytes()); @@ -274,7 +277,6 @@ public class StatSummarizer implements Runnable { def.setTimeSpan(start/1000, end/1000); def.setMinValue(0d); def.setBase(1024); - // Note to translators: all runtime zh translation disabled in this file, no font available in RRD String title = _("Bandwidth usage"); if (!hideTitle) def.setTitle(title); @@ -365,6 +367,15 @@ public class StatSummarizer implements Runnable { return rv; } + /** + * Delete the old rrd dir if we are no longer persistent + * @since 0.8.6 + */ + private void deleteOldRRDs() { + File rrdDir = new File(_context.getRouterDir(), SummaryListener.RRD_DIR); + FileUtil.rmdir(rrdDir, false); + } + /** translate a string */ private String _(String s) { // the RRD font doesn't have zh chars, at least on my system diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java index c66c2fbcc..b5721ff5f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryListener.java @@ -32,9 +32,9 @@ import org.jrobin.graph.RrdGraphDefTemplate; * @since 0.6.1.13 */ class SummaryListener implements RateSummaryListener { - private static final String PROP_PERSISTENT = "routerconsole.graphPersistent"; + static final String PROP_PERSISTENT = "routerconsole.graphPersistent"; /** note that .jrb files are NOT compatible with .rrd files */ - private static final String RRD_DIR = "rrd"; + static final String RRD_DIR = "rrd"; private static final String RRD_PREFIX = "rrd-"; private static final String RRD_SUFFIX = ".jrb"; static final String CF = "AVERAGE"; @@ -62,7 +62,7 @@ class SummaryListener implements RateSummaryListener { _context = I2PAppContext.getGlobalContext(); _rate = r; _log = _context.logManager().getLog(SummaryListener.class); - _isPersistent = _context.getBooleanProperty(PROP_PERSISTENT); + _isPersistent = _context.getBooleanPropertyDefaultTrue(PROP_PERSISTENT); } public void add(double totalValue, long eventCount, double totalEventTime, long period) { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java index 9aaa83ecd..0d9693c2b 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java @@ -101,12 +101,11 @@ class SummaryRenderer { String title; String p; // we want the formatting and translation of formatDuration2(), except not zh, and not the   - if ("zh".equals(Messages.getLanguage(_context))) - p = DataHelper.formatDuration(_listener.getRate().getPeriod()); - else + //if ("zh".equals(Messages.getLanguage(_context))) + // p = DataHelper.formatDuration(_listener.getRate().getPeriod()); + //else p = DataHelper.formatDuration2(_listener.getRate().getPeriod()).replace(" ", " "); if (showEvents) - // Note to translators: all runtime zh translation disabled in this file, no font available in RRD title = name + ' ' + _("events in {0}", p); else title = name + ' ' + _("averaged for {0}", p); @@ -133,7 +132,10 @@ class SummaryRenderer { if (started > start && started < end) def.vrule(started / 1000, Color.BLACK, _("Restart"), 4.0f); def.datasource(plotName, path, plotName, SummaryListener.CF, _listener.getBackendName()); - def.area(plotName, Color.BLUE, descr + "\\r"); + if (descr.length() > 0) + def.area(plotName, Color.BLUE, descr + "\\r"); + else + def.area(plotName, Color.BLUE); if (!hideLegend) { def.gprint(plotName, SummaryListener.CF, _("avg") + ": %.2f %s"); def.gprint(plotName, "MAX", ' ' + _("max") + ": %.2f %S"); diff --git a/apps/routerconsole/jsp/graphs.jsp b/apps/routerconsole/jsp/graphs.jsp index bbda25944..e54133250 100644 --- a/apps/routerconsole/jsp/graphs.jsp +++ b/apps/routerconsole/jsp/graphs.jsp @@ -5,19 +5,26 @@ <%@include file="css.jsi" %> <%=intl.title("graphs")%> - - -<%@include file="summary.jsi" %> -

<%=intl._("I2P Performance Graphs")%>

-
-
-
<% graphHelper.storeMethod(request.getMethod()); %> " /> <% /* GraphHelper sets the defaults in setContextId, so setting the properties must be after the context */ %> - <% graphHelper.storeWriter(out); %> +<% + graphHelper.storeWriter(out); + graphHelper.storeMethod(request.getMethod()); + // meta must be inside the head + boolean allowRefresh = intl.allowIFrame(request.getHeader("User-Agent")); + if (allowRefresh) { + out.print(graphHelper.getRefreshMeta()); + } +%> + +<%@include file="summary.jsi" %> +

<%=intl._("I2P Performance Graphs")%>

+
+
+
diff --git a/core/java/src/net/i2p/stat/Rate.java b/core/java/src/net/i2p/stat/Rate.java index 8473d58eb..21e496739 100644 --- a/core/java/src/net/i2p/stat/Rate.java +++ b/core/java/src/net/i2p/stat/Rate.java @@ -3,6 +3,7 @@ package net.i2p.stat; import java.io.IOException; import java.util.Properties; +import net.i2p.data.DataHelper; import net.i2p.util.Log; /** @@ -471,48 +472,28 @@ public class Rate { coalesce(); } + /** + * This is used in StatSummarizer and SummaryListener. + * We base it on the stat we are tracking, not the stored data. + */ @Override public boolean equals(Object obj) { if ((obj == null) || !(obj instanceof Rate)) return false; if (obj == this) return true; Rate r = (Rate) obj; return _period == r.getPeriod() && _creationDate == r.getCreationDate() && - //_lastCoalesceDate == r.getLastCoalesceDate() && - _currentTotalValue == r.getCurrentTotalValue() && _currentEventCount == r.getCurrentEventCount() - && _currentTotalEventTime == r.getCurrentTotalEventTime() && _lastTotalValue == r.getLastTotalValue() - && _lastEventCount == r.getLastEventCount() && _lastTotalEventTime == r.getLastTotalEventTime() - && _extremeTotalValue == r.getExtremeTotalValue() && _extremeEventCount == r.getExtremeEventCount() - && _extremeTotalEventTime == r.getExtremeTotalEventTime() - && _lifetimeTotalValue == r.getLifetimeTotalValue() && _lifetimeEventCount == r.getLifetimeEventCount() - && _lifetimeTotalEventTime == r.getLifetimeTotalEventTime(); + // do this the easy way to avoid NPEs. + // Alternative: compare name and group name (very carefully to avoid NPEs) + _stat == r._stat; } /** * It doesn't appear that Rates are ever stored in a Set or Map * (RateStat stores in an array) so let's make this easy. - * We can always make something faster if it's actually used. */ @Override public int hashCode() { -/***** - int hash = 5; - hash = 67 * hash + (int)(Double.doubleToLongBits(this._currentTotalValue) ^ (Double.doubleToLongBits(this._currentTotalValue) >>> 32)); - hash = 67 * hash + (int)(this._currentEventCount ^ (this._currentEventCount >>> 32)); - hash = 67 * hash + (int)(this._currentTotalEventTime ^ (this._currentTotalEventTime >>> 32)); - hash = 67 * hash + (int)(Double.doubleToLongBits(this._lastTotalValue) ^ (Double.doubleToLongBits(this._lastTotalValue) >>> 32)); - hash = 67 * hash + (int)(this._lastEventCount ^ (this._lastEventCount >>> 32)); - hash = 67 * hash + (int)(this._lastTotalEventTime ^ (this._lastTotalEventTime >>> 32)); - hash = 67 * hash + (int)(Double.doubleToLongBits(this._extremeTotalValue) ^ (Double.doubleToLongBits(this._extremeTotalValue) >>> 32)); - hash = 67 * hash + (int)(this._extremeEventCount ^ (this._extremeEventCount >>> 32)); - hash = 67 * hash + (int)(this._extremeTotalEventTime ^ (this._extremeTotalEventTime >>> 32)); - hash = 67 * hash + (int)(Double.doubleToLongBits(this._lifetimeTotalValue) ^ (Double.doubleToLongBits(this._lifetimeTotalValue) >>> 32)); - hash = 67 * hash + (int)(this._lifetimeEventCount ^ (this._lifetimeEventCount >>> 32)); - hash = 67 * hash + (int)(this._lifetimeTotalEventTime ^ (this._lifetimeTotalEventTime >>> 32)); - hash = 67 * hash + (int)(this._creationDate ^ (this._creationDate >>> 32)); - hash = 67 * hash + (int)(this._period ^ (this._period >>> 32)); - return hash; -******/ - return toString().hashCode(); + return DataHelper.hashCode(_stat) ^ ((int)_period) ^ ((int) _creationDate); } @Override