The HttpClient
in Java 11
The new and fresh HttpClient
is long overdue. Finally support for the latest HTTP/2 and Web Socket. But what if you want to cache the responses you receive? And how do you add custom behaviour as there is no source code available?
Decorator Design Pattern
This is a nice application of the Decorator Design Pattern. You can visualize this pattern as an onion. In this particular case, the onion core is Java’s HttpClient
, and the next layer is the caching we’ll add.
Let’s code!
The steps we will implement are the following:
- Add a new implementation of
HttpClient
, by creating a new subclassCachingHttpClient
; - This implementation will have
- a
HttpClient
field to delegate the requests to; - a basic
Map<HttpRequest, HttpResponse>
field for caching. I use aMap
to keep things simple, feel free to add a real cache provided by Guava or its successor Caffeine; - all abstract methods will call the same method on the delegate field, except the
send
method.
- a
public class CachingHttpClient extends HttpClient{
private Map<HttpRequest, HttpResponse> cache;
private HttpClient delegate;
public CachingHttpClient(HttpClient client){
cache = new HashMap<>();
delegate = client;
}
// example how to delegate a method we don't want to enhance
public SSLContext sslContext(){
return delegate.sslContext();
}
// add caching to the send method
public <T> HttpResponse<T> send(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) throws IOException, InterruptedException{
// check if request is in cache, if yes the return cached response
if(cache.containsKey(request)){
System.out.println("Cache hit!");
return cache.get(request);
}
// request is not in cache, let's execute it with the delegate
HttpResponse<T> response = delegate.send(request, responseBodyHandler);
//add response to cache
cache.put(request, response);
//return response
return response;
}
}
Testrun
Let’s do two calls to the /users
endpoint of jsonplaceholder.com . The first time, the request is not in the cache and should execute the request. The second time it should use the cached response.
public static void main(String[] args) throws IOException, InterruptedException {
// create a default HttpClient
HttpClient client = HttpClient.newHttpClient();
// create an instance of our cachinghttpclient
CachingHttpClient cachingHttpClient = new CachingHttpClient(client);
//create the request for the users endpoint
HttpRequest request = HttpRequest.newBuilder(URI.create("https://jsonplaceholder.typicode.com/users"))
.GET().build();
System.out.println("doing first call");
cachingHttpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("doing second call");
cachingHttpClient.send(request, HttpResponse.BodyHandlers.ofString());
}
Output of this simple program:
doing first call
doing second call
Cache hit!