Skip to content

Commit d6ea880

Browse files
r1-dockerbrian-brazil
authored andcommitted
Adding ability to control whether the httpserver is a daemon thread (#300)
1 parent 4f64a6c commit d6ea880

File tree

2 files changed

+122
-6
lines changed

2 files changed

+122
-6
lines changed

simpleclient_httpserver/src/main/java/io/prometheus/client/exporter/HTTPServer.java

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
import java.util.Set;
1414
import java.util.HashSet;
1515
import java.util.concurrent.ExecutorService;
16+
import java.util.concurrent.ExecutionException;
1617
import java.util.concurrent.Executors;
18+
import java.util.concurrent.FutureTask;
19+
import java.util.concurrent.ThreadFactory;
1720
import java.util.zip.GZIPOutputStream;
1821

1922
import com.sun.net.httpserver.HttpHandler;
@@ -108,36 +111,106 @@ protected static Set<String> parseQuery(String query) throws IOException {
108111
return names;
109112
}
110113

111-
protected HttpServer server;
114+
115+
static class DaemonThreadFactory implements ThreadFactory {
116+
private ThreadFactory delegate;
117+
private final boolean daemon;
118+
119+
DaemonThreadFactory(ThreadFactory delegate, boolean daemon) {
120+
this.delegate = delegate;
121+
this.daemon = daemon;
122+
}
123+
124+
@Override
125+
public Thread newThread(Runnable r) {
126+
Thread t = delegate.newThread(r);
127+
t.setDaemon(daemon);
128+
return t;
129+
}
130+
131+
static ThreadFactory defaultThreadFactory(boolean daemon) {
132+
return new DaemonThreadFactory(Executors.defaultThreadFactory(), daemon);
133+
}
134+
}
135+
136+
protected final HttpServer server;
112137
protected final ExecutorService executorService;
113138

114139

115140
/**
116141
* Start a HTTP server serving Prometheus metrics from the given registry.
117142
*/
118-
public HTTPServer(InetSocketAddress addr, CollectorRegistry registry) throws IOException {
143+
public HTTPServer(InetSocketAddress addr, CollectorRegistry registry, boolean daemon) throws IOException {
119144
server = HttpServer.create();
120145
server.bind(addr, 3);
121146
HttpHandler mHandler = new HTTPMetricHandler(registry);
122147
server.createContext("/", mHandler);
123148
server.createContext("/metrics", mHandler);
124-
executorService = Executors.newFixedThreadPool(5);
149+
executorService = Executors.newFixedThreadPool(5, DaemonThreadFactory.defaultThreadFactory(daemon));
125150
server.setExecutor(executorService);
126-
server.start();
151+
start(daemon);
152+
}
153+
154+
/**
155+
* Start a HTTP server serving Prometheus metrics from the given registry using non-daemon threads.
156+
*/
157+
public HTTPServer(InetSocketAddress addr, CollectorRegistry registry) throws IOException {
158+
this(addr, registry, false);
127159
}
128160

129161
/**
130162
* Start a HTTP server serving the default Prometheus registry.
131163
*/
164+
public HTTPServer(int port, boolean daemon) throws IOException {
165+
this(new InetSocketAddress(port), CollectorRegistry.defaultRegistry, daemon);
166+
}
167+
168+
/**
169+
* Start a HTTP server serving the default Prometheus registry using non-daemon threads.
170+
*/
132171
public HTTPServer(int port) throws IOException {
133-
this(new InetSocketAddress(port), CollectorRegistry.defaultRegistry);
172+
this(port, false);
134173
}
135174

136175
/**
137176
* Start a HTTP server serving the default Prometheus registry.
138177
*/
178+
public HTTPServer(String host, int port, boolean daemon) throws IOException {
179+
this(new InetSocketAddress(host, port), CollectorRegistry.defaultRegistry, daemon);
180+
}
181+
182+
/**
183+
* Start a HTTP server serving the default Prometheus registry using non-daemon threads.
184+
*/
139185
public HTTPServer(String host, int port) throws IOException {
140-
this(new InetSocketAddress(host, port), CollectorRegistry.defaultRegistry);
186+
this(new InetSocketAddress(host, port), CollectorRegistry.defaultRegistry, false);
187+
}
188+
189+
/**
190+
* Start a HTTP server by making sure that its background thread inherit proper daemon flag.
191+
*/
192+
private void start(boolean daemon) {
193+
if (daemon == Thread.currentThread().isDaemon()) {
194+
server.start();
195+
} else {
196+
FutureTask<Void> startTask = new FutureTask<Void>(new Runnable() {
197+
@Override
198+
public void run() {
199+
server.start();
200+
}
201+
}, null);
202+
DaemonThreadFactory.defaultThreadFactory(daemon).newThread(startTask).start();
203+
try {
204+
startTask.get();
205+
} catch (ExecutionException e) {
206+
throw new RuntimeException("Unexpected exception on starting HTTPSever", e);
207+
} catch (InterruptedException e) {
208+
// This is possible only if the current tread has been interrupted,
209+
// but in real use cases this should not happen.
210+
// In any case, there is nothing to do, except to propagate interrupted flag.
211+
Thread.currentThread().interrupt();
212+
}
213+
}
141214
}
142215

143216
/**
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package io.prometheus.client.exporter;
2+
3+
import org.junit.Test;
4+
5+
import java.io.IOException;
6+
import java.util.concurrent.Callable;
7+
import java.util.concurrent.ExecutionException;
8+
import java.util.concurrent.FutureTask;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
12+
public class TestDaemonFlags {
13+
14+
@Test
15+
public void testDefaultIsNotDaemon() throws IOException, ExecutionException, InterruptedException {
16+
assertThat(usesDaemonExecutor(new HTTPServer(0))).isFalse();
17+
}
18+
19+
@Test
20+
public void testDaemon() throws IOException, ExecutionException, InterruptedException {
21+
assertThat(usesDaemonExecutor(new HTTPServer(0, true))).isTrue();
22+
}
23+
24+
@Test
25+
public void testNonDaemon() throws IOException, ExecutionException, InterruptedException {
26+
assertThat(usesDaemonExecutor(new HTTPServer(0, false))).isFalse();
27+
}
28+
29+
private boolean usesDaemonExecutor(HTTPServer httpServer) throws IOException, InterruptedException, ExecutionException {
30+
try {
31+
FutureTask<Boolean> task = new FutureTask<Boolean>(new Callable<Boolean>() {
32+
@Override
33+
public Boolean call() throws Exception {
34+
return Thread.currentThread().isDaemon();
35+
}
36+
});
37+
httpServer.server.getExecutor().execute(task);
38+
return task.get();
39+
} finally {
40+
httpServer.stop();
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)