From 6462e2a2922f44558ddbae7f6ebca51d0a3a9ddf Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Wed, 14 Nov 2018 14:48:10 +0000 Subject: [PATCH] MLab/NDT: Prep for connecting to wizard, fixes, cleanups --- .../src/com/vuze/plugins/mlab/MLabRunner.java | 84 ++++++----- .../i2p/router/web/helpers/WizardHandler.java | 133 ++++++++++++++++++ .../i2p/router/web/helpers/WizardHelper.java | 4 +- apps/routerconsole/jsp/index.jsp | 7 +- apps/routerconsole/jsp/welcome.jsp | 12 +- 5 files changed, 200 insertions(+), 40 deletions(-) diff --git a/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java b/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java index fab96920e3..ea28c46f8a 100644 --- a/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java +++ b/apps/routerconsole/java/src/com/vuze/plugins/mlab/MLabRunner.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import com.vuze.plugins.mlab.tools.ndt.swingemu.Tcpbw100UIWrapper; import com.vuze.plugins.mlab.tools.ndt.swingemu.Tcpbw100UIWrapperListener; @@ -40,7 +41,6 @@ import net.minidev.json.parser.ParseException; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; -import net.i2p.router.RouterContext; import net.i2p.util.EepGet; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; @@ -51,13 +51,14 @@ import net.i2p.util.Log; * @since 0.9.38 */ public class MLabRunner { + // ns.measurementlab.net does not support https + // use ndt_ssl for test over ssl? but Tcpbw100 doesn't support it private static final String NS_URL = "http://ns.measurementlab.net/ndt?format=json"; private static final long NS_TIMEOUT = 20*1000; private boolean test_active; private final I2PAppContext _context; - // null for testing - private final RouterContext _rcontext; private final Log _log; + private final AtomicBoolean _running = new AtomicBoolean(); private static MLabRunner _instance; public static MLabRunner getInstance(I2PAppContext ctx) { @@ -70,13 +71,26 @@ public class MLabRunner { private MLabRunner(I2PAppContext ctx) { _context = ctx; - _rcontext = ctx.isRouterContext() ? (RouterContext) ctx : null; _log = ctx.logManager().getLog(MLabRunner.class); } + + public boolean isRunning() { + return _running.get(); + } + /** + * Non-blocking, spawns a thread and returns immediately. + * + * @param listener use to detect completion and get results + * @return a ToolRun object which may be used to cancel the test, + * or null if there was already a test in progress. + */ public ToolRun runNDT(final ToolListener listener) { + if (!_running.compareAndSet(false, true)) { + _log.warn("Test already running"); + return null; + } final ToolRun run = new ToolRunImpl(); - //final AESemaphore sem = new AESemaphore( "waiter" ); runTool( new Runnable() @@ -85,7 +99,6 @@ public class MLabRunner { boolean completed = false; try{ _log.warn("Starting NDT Test"); - _log.warn("-----------------"); new Tcpbw100UIWrapper( new Tcpbw100UIWrapperListener() @@ -141,7 +154,6 @@ public class MLabRunner { // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, // int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, // String url, boolean allowCaching, String etag, String postData) { - // TODO why no HTTPS? EepGet eepget = new EepGet(_context, false, null, 0, 0, 2, 1024, null, baos, NS_URL, false, null, null); @@ -160,6 +172,7 @@ public class MLabRunner { if (_log.shouldWarn()) _log.warn("Got response: " + DataHelper.getUTF8(b)); // TODO use IP instead to avoid another lookup? + // or use "fqdn" in response instead of "url" URL url = new URL((String)map.get( "url" )); if (url == null) { throw new IOException("no url"); @@ -174,7 +187,7 @@ public class MLabRunner { } if (server_host == null) { - // fallback to old, discouraged approach + // fallback to old, discouraged approach server_host = "ndt.iupui.donar.measurement-lab.org"; if (_log.shouldWarn()) _log.warn("Failed to select server, falling back to donar method"); @@ -191,8 +204,6 @@ public class MLabRunner { } }); - //sem.release(); - test.runIt(); try { Thread.sleep(2000); } catch (InterruptedException ie) { return; } @@ -202,11 +213,13 @@ public class MLabRunner { try { Thread.sleep(1000); } catch (InterruptedException ie) { break; } } + // in integer bytes per second long up_bps = 0; try { up_bps = (long)(Double.parseDouble(test.get_c2sspd())*1000000)/8; } catch(Throwable e) {} + // in integer bytes per second long down_bps = 0; try { down_bps = (long)(Double.parseDouble(test.get_s2cspd())*1000000)/8; @@ -236,54 +249,44 @@ public class MLabRunner { _log.warn("Test complete in " + DataHelper.formatDuration(end - start)); } } finally { - //sem.release(); if (!completed && listener != null) { listener.complete( new HashMap<String, Object>()); } + _running.set(false); } } }); - //sem.reserve(); return run; } - protected void runTool(final Runnable target) { - // need something like this - //ap.setEnabled( false ); - new I2PAppThread("toolRunner") + /** + * Non-blocking, spawns a thread and returns immediately. + */ + private void runTool(final Runnable target) { + new I2PAppThread("MLabRunner") { @Override public void run() { try{ target.run(); }finally{ - //ap.setEnabled( true ); } } }.start(); } - public void runTest( - final Map<String,Object> args, - //final IPCInterface callback, - final boolean autoApply) - - throws Exception - { - synchronized( this ){ - if (test_active) { - throw new Exception("Test already active"); - } - test_active = true; - } - } - + /** + * Returned from runNDT + */ public interface ToolRun { public void cancel(); public void addListener(ToolRunListener l); } + /** + * Returned from runNDT + */ private class ToolRunImpl implements ToolRun { private List<ToolRunListener> listeners = new ArrayList<ToolRunListener>(); private boolean cancelled; @@ -319,10 +322,12 @@ public class MLabRunner { } } + /** The listener for ToolRun */ public interface ToolRunListener { public void cancelled(); } + /** The parameter for runNDT() */ public interface ToolListener { public void reportSummary(String str); public void reportDetail(String str); @@ -331,6 +336,8 @@ public class MLabRunner { /** standalone test */ private static class TestListener implements ToolListener { + private final AtomicBoolean _complete = new AtomicBoolean(); + public void reportSummary(String str) { System.out.println(str); } @@ -341,6 +348,11 @@ public class MLabRunner { public void complete(Map<String, Object> results) { System.out.println("**************** Results: " + DataHelper.toString(results) + "***********************"); + _complete.set(true); + } + + public boolean isComplete() { + return _complete.get(); } } @@ -348,7 +360,13 @@ public class MLabRunner { public static void main(String[] args) { I2PAppContext ctx = I2PAppContext.getGlobalContext(); MLabRunner mlab = MLabRunner.getInstance(ctx); - ToolListener lsnr = new TestListener(); + TestListener lsnr = new TestListener(); mlab.runNDT(lsnr); + try { Thread.sleep(2000); } catch (InterruptedException ie) { return; } + for (int i = 0; i < 180; i++) { + if (lsnr.isComplete()) + break; + try { Thread.sleep(1000); } catch (InterruptedException ie) { break; } + } } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHandler.java index c5c3d60941..7bf50fbddc 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHandler.java @@ -2,6 +2,9 @@ package net.i2p.router.web.helpers; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.vuze.plugins.mlab.MLabRunner; import net.i2p.router.Router; import net.i2p.router.transport.FIFOBandwidthRefiller; @@ -9,10 +12,23 @@ import net.i2p.router.web.FormHandler; /** * The new user wizard. + * This bean has SESSION scope so the results may be retrieved. + * All necessary methods are synchronized. * * @since 0.9.38 */ public class WizardHandler extends FormHandler { + + // session scope, but it's an underlying singleton + private MLabRunner _mlab; + // session scope + private TestListener _listener; + + @Override + public void setContextId(String contextId) { + super.setContextId(contextId); + _mlab = MLabRunner.getInstance(_context); + } @Override protected void processForm() { @@ -99,4 +115,121 @@ public class WizardHandler extends FormHandler { } return updated; } + + public synchronized boolean isNDTComplete() { + return _listener != null && _listener.isComplete(); + } + + public synchronized boolean isNDTRunning() { + return _listener != null && !_listener.isComplete(); + } + + /** + * @return status string or null + */ + public synchronized String getCompletionStatus() { + return _listener != null ? _listener.getSummary() : null; + } + + /** + * @return status string or null + */ + public synchronized String getDetailStatus() { + return _listener != null ? _listener.getDetail() : null; + } + + /** + * @return bytes per second or 0 + */ + public long getUpBandwidth() { + return getLongResult("up"); + } + + /** + * @return bytes per second or 0 + */ + public long getDownBandwidth() { + return getLongResult("down"); + } + + public synchronized long getLongResult(String key) { + if (_listener != null) { + Map<String, Object> results = _listener.getResults(); + if (results != null) { + Long v = (Long) results.get(key); + if (v != null) + return v.longValue(); + } + } + return 0; + } + + /** start the test */ + public synchronized void startNDT() { + if (_mlab.isRunning() || _listener != null && !_listener.isComplete()) { + addFormError(_t("Bandwidth test is already running")); + return; + } + _listener = new TestListener(); + MLabRunner.ToolRun runner = _mlab.runNDT(_listener); + if (runner != null) { + addFormNotice(_t("Started bandwidth test")); + } else { + Map<String, Object> map = new HashMap<String, Object>(2); + _listener.complete(map); + addFormError(_t("Bandwidth test is already running")); + } + } + + /** cancel the test */ + public synchronized void cancelNDT() { + synchronized(WizardHandler.class) { + if (!_mlab.isRunning()) { + addFormError(_t("Bandwidth test was not running")); + return; + } +/**** +TODO + if (runner != null) + addFormNotice(_t("Started bandwidth test")); + else + addFormError(_t("Bandwidth test is already running")); +****/ + } + } + + /** test results */ + private static class TestListener implements MLabRunner.ToolListener { + private String _summary, _detail; + private Map<String, Object> _results; + + public synchronized void reportSummary(String str) { + _summary = str; + } + + public synchronized void reportDetail(String str) { + _detail = str; + } + + public synchronized void complete(Map<String, Object> results) { + _results = results; + } + + public synchronized boolean isComplete() { + return _results != null; + } + + public synchronized String getSummary() { + return _summary; + } + + public synchronized String getDetail() { + return _detail; + } + + public synchronized Map<String, Object> getResults() { + return _results; + } + } + } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHelper.java index 8420e139d7..c3a326d006 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/WizardHelper.java @@ -9,7 +9,9 @@ import net.i2p.router.web.HelperBase; */ public class WizardHelper extends HelperBase { + public static final String PROP_COMPLETE = "routerconsole.welcomeWizardComplete"; + public void complete() { - _context.router().saveConfig("routerconsole.welcomeWizardComplete", "true"); + _context.router().saveConfig(PROP_COMPLETE, "true"); } } diff --git a/apps/routerconsole/jsp/index.jsp b/apps/routerconsole/jsp/index.jsp index 10010d1819..bc2939e6cc 100644 --- a/apps/routerconsole/jsp/index.jsp +++ b/apps/routerconsole/jsp/index.jsp @@ -12,6 +12,7 @@ // while preserving any query parameters // response.setStatus(307); + response.setHeader("Cache-Control","no-cache"); String req = request.getRequestURL().toString(); StringBuilder buf = new StringBuilder(128); if (req.endsWith("index")) @@ -23,13 +24,13 @@ buf.append('/'); net.i2p.I2PAppContext ctx = net.i2p.I2PAppContext.getGlobalContext(); boolean oldHome = ctx.getBooleanProperty("routerconsole.oldHomePage"); - boolean wizRun = ctx.getBooleanProperty("routerconsole.welcomeWizardComplete"); + boolean wizRun = ctx.getBooleanProperty(net.i2p.router.web.helpers.WizardHelper.PROP_COMPLETE); String firstVersion = ctx.getProperty("router.firstVersion"); String tgt; final boolean ENABLE_WIZARD_ON_FIRST_RUN = false; if (oldHome) { tgt = "console"; - } else if (ENABLE_WIZARD_ON_FIRST_RUN && (wizRun || firstVersion == null)) { + } else if (!ENABLE_WIZARD_ON_FIRST_RUN || wizRun || firstVersion == null) { // wizard already run tgt = "home"; } else { @@ -52,4 +53,4 @@ response.setHeader("Location", buf.toString()); // force commitment response.getOutputStream().close(); -%> \ No newline at end of file +%> diff --git a/apps/routerconsole/jsp/welcome.jsp b/apps/routerconsole/jsp/welcome.jsp index 5056807248..906040c6b8 100644 --- a/apps/routerconsole/jsp/welcome.jsp +++ b/apps/routerconsole/jsp/welcome.jsp @@ -42,6 +42,7 @@ // redirect to /home response.setStatus(307); + response.setHeader("Cache-Control","no-cache"); String req = request.getRequestURL().toString(); int slash = req.indexOf("/welcome"); if (slash >= 0) @@ -63,8 +64,8 @@ <script src="/js/ajax.js" type="text/javascript"></script> <script type="text/javascript"> var failMessage = "<hr><b><%=intl._t("Router is down")%><\/b>"; - function requestAjax1() { ajax("/welcomexhr.jsp?requestURI=<%=request.getRequestURI()%>", "xhr", "1000"); } - function initAjax() { setTimeout(requestAjax1, <%=intl.getRefresh()%>000); } + function requestAjax1() { ajax("/welcomexhr.jsp", "xhr", "1000"); } + function initAjax() { setTimeout(requestAjax1, "1000"); } </script> <% } @@ -83,7 +84,12 @@ %> <h2><%=intl._t("New Install Wizard")%> <%=ipg%>/<%=LAST_PAGE%></h2> <div id="wizard"> -<jsp:useBean class="net.i2p.router.web.helpers.WizardHandler" id="formhandler" scope="request" /> +<%-- + // note that for the handler we use a session scope, not a page scope, + // so that we can access the NDT test results. + // The MLabHelper singleton will prevent multiple simultaneous tests, even across sessions. +--%> +<jsp:useBean class="net.i2p.router.web.helpers.WizardHandler" id="formhandler" scope="session" /> <%@include file="formhandler.jsi" %> <form action="" method="POST"> <input type="hidden" name="nonce" value="<%=pageNonce%>"> -- GitLab