3

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?

Community
  • 1
  • 1
Suma
  • 33,181
  • 16
  • 123
  • 191
  • can you just send to server a special request--shutdown command, after client accepts all the data? – Alex Salauyou Mar 21 '16 at 10:10
  • @SashaSalauyou This sounds interesting. However the client in this case is a desktop web browser, and I have no control over it, other than the web page I serve it. Perhaps there could be some way sending some data from the page using Javascript, but even if it is, what if the Javascript is disabled / not supported on the server.server, perhaps with Javascri – Suma Mar 21 '16 at 10:18

1 Answers1

4

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

Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67