I am using a simple Http server from com.sun.net.httpserver
, as described in simple HTTP server in Java using only Java SE API.
Everything works fine, however I am unsure how can I cleanly shutdown the server once I no longer need it. I do not want to stop the server while it is still sending the data for some request, only once it is idle.
My particular scenario is a desktop application performing OAuth dance. I use a local web server embedded in the application to provide a callback response, the application launches a desktop browser to show the server response using java.awt.Desktop.browse
API.
I have tried calling HttpServer.stop(0)
directly from my HttpHandler.handle
function, after I have written the response page into the HttpExchange
response output stream, but this is too early, the browser shows ERR_EMPTY_RESPONSE
.
The same happens when I stop the server once my main application has finished its work - this is often too early, the HttpServer
has not completed sending the data out yet at that moment.
I could provide a few seconds delay value to stop, but I would like to achieve a proper and clean synchronization.
What is a proper solution to this?
I can offer a solution based on one CountDownLatch
+ custom ExecutorService
that is set up as executor for HttpServer
:
public void runServer() throws Exception {
final ExecutorService ex = Executors.newSingleThreadExecutor();
final CountDownLatch c = new CountDownLatch(1);
HttpServer server = HttpServer.create(new InetSocketAddress(8888), 0);
server.createContext("/test", (HttpExchange h) -> {
StringBuilder resp = new StringBuilder();
for (int i = 0; i < 1_000_000; i++)
resp.append(i).append(", ");
String response = resp.toString();
h.sendResponseHeaders(200, response.length());
OutputStream os = h.getResponseBody();
os.write(response.getBytes());
os.close();
c.countDown(); // count down, letting `c.await()` to return
});
server.setExecutor(ex); // set up a custom executor for the server
server.start(); // start the server
System.out.println("HTTP server started");
c.await(); // wait until `c.countDown()` is invoked
ex.shutdown(); // send shutdown command to executor
// wait until all tasks complete (i. e. all responses are sent)
ex.awaitTermination(1, TimeUnit.HOURS);
server.stop(0);
System.out.println("HTTP server stopped");
}
I did test this on our work network environment, and it seems to work correctly. HttpServer
stops no earlier than response is fully sent, so I think this is exactly what you need.
Another approach may be not shutting down the executor ex
, but sending there a new task containing server.stop()
after the response is written to a stream. As ex
is constructed single-threaded, such task will execute not earlier than previous task completes, i. e. a response is fully sent:
public void run() throws Exception {
final ExecutorService ex = Executors.newSingleThreadExecutor();
final HttpServer server = HttpServer.create(new InetSocketAddress(8888), 0);
server.createContext("/test", (HttpExchange h) -> {
// ... generate and write a response
ex.submit(() -> {
server.stop(0);
System.out.println("HTTP server stopped");
});
});
server.setExecutor(ex);
server.start();
System.out.println("HTTP server started");
}
For more information, see ExecutorService