2

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
  • 31,745
  • 15
  • 120
  • 184
  • 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

2 Answers2

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
  • 13,786
  • 4
  • 41
  • 67
0

Old post, but I would like to suggest an alternative for anyone else interested. I understand you want to shutdown the server but do you really need to? Would it not be sufficient to just not accept requests?

I am handling such a situation where the server is launched as part of another java application. Have a flag in the handler and update it once the required condition is met and there on, simply stop accepting requests. This way, you could also send some useful information to the client if you'd like on receiving requests after the flag is updated.

PKU
  • 315
  • 4
  • 14