diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java index e6734dd127d73d29f917110330390704c34f1086..46da0de60b33ad8e5cf90ba4f601dcccc24ab03f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -833,7 +833,7 @@ public class RouterConsoleRunner implements RouterApp { } } - Thread t = new I2PAppThread(new StatSummarizer(), "StatSummarizer", true); + Thread t = new I2PAppThread(new StatSummarizer(_context), "StatSummarizer", true); t.setPriority(Thread.NORM_PRIORITY - 1); t.start(); 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 275d237f0d194ec7b97f4628d52aeb67ec774d66..e2b8067ddfd115ff905f5f9c6a120705aeaa64a6 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java @@ -11,6 +11,9 @@ import java.util.StringTokenizer; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Semaphore; +import net.i2p.I2PAppContext; +import net.i2p.app.ClientApp; +import net.i2p.app.ClientAppState; import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; import static net.i2p.router.web.GraphConstants.*; @@ -32,29 +35,40 @@ import net.i2p.util.SystemVersion; * * @since 0.6.1.13 */ -public class StatSummarizer implements Runnable { +public class StatSummarizer implements Runnable, ClientApp { private final RouterContext _context; private final Log _log; /** list of SummaryListener instances */ private final List<SummaryListener> _listeners; - // TODO remove static instance - private static StatSummarizer _instance; private static final int MAX_CONCURRENT_PNG = SystemVersion.isARM() ? 2 : 3; private final Semaphore _sem; - private volatile boolean _isRunning = true; - private boolean _isDisabled; - private Thread _thread; + private volatile boolean _isRunning; + private volatile Thread _thread; + private static final String NAME = "StatSummarizer"; - public StatSummarizer() { - _context = RouterContext.listContexts().get(0); // only summarize one per jvm + public StatSummarizer(RouterContext ctx) { + _context = ctx; _log = _context.logManager().getLog(getClass()); _listeners = new CopyOnWriteArrayList<SummaryListener>(); - _instance = this; _sem = new Semaphore(MAX_CONCURRENT_PNG, true); _context.addShutdownTask(new Shutdown()); } - public static StatSummarizer instance() { return _instance; } + /** + * @return null if disabled + */ + public static StatSummarizer instance() { + return instance(I2PAppContext.getGlobalContext()); + } + + /** + * @return null if disabled + * @since 0.0.38 + */ + public static StatSummarizer instance(I2PAppContext ctx) { + ClientApp app = ctx.clientAppManager().getRegisteredApp(NAME); + return (app != null) ? (StatSummarizer) app : null; + } public void run() { // JRobin 1.5.9 crashes these JVMs @@ -65,24 +79,29 @@ public class StatSummarizer implements Runnable { System.getProperty("java.version") + " (" + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version") + ')'); - _isDisabled = true; - _isRunning = false; return; } + _isRunning = true; boolean isPersistent = _context.getBooleanPropertyDefaultTrue(SummaryListener.PROP_PERSISTENT); if (!isPersistent) deleteOldRRDs(); _thread = Thread.currentThread(); + _context.clientAppManager().register(this); String specs = ""; - while (_isRunning && _context.router().isAlive()) { - specs = adjustDatabases(specs); - try { Thread.sleep(60*1000); } catch (InterruptedException ie) {} + try { + while (_isRunning && _context.router().isAlive()) { + specs = adjustDatabases(specs); + try { Thread.sleep(60*1000); } catch (InterruptedException ie) {} + } + } finally { + _isRunning = false; + _context.clientAppManager().unregister(this); } } - /** @since 0.8.7, public since 0.9.33, was package private */ - public static boolean isDisabled() { - return _instance == null || _instance._isDisabled; + /** @since 0.0.38 */ + public static boolean isDisabled(I2PAppContext ctx) { + return ctx.clientAppManager().getRegisteredApp(NAME) == null; } /** @@ -90,13 +109,57 @@ public class StatSummarizer implements Runnable { * See SummaryRenderer.render() * @since 0.9.6 */ - static void setDisabled() { - if (_instance != null) { - _instance._isDisabled = true; - _instance._isRunning = false; + static void setDisabled(I2PAppContext ctx) { + StatSummarizer ss = instance(ctx); + if (ss != null) + ss.setDisabled(); + } + + /** + * Disable graph generation until restart + * See SummaryRenderer.render() + * @since 0.9.38 + */ + synchronized void setDisabled() { + if (_isRunning) { + _isRunning = false; + Thread t = _thread; + if (t != null) + t.interrupt(); } } + /////// ClientApp methods + + /** + * Does nothing, we aren't tracked + * @since 0.9.38 + */ + public void startup() {} + + /** + * Does nothing, we aren't tracked + * @since 0.9.38 + */ + public void shutdown(String[] args) {} + + /** @since 0.9.38 */ + public ClientAppState getState() { + return ClientAppState.RUNNING; + } + + /** @since 0.9.38 */ + public String getName() { + return NAME; + } + + /** @since 0.9.38 */ + public String getDisplayName() { + return "Console stats summarizer"; + } + + /////// End ClientApp methods + /** * List of SummaryListener instances * @since public since 0.9.33, was package private @@ -206,8 +269,7 @@ public class StatSummarizer implements Runnable { // at java.lang.Class.forName0(Native Method) // at java.lang.Class.forName(Class.java:270) // at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:82) - _isDisabled = true; - _isRunning = false; + setDisabled(); String s = "Error rendering - disabling graph generation. Install ttf-dejavu font package?"; _log.logAlways(Log.WARN, s); IOException ioe = new IOException(s); @@ -296,8 +358,7 @@ public class StatSummarizer implements Runnable { // at java.lang.Class.forName0(Native Method) // at java.lang.Class.forName(Class.java:270) // at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:82) - _isDisabled = true; - _isRunning = false; + setDisabled(); String s = "Error rendering - disabling graph generation. Install ttf-dejavu font package?"; _log.logAlways(Log.WARN, s); IOException ioe = new IOException(s); @@ -316,7 +377,7 @@ public class StatSummarizer implements Runnable { // go to some trouble to see if we have the data for the combined bw graph SummaryListener txLsnr = null; SummaryListener rxLsnr = null; - for (SummaryListener lsnr : StatSummarizer.instance().getListeners()) { + for (SummaryListener lsnr : getListeners()) { String title = lsnr.getRate().getRateStat().getName(); if (title.equals("bw.sendRate")) txLsnr = lsnr; @@ -396,9 +457,7 @@ public class StatSummarizer implements Runnable { */ private class Shutdown implements Runnable { public void run() { - _isRunning = false; - if (_thread != null) - _thread.interrupt(); + setDisabled(); for (SummaryListener lsnr : _listeners) { // FIXME could cause exceptions if rendering? lsnr.stopListening(); 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 8571ef6ad64284abc3b027c335a931b85c8c1d52..ac66e9105d356a682a70f41890c90a5531a1a398 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java @@ -289,7 +289,7 @@ class SummaryRenderer { graph = new RrdGraph(def); } catch (NullPointerException npe) { _log.error("Error rendering", npe); - StatSummarizer.setDisabled(); + StatSummarizer.setDisabled(_context); throw new IOException("Error rendering - disabling graph generation. Missing font? See http://trac.i2p2.i2p/ticket/915"); } int totalWidth = graph.getRrdGraphInfo().getWidth(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java index c7867a4f6d47624841c32992f2144113d0b05a65..848e8d113442caff7963e090f3f293e7c7bd3c1d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java @@ -147,10 +147,11 @@ public class GraphHelper extends FormHandler { } public String getImages() { - if (StatSummarizer.isDisabled()) + StatSummarizer ss = StatSummarizer.instance(_context); + if (ss == null) return ""; try { - List<SummaryListener> listeners = StatSummarizer.instance().getListeners(); + List<SummaryListener> listeners = ss.getListeners(); TreeSet<SummaryListener> ordered = new TreeSet<SummaryListener>(new AlphaComparator()); ordered.addAll(listeners); @@ -235,9 +236,10 @@ public class GraphHelper extends FormHandler { * @since 0.9 */ public String getSingleStat() { + StatSummarizer ss = StatSummarizer.instance(_context); + if (ss == null) + return ""; try { - if (StatSummarizer.isDisabled()) - return ""; if (_stat == null) { _out.write("No stat specified"); return ""; @@ -249,7 +251,7 @@ public class GraphHelper extends FormHandler { name = _stat; displayName = _t("Bandwidth usage"); } else { - Set<Rate> rates = StatSummarizer.instance().parseSpecs(_stat); + Set<Rate> rates = ss.parseSpecs(_stat); if (rates.size() != 1) { _out.write("Graphs not enabled for " + _stat); return ""; @@ -376,7 +378,8 @@ public class GraphHelper extends FormHandler { private static final int[] times = { 15, 30, 60, 2*60, 5*60, 10*60, 30*60, 60*60, -1 }; public String getForm() { - if (StatSummarizer.isDisabled()) + StatSummarizer ss = StatSummarizer.instance(_context); + if (ss == null) return ""; // too hard to use the standard formhandler.jsi / FormHandler.java session nonces // since graphs.jsp needs the refresh value in its <head>. @@ -440,7 +443,7 @@ public class GraphHelper extends FormHandler { */ @Override public String getAllMessages() { - if (StatSummarizer.isDisabled()) { + if (StatSummarizer.isDisabled(_context)) { addFormError("Graphing not supported with this JVM: " + System.getProperty("java.vendor") + ' ' + System.getProperty("java.version") + " (" + diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java index 81e8956ebe254a5a931cee21227f1a7cd4c6a864..5f1a43caa23050d20f455c841a864866e85c12e3 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SummaryBarRenderer.java @@ -277,7 +277,7 @@ class SummaryBarRenderer { .append("</a>\n"); } - if (!StatSummarizer.isDisabled()) { + if (!StatSummarizer.isDisabled(_context)) { buf.append("<a href=\"/graphs\" target=\"_top\" title=\"") .append(_t("Graph router performance")) .append("\">") @@ -822,13 +822,14 @@ class SummaryBarRenderer { public String renderBandwidthGraphHTML() { if (_helper == null) return ""; StringBuilder buf = new StringBuilder(512); - if (!StatSummarizer.isDisabled()) + if (!StatSummarizer.isDisabled(_context)) { buf.append("<div id=\"sb_graphcontainer\"><a href=\"/graphs\"><table id=\"sb_bandwidthgraph\">" + "<tr title=\"") .append(_t("Our inbound & outbound traffic for the last 20 minutes")) .append("\"><td><span id=\"sb_graphstats\">") .append(_helper.getSecondKBps()) .append("Bps</span></td></tr></table></a></div>\n"); + } buf.append("<script src=\"/js/refreshGraph.js\" type=\"text/javascript\" id=\"refreshGraph\" async></script>"); return buf.toString(); } diff --git a/apps/routerconsole/jsp/viewstat.jsp b/apps/routerconsole/jsp/viewstat.jsp index 892521b6a77b34c75fe89f786937d2a283259d90..f388d39c0aab3532b1ef88334d1736b1fd830979 100644 --- a/apps/routerconsole/jsp/viewstat.jsp +++ b/apps/routerconsole/jsp/viewstat.jsp @@ -6,14 +6,19 @@ * * Do not tag this file for translation. */ - +net.i2p.I2PAppContext ctx = net.i2p.I2PAppContext.getGlobalContext(); +net.i2p.router.web.StatSummarizer ss = net.i2p.router.web.StatSummarizer.instance(ctx); +if (ss == null) { + response.sendError(403, "Stats disabled"); + return; +} boolean rendered = false; /**** unused String templateFile = request.getParameter("template"); if (templateFile != null) { java.io.OutputStream cout = response.getOutputStream(); response.setContentType("image/png"); - rendered = net.i2p.router.web.StatSummarizer.instance().renderPng(cout, templateFile); + rendered = ss.renderPng(cout, templateFile); } ****/ net.i2p.stat.Rate rate = null; @@ -22,7 +27,7 @@ String period = request.getParameter("period"); boolean fakeBw = (stat != null && ("bw.combined".equals(stat))); net.i2p.stat.RateStat rs = null; if (stat != null) - rs = net.i2p.I2PAppContext.getGlobalContext().statManager().getRate(stat); + rs = ctx.statManager().getRate(stat); if ( !rendered && ((rs != null) || fakeBw) ) { long per = -1; try { @@ -39,12 +44,12 @@ if ( !rendered && ((rs != null) || fakeBw) ) { if ("xml".equals(format)) { if (!fakeBw) { response.setContentType("text/xml"); - rendered = net.i2p.router.web.StatSummarizer.instance().getXML(rate, cout); + rendered = ss.getXML(rate, cout); } } else { response.setContentType("image/png"); // very brief 45 sec expire - response.setDateHeader("Expires", net.i2p.I2PAppContext.getGlobalContext().clock().now() + (45*1000)); + response.setDateHeader("Expires", ctx.clock().now() + (45*1000)); response.setHeader("Accept-Ranges", "none"); // http://jira.codehaus.org/browse/JETTY-1346 // This doesn't actually appear in the response, but it fixes the problem, @@ -70,9 +75,9 @@ if ( !rendered && ((rs != null) || fakeBw) ) { if (request.getParameter("showCredit") != null) showCredit = Boolean.parseBoolean(request.getParameter("showCredit")); if (fakeBw) - rendered = net.i2p.router.web.StatSummarizer.instance().renderRatePng(cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit); + rendered = ss.renderRatePng(cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit); else - rendered = net.i2p.router.web.StatSummarizer.instance().renderPng(rate, cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit); + rendered = ss.renderPng(rate, cout, width, height, hideLegend, hideGrid, hideTitle, showEvents, periodCount, end, showCredit); } if (rendered) cout.close();