diff --git a/build.gradle b/build.gradle
index 7d2d24d968..f82d571123 100644
--- a/build.gradle
+++ b/build.gradle
@@ -330,6 +330,9 @@ configure(project(':monitor')) {
compile('com.github.JesusMcCloud.netlayer:tor.native:0.6.2') {
exclude(module: 'slf4j-api')
}
+ compile('com.github.JesusMcCloud.netlayer:tor.external:0.6') {
+ exclude(module: 'slf4j-api')
+ }
testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.2'
testCompile 'org.junit.jupiter:junit-jupiter-params:5.3.2'
diff --git a/monitor/src/main/java/bisq/monitor/Monitor.java b/monitor/src/main/java/bisq/monitor/Monitor.java
index fe3dfdec98..34a59d4636 100644
--- a/monitor/src/main/java/bisq/monitor/Monitor.java
+++ b/monitor/src/main/java/bisq/monitor/Monitor.java
@@ -29,6 +29,7 @@ import org.berndpruenster.netlayer.tor.Tor;
import bisq.monitor.metric.TorStartupTime;
import bisq.monitor.metric.Metric;
import bisq.monitor.metric.TorRoundtripTime;
+import bisq.monitor.metric.TorHiddenServiceStartupTime;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Signal;
@@ -64,6 +65,7 @@ public class Monitor {
// assemble Metrics
metrics.add(new TorStartupTime());
metrics.add(new TorRoundtripTime());
+ metrics.add(new TorHiddenServiceStartupTime());
// configure Metrics
Properties properties = getProperties();
diff --git a/monitor/src/main/java/bisq/monitor/metric/TorHiddenServiceStartupTime.java b/monitor/src/main/java/bisq/monitor/metric/TorHiddenServiceStartupTime.java
new file mode 100644
index 0000000000..8c36f5e0a0
--- /dev/null
+++ b/monitor/src/main/java/bisq/monitor/metric/TorHiddenServiceStartupTime.java
@@ -0,0 +1,95 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.monitor.metric;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.berndpruenster.netlayer.tor.HiddenServiceSocket;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * A Metric to measure the startup time of a Tor Hidden Service on a already
+ * running Tor.
+ *
+ * @author Florian Reimair
+ */
+@Slf4j
+public class TorHiddenServiceStartupTime extends Metric {
+
+ private static final String SERVICE_PORT = "run.servicePort";
+ private static final String LOCAL_PORT = "run.localPort";
+ private final String hiddenServiceDirectory = "metric_" + getName();
+
+ public TorHiddenServiceStartupTime() throws IOException {
+ super();
+ }
+
+ /**
+ * synchronization helper. Required because directly closing the
+ * HiddenServiceSocket in its ReadyListener causes a deadlock
+ */
+ private void await() {
+ synchronized (hiddenServiceDirectory) {
+ try {
+ hiddenServiceDirectory.wait();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void proceed() {
+ synchronized (hiddenServiceDirectory) {
+ hiddenServiceDirectory.notify();
+ }
+ }
+
+ @Override
+ protected void execute() {
+ // prepare settings. Fetch them everytime we run the Metric so we do not have to
+ // restart on a config update
+ int localPort = Integer.parseInt(configuration.getProperty(LOCAL_PORT, "9998"));
+ int servicePort = Integer.parseInt(configuration.getProperty(SERVICE_PORT, "9999"));
+
+ // clear directory so we get a new onion address everytime
+ new File(hiddenServiceDirectory).delete();
+
+ log.debug("creating the hidden service");
+ // start timer - we do not need System.nanoTime as we expect our result to be in
+ // the range of tenth of seconds.
+ long start = System.currentTimeMillis();
+
+ HiddenServiceSocket hiddenServiceSocket = new HiddenServiceSocket(localPort, hiddenServiceDirectory,
+ servicePort);
+ hiddenServiceSocket.addReadyListener(socket -> {
+ // stop the timer and report
+ report(System.currentTimeMillis() - start);
+ log.debug("the hidden service is ready");
+ proceed();
+ return null;
+ });
+
+ await();
+ log.debug("going to unpublish the hidden service...");
+ hiddenServiceSocket.close();
+ log.debug("[going to unpublish the hidden service...] done");
+ }
+}
diff --git a/monitor/src/main/resources/metrics.properties b/monitor/src/main/resources/metrics.properties
index b1d711e6b6..1c584561de 100644
--- a/monitor/src/main/resources/metrics.properties
+++ b/monitor/src/main/resources/metrics.properties
@@ -18,5 +18,11 @@ TorRoundtripTime.run.sampleSize=3
# torproject.org hidden service
TorRoundtripTime.run.hosts=http://expyuzz4wqqyqhjn.onion:80
+#TorHiddenServiceStartupTime Metric
+TorHiddenServiceStartupTime.enabled=true
+TorHiddenServiceStartupTime.run.interval=2
+TorHiddenServiceStartupTime.run.localPort=90501
+TorHiddenServiceStartupTime.run.servicePort=90511
+
#Another Metric
Another.run.interval=5
\ No newline at end of file
diff --git a/monitor/src/test/java/bisq/monitor/TorHiddenServiceStartupTimeTests.java b/monitor/src/test/java/bisq/monitor/TorHiddenServiceStartupTimeTests.java
new file mode 100644
index 0000000000..6535380cb5
--- /dev/null
+++ b/monitor/src/test/java/bisq/monitor/TorHiddenServiceStartupTimeTests.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.monitor;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.berndpruenster.netlayer.tor.NativeTor;
+import org.berndpruenster.netlayer.tor.Tor;
+import org.berndpruenster.netlayer.tor.TorCtlException;
+import org.junit.Assert;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import bisq.monitor.metric.TorHiddenServiceStartupTime;
+
+public class TorHiddenServiceStartupTimeTests {
+
+ private class Dut extends TorHiddenServiceStartupTime {
+
+ private long result;
+
+ public Dut() throws IOException {
+ super();
+ }
+
+ @Override
+ protected void report(long value) {
+ super.report(value);
+
+ result = value;
+ }
+
+ public long results() {
+ return result;
+ }
+ }
+
+ private final static File torWorkingDirectory = new File(TorHiddenServiceStartupTimeTests.class.getSimpleName());
+
+ @BeforeAll
+ public static void setup() throws TorCtlException {
+ Tor.setDefault(new NativeTor(torWorkingDirectory));
+ }
+
+ @Test
+ public void run() throws Exception {
+
+ // configure
+ Properties configuration = new Properties();
+ configuration.put("Dut.enabled", "true");
+ configuration.put("Dut.run.interval", "5");
+
+ Dut DUT = new Dut();
+ DUT.configure(configuration);
+
+ // start
+ DUT.start();
+
+ // give it some time and then stop
+ Thread.sleep(180 * 1000);
+ DUT.shutdown();
+
+ // observe results
+ Assert.assertTrue(DUT.results() > 0);
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ Tor.getDefault().shutdown();
+ torWorkingDirectory.delete();
+ }
+}