Search code examples
javaasynchronousundertow

How to execute blocking code in a non-blocking Handler in Undertow?


As described in a separate question, when using Undertow, all the processing should be done in a dedicated Worker thread pool, which looks like this:

public class Start {

  public static void main(String[] args) {
    Undertow server = Undertow.builder()
        .addListener(8080, "localhost")
        .setHandler(new HttpHandler() {
          public void handleRequest(HttpServerExchange exchange)
              throws Exception {
            if (exchange.isInIoThread()) {
              exchange.dispatch(this);
              return;
            }
            exchange.getResponseHeaders()
                    .put(Headers.CONTENT_TYPE, "text/plain");
            exchange.getResponseSender()
                    .send("Hello World");
          }
        })
        .build();
    server.start();
  }
}

I understand that BlockingHandler can be used for explicitly telling Undertow to schedule the request on a dedicated thread pool for blocking requests. We could adapt the above example by wrapping the HttpHandler in an instance of BlockingHandler, like so:

        .setHandler(new BlockingHandler(new HttpHandler() {

This would work for calls that we know are always blocking.

However, in case some code is non-blocking most of the time, but sometimes requires a blocking call, how to turn that blocking call into a non-blocking one? For example, if the requested value is present in cache, the following code would not block (it's just fetching from some Map<>), but if it's not, it has to be fetched from the database.

public class Start {

  public static void main(String[] args) {
    Undertow server = Undertow.builder()
        .addListener(8080, "localhost")
        .setHandler(new HttpHandler() {
          public void handleRequest(HttpServerExchange exchange)
              throws Exception {
            if (exchange.isInIoThread()) {
              exchange.dispatch(this);
              return;
            }
            if (valueIsPresentInCache(exchange)) {
              return valueFromCache;  // non-blocking
            } else {
              return fetchValueFromDatabase(); // blocking!!!
            }
          }
        })
        .build();
    server.start();
  }
}

According to the docs, there is a method HttpServerExchange.startBlocking(), but according to JavaDoc, unless you really need to use the input stream, this call is still a blocking one.

Calling this method puts the exchange in blocking mode, and creates a BlockingHttpExchange object to store the streams. When an exchange is in blocking mode the input stream methods become available, other than that there is presently no major difference between blocking an non-blocking modes

How would one turn this blocking call into a non-blocking one?


Solution

  • The correct way is to actually do the logic in the IO thread, if it is non-blocking. Otherwise, delegate the request to a dedicated thread, like this:

    public class Example {
    
      public static void main(String[] args) {
        Undertow server = Undertow.builder()
            .addListener(8080, "localhost")
            .setHandler(new HttpHandler() {
              public void handleRequest(HttpServerExchange exchange)
                  throws Exception {
    
                if (valueIsPresentInCache(exchange)) {
                  getValueFromCache();  // non-blocking, can be done from IO thread           
                } else {
    
                  if (exchange.isInIoThread()) {
                    exchange.dispatch(this);
                    // we return immediately, otherwise this request will be
                    // handled both in IO thread and a Worker thread, throwing
                    // an exception
                    return;
                  }
    
                  fetchValueFromDatabase(); // blocking!!!
    
                }
              }
            })
            .build();
        server.start();
      }
    }