Java 11 was released two years ago on September 25, 2018 and Java 15 is due to be released anytime now. There were many new features added in Java versions 9 and 10 before Java 11. However, with the frequency of so many new Java releases, most of the Java Developer Community is still using Java 8. Java 8 was a game changer to the Java Developer Community after Java 5. The same is true for Java 11.
One of the common goals for a new Java version is to bring third-party library functionalities into Java. Bringing in Java Time from JODA and Dependency Injection from Spring Framework are great examples.
Similarly, for invoking HTTP services we would most likely use Apache HttpComponents or OKHTTP (Spring Framework has an abstraction over these libraries). With this new HTTP Client addition, we do not need any of those libraries. This blog post will discuss the HTTP Client feature in Java 11 with a focus on the new classes in the package java.net.http and the HTTP Client related classes (though the module java.net.http has both HTTP Client and Websocket API).
Below are some highlights of the HTTP Client:
- Support for both HTTP Protocol version 1.1 and 2.0.
- Asynchronous support using CompletableFutures (Executor Framework).
- Adoption of reactive streams for request and response.
- Default HTTP/2 behavior and under the hood switching for HTTP/1 servers.
- Excellent performance because of Header compression and Single Connection for multiple requests.
Three important classes in the package java.net.http form the basis for the HTTP Client functionality:
HttpClient class is used to send requests and retrieve their responses. HttpClient instance is created through a newBuilder() method which uses builder pattern. The builder pattern is used when different instances need to be created with different combinations of mandatory or optional fields of a class. Generally, having multiple constructors for this approach is discouraged for being error prone. HttpClient instance can be created with different configurations like: the preferred protocol version ( HTTP/1.1 or HTTP/2 ), whether to follow redirects, a proxy, an authenticator, etc. HttpClient instances are immutable and can be used to send multiple requests.
HttpClient.Builder instances are created from a newBuilder() call, which is not thread-safe. The proper synchronization approach should be considered when the instances are created from the multi-threaded environment.
Once an HttpClient instance is created, it is ready to send multiple HttpRequest and retrieve HttpResponse.
Similar to HttpClient, an HttpRequest instance can be built through an HttpRequest builder (there are no public constructors available). The newBuilder() method is used to get an instance of HttpRequest.Builder which has two variants, one with empty parameters and another takes the URI as an input parameter. A request’s URI, headers, and body can be set. Builders are reusable in the sense that they can be copied and modified many times to build multiple related requests that differ in some parameters.
The HttpResponse instance is returned when the HttpRequest is sent by the HttpClient. When a response is received in the HttpClient, the BodyHandler (discussed later) that is passed when an HttpRequest is made will be used to handle the response that includes the response status code and headers, and typically after the response body has been completely received.
- Other Notable Classes
A HttpResponse.BodyHandler must be supplied for each HttpRequest sent. The HttpResponse.BodyHandler is required when sending an HttpRequest when invoking a call using HttpClient which defines how a response body is handled.
The HttpRequest.BodyPublisher is used to publish a HttpRequest which extends Flow.Publisher<ByteBuffer>. HttpRequest.BodyPublisher’s role is to convert Java objects into a flow of byte buffers to send as a request body. For contents of content length 0, meaning no bodies, this is not required. For each HttpRequest.BodyPublisher, a new Subscriber will be created by the HttpClient. So, for any reason if the ByteBuffer publisher published is incorrect and resulted in errors, it cannot be accessed.
The HttpResponse.BodySubscriber is used to subscribe to the HttpRequest.BodyPublisher to convert the response body bytes (the flow of data containing the response body is immutable) and convert into Java objects. Any implementation of the HttpResponse.BodySubscriber should be sure to request more data until onComplete or onError are triggered.
This handler is specific for push promises, which is specific to the HTTP/2. A push promise is a request sent by an HTTP/2 server when retrieving an initiating client-sent request. The HTTP/2-compliant server usually inspects the request and its headers and will send some resources which might have been asked by the client anyway preemptively.
Sample Synchronous GET
Sample Asynchronous GET
Sample Synchronous POST
Sample Multiple Concurrent GET
Sample Asynchronous GET using Proxy
Based on these samples, you can see how easy it is to use Java 11 HttpClient features without adding one more dependency. Stay tuned for more posts on additional Java 11 features. In the meantime, if your organization has any questions around any Java 11 features, please don’t hesitate to reach out to us. We’d love to help you out.