Once upon a time, using the Java SE (Standard Edition) APIs to do common HTTP operations such as REST API calls might have been described as unnatural and cumbersome. Java 11 officially changes this. Coming with Java 11, the incubated HTTP APIs from Java 9 are now officially incorporated into the Java SE API. The JDK Enhancement Proposal (JEP) 321 was the JEP behind this effort. Since being integrated into Java 11, the API has seen a few changes. As of Java 11, the API is now fully asynchronous. This article will attempt to show you basic use of the new API by performing a REST API call. We will be using openJDK 11.
The APIs use
java.util.concurrent.CompleteableFuture<T> to provide asynchronous, non-blocking request/response behavior allowing for dependent actions. The API used new-style OOP method chaining, like a builder which returns an object that may be affected by the method call.
In addition to the standard documentation, you can learn how to practically apply the
CompleteableFuture<T> API below.
The new HTTP APIs can be found in
The new APIs provide native support for HTTP 1.1/2, WebSocket. The core classes & interface providing the core functionality include:
The new API is, semantically, everything you would expect of a Java API in 2018. For a start, it is verbose. Secondly, it is modular. Thirdly, it follows the new OOP style of method chaining (or using builders) to construct objects. Fourthly, it feels more natively HTTP.
The bane and (to some) the valor of java has always been verbosity. In Java, unlike most duck typed languages, it is easier to the eye to induce types and behavior. Java API typically follows a natural approach to naming as one might expect in the real world — it is usually intuitive. In addition, java’s typing semantics are explicit, adding to this verbosity. However, the typical verbosity was reduced in my examples through my frequent use of Java 10’s var. Nevertheless, the API feels a bit verbose.
However, credit must be given to the API writers for allowing the implementation to be as configured or as least configured as desired. The new OOP style has allowed for that. For that matter, it should also be noted that there are many things that one can configure in the HTTP request response cycle. Therefore, a great deal of verbosity is expected.
Following the long standing unix tradition, long beloved by the Java community and in line with Java 9 efforts, the API is quite modular. The API is careful split into pieces that offer little to no cruft — one gets what one wants and usually nothing more. In addition, there is little dependency on classes not immediately relevant to the task at hand.
One exception to this rule that could be argued is the creation of a WebSocket. The most straightforward (and API recommended) way to create a WebSocket is to use an instance of WebSocket.Builder. WebSocket.Builder instances are most straightforwardly created by using
java.net.http.HttpClient.newWebSocketBuilder(). Once that call has been made, it is typically chained with a call to
java.net.WebSocket.Builder.buildAsynce(URI uri, WebSocket.LIstener) to produce a CompleteableFuture<WebSocket> object associated with the WebSocket. The WebSocket may then be retrieved by calling get() on the CompleteableFuture object.
What I like about designing an API with method chaining as a means to construct objects is that it allows for objects that are as least configured as you would like them to be or as highly configured as you would like them to be. This result can be achieved by way of overloading constructors, of course, but after the object has been created one has to call a setter() method to update or a property on the object. With builders, one may retrieve the modified object all in one call. It plays well into handling things like HTTP, where there are many properties to specify. It would be unwieldy to have to put in a 10 argument constructor list to modify an object. This style has been welcomed by the OOP community at large and is here to stay.
Lastly, the API feels more native to HTTP. Method names such as body() and headers() are more appropriate ways to name a method than getContent() and getHeaderField(), getHeaderKey(). The old APIs seemed to me to be to abstract and concerned with networking in a general sense as opposed to HTTP. It’s also more intuitive in terms of specifying BodyHandlers, and so on. In the old API, this felt like an unnatural operation, or rather, specifying them felt like incorporating a foreign citizen; The design and use of the API can lead to some unexpected behavior. This is, however, just my impression.
The advantages that have personally caught my eye over the old API are:
The full source code, including examples using WebSockets, can be found here.
Create your free account to unlock your custom reading experience.