microBean™ Jersey Netty Integration

Build Status Maven Central

The microBean™ Jersey Netty Integration project integrates Jersey into Netty in an idiomatic way.

Jersey can be run as a simple handler of sorts in a Netty pipeline. The Netty event loop remains unblocked, there is no locking, reads and writes involve no copying of byte arrays, output is streamed where appropriate and it is intended that there be as few object allocations as possible.

HTTP 1.1 and HTTP/2 are both supported, including upgrades via HTTP’s upgrade header, ALPN or prior knowledge.

Status

This project uses semantic versioning. It is still in an alpha state.

Installation

Add a dependency on this project in your Netty-based Maven project:

<dependency>
  <groupId>org.microbean</groupId>
  <artifactId>microbean-jersey-netty</artifactId>
  <version>0.25.0</version>
</dependency>

Usage

To use, install an instance of JerseyChannelInitializer as the child handler of a Netty ServerBootstrap:

serverBootstrap.childHandler(new JerseyChannelInitializer(baseUri, // e.g. URI.create("/")
    sslContext, // an SslContext, or null if you don't want TLS support
    true, // yes, HTTP/2 support please
    20971520L, // 20MB maximum incoming payload (arbitrary)
    new DefaultEventExecutorGroup(8), // a DefaultEventExecutorGroup with 8 threads (arbitrary) to run your application
    true, // yes, use Jersey's native dependency injection facilities
    new ApplicationHandler(yourJaxRsApplication), // the ApplicationHandler wrapping your application
    8192, // write in 8K chunks (arbitrary)
    Unpooled::new /* how to create those chunks */));

Background and Motivation

While Jersey itself contains a Netty integration project, it is annotated with @Beta, and the author additionally writes that his “implementation cannot be more experimental”. In addition, there are several issues with the Jersey-supplied Netty integration project. The most problematic seems to be issue 3500. This issue and others stem from the fact that the Jersey-supplied Netty integration project sets up its own internal queues for streaming, which overflow. Additionally, new ByteBuffers and InputStreams are allocated throughout. It is also not entirely clear if HTTP/2 is fully supported.

Recently, there have been some efforts to improve this area, but they still do not take advantage of the built in queuing and threading constructs offered up by Netty itself.

Implementation Details

By contrast, microBean™ Jersey Netty Integration approaches the problem of running a Jakarta RESTful Web Services application under Netty by following the spirit of Netty.

Several composable channel pipeline components are provided.

Considering HTTP 1.1 support, the first is a decoder that decodes HttpRequest and HttpContent messages into ContainerRequest objects, which are the objects consumed natively by Jersey.

A ContainerRequest may need an entity stream, and that can be tricky. Since a Jakarta RESTful Web Services application may block whatever thread it is running on for some time (for example, perhaps it is performing synchronous database access over a slow connection), then normally it should be run on its own thread that is not the Netty event loop. But assuming that this is so, the InputStream it will receive by way of a ContainerRequest’s entity stream will now be operated on by two threads: the application-hosting thread, and the Netty event loop. microBean™ Jersey Netty Integration ensures that this InputStream implementation is thread-safe, uses as few locks as possible, allocates as little memory as possible, and takes advantage of CompositeByteBuf and other Netty native constructs to ensure this inter-thread messaging is safe and efficient.

Similarly, a ContainerRequest needs a ContainerResponseWriter which is responsible for actually writing the Jakarta RESTful Web Services application’s output. microBean™ Jersey Netty Integration provides a ContainerResponseWriter implementation that is also a ChannelInboundHandlerAdapter: it takes ContainerRequests as input, installs itself as their ContainerResponseWriter implementation, and writes the requisite output back to the channel safely and efficiently.

The OutputStream implementation used to do this must, of course, ensure that writes take place on the Netty event loop. microBean™ Jersey Netty Integration ensures that this OutputStream implementation does not needlessly buffer and/or copy byte arrays but instead takes advantage of the built-in outbound event queuing present in the Netty event loop itself.

Similar constructs are provided for HTTP/2 support as well.