Jersey 2.30 comes with multiple new features, and this post describes three new interfaces on the client. They are PreInvocationInterceptor, PostInvocationInterceptor, and InvocationBuilderListener.
Suppose a case that the start of the request is to be logged and even measured. This can be done by ClientRequestFilter, which is usually invoked before the request is wired on the network. However, the filter may be called as a last of the filters in the chain. Sure, it can have the highest priority, but the other filters can have the very same priority! Some long-running operations can be performed before the measuring can actually start. Even worse, the filter may even be skipped from the chain by the previous #abortWith!
PreInvocationInterceptor
For this, PreInvocationInterceptor, the code that executes before the ClientRequestFilters are invoked, has been added to the client request chain. Jersey ensures all the interceptors are invoked with each request. The interceptor contains a single #beforeRequest method, which corresponds to ClientRequestFilter:
<span class="line"><span style="color: #93A1A1; font-style: italic"> /**</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * The method invoked before the request starts.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * </span><span style="color: #859900; font-style: italic">@param</span><span style="color: #93A1A1; font-style: italic"> requestContext the request context shared with </span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * ClientRequestFilter.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> */</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">void</span><span style="color: #657B83"> </span><span style="color: #268BD2">beforeRequest</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">ClientRequestContext</span><span style="color: #657B83"> requestContext);</span></span>
Note that only a single #abortWith is allowed in all PreInvocationInterceptors, otherwise an IllegalStateException is thrown. All the exceptions accumulated in PreInvocationInterceptors are thrown in a single Exception, available through #getSuppressed().
PostInvocationInterceptor
Similarly, ClientResponseFilter seems to be a good place where the total time of the HTTP request can be measured, but similarly to ClientRequestFilter, the response filter may not be invoked at all. For this, PostInvocationInterceptor has been introduced. Jersey runtime ensures that every PostInvocationInterceptor is called. Since an exception can occur during the HTTP request, PostInvocationInterceptor comes with two methods:
<span class="line"><span style="color: #93A1A1; font-style: italic"> /**</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * The method is invoked after a request when no </span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * is thrown, or the Throwables are resolved </span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * by previous PostInvocationInterceptor.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> *</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * </span><span style="color: #859900; font-style: italic">@param</span><span style="color: #93A1A1; font-style: italic"> requestContext the request context.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * </span><span style="color: #859900; font-style: italic">@param</span><span style="color: #93A1A1; font-style: italic"> responseContext the response context </span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * of the original Response or response context</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * defined by the new resolving Response.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> */</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">void</span><span style="color: #657B83"> </span><span style="color: #268BD2">afterRequest</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">ClientRequestContext</span><span style="color: #657B83"> requestContext, </span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">ClientResponseContext</span><span style="color: #657B83"> responseContext);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> /**</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * The method is invoked after a Throwable is caught </span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * during the client request chain processing.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> *</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * </span><span style="color: #859900; font-style: italic">@param</span><span style="color: #93A1A1; font-style: italic"> requestContext the request context.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * </span><span style="color: #859900; font-style: italic">@param</span><span style="color: #93A1A1; font-style: italic"> exceptionContext the context available to handle the </span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * caught Throwables.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> */</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">void</span><span style="color: #657B83"> </span><span style="color: #268BD2">onException</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">ClientRequestContext</span><span style="color: #657B83"> requestContext, </span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">ExceptionContext</span><span style="color: #657B83"> exceptionContext);</span></span>
The #afterRequest method is executed when no exception has been thrown during the HTTP request, #onException method is executed if the exception has been thrown during the request. It is possible to set a response in #onException, and the consecutive PostInvocationInterceptor will execute its #afterRequest method.
The measuring example can look as follows, then:
<span class="line"><span style="color: #586E75; font-weight: bold">String</span><span style="color: #657B83"> </span><span style="color: #268BD2">response</span><span style="color: #657B83"> </span><span style="color: #859900">=</span><span style="color: #657B83"> </span><span style="color: #268BD2">ClientBuilder</span><span style="color: #657B83">.</span><span style="color: #268BD2">newClient</span><span style="color: #657B83">().</span><span style="color: #268BD2">target</span><span style="color: #657B83">(</span><span style="color: #2AA198">"path"</span><span style="color: #657B83">)</span></span>
<span class="line"><span style="color: #657B83"> .</span><span style="color: #268BD2">register</span><span style="color: #657B83">(</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #859900">new</span><span style="color: #657B83"> </span><span style="color: #268BD2">PreInvocationInterceptor</span><span style="color: #657B83">() {</span></span>
<span class="line"><span style="color: #657B83"> @</span><span style="color: #586E75; font-weight: bold">Override</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">public</span><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">void</span><span style="color: #657B83"> </span><span style="color: #268BD2">beforeRequest</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">ClientRequestContext</span><span style="color: #657B83"> requestContext) {</span></span>
<span class="line"><span style="color: #657B83"> startTime </span><span style="color: #859900">=</span><span style="color: #657B83"> </span><span style="color: #268BD2">System</span><span style="color: #657B83">.</span><span style="color: #268BD2">currentTimeMillis</span><span style="color: #657B83">();</span></span>
<span class="line"><span style="color: #657B83"> } </span></span>
<span class="line"><span style="color: #657B83"> })</span></span>
<span class="line"><span style="color: #657B83"> .</span><span style="color: #268BD2">register</span><span style="color: #657B83">(</span><span style="color: #859900">new</span><span style="color: #657B83"> </span><span style="color: #268BD2">PostInvocationInterceptor</span><span style="color: #657B83">() {</span></span>
<span class="line"><span style="color: #657B83"> @</span><span style="color: #586E75; font-weight: bold">Override</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">public</span><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">void</span><span style="color: #657B83"> </span><span style="color: #268BD2">afterRequest</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">ClientRequestContext</span><span style="color: #657B83"> requestContext, </span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">ClientResponseContext</span><span style="color: #657B83"> responseContext) {</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #268BD2">logDuration</span><span style="color: #657B83">(</span><span style="color: #268BD2">System</span><span style="color: #657B83">.</span><span style="color: #268BD2">currentTimeMillis</span><span style="color: #657B83">() </span><span style="color: #859900">-</span><span style="color: #657B83"> startTime);</span></span>
<span class="line"><span style="color: #657B83"> }</span></span>
<span class="line"><span style="color: #657B83"> @</span><span style="color: #586E75; font-weight: bold">Override</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">public</span><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">void</span><span style="color: #657B83"> </span><span style="color: #268BD2">onException</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">ClientRequestContext</span><span style="color: #657B83"> requestContext, </span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">ExceptionContext</span><span style="color: #657B83"> exceptionContext) {</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #268BD2">logDuration</span><span style="color: #657B83">(</span><span style="color: #268BD2">System</span><span style="color: #657B83">.</span><span style="color: #268BD2">currentTimeMillis</span><span style="color: #657B83">() </span><span style="color: #859900">-</span><span style="color: #657B83"> startTime);</span></span>
<span class="line"><span style="color: #657B83"> }</span></span>
<span class="line"><span style="color: #657B83"> })</span></span>
<span class="line"><span style="color: #657B83"> .</span><span style="color: #268BD2">request</span><span style="color: #657B83">().</span><span style="color: #268BD2">get</span><span style="color: #657B83">().</span><span style="color: #268BD2">readEntity</span><span style="color: #657B83">(</span><span style="color: #268BD2">String</span><span style="color: #657B83">.</span><span style="color: #268BD2">class</span><span style="color: #657B83">);</span></span>
InvocationBuilderListener
InvocationBuilderListener is an interface that is inspired by Microprofile REST Client RestClientBuilderListener and it contains a single method:
<span class="line"><span style="color: #93A1A1; font-style: italic"> /**</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * Whenever an Invocation.Builder is created, (i.e. when</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * WebTarget#request() is called, this method would be invoked.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> *</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> * </span><span style="color: #859900; font-style: italic">@param</span><span style="color: #93A1A1; font-style: italic"> context the updated InvocationBuilderContext.</span></span>
<span class="line"><span style="color: #93A1A1; font-style: italic"> */</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">void</span><span style="color: #657B83"> </span><span style="color: #268BD2">onNewBuilder</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">InvocationBuilderContext</span><span style="color: #657B83"> context);</span></span>
InvocationBuilderContext a subset of methods of the Invocation.Builder. It can be used to call the default values of the Invocation.Builder. Since it is invoked at the time Invocation.Builder is instantiated, any consequent calls of the Invocation.Builder‘s methods will replace the defaults set by the InvocationBuilderListener.
For instance, if all the HTTP requests should contain a custom HTTP header, there can be created a feature that would be registered on the client:
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">public</span><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">static</span><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">class</span><span style="color: #657B83"> </span><span style="color: #CB4B16">MyFeature</span><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">implements</span><span style="color: #657B83"> </span><span style="color: #6C71C4">Feature</span><span style="color: #657B83"> {</span></span>
<span class="line"><span style="color: #657B83"> @</span><span style="color: #586E75; font-weight: bold">Override</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">public</span><span style="color: #657B83"> </span><span style="color: #586E75; font-weight: bold">boolean</span><span style="color: #657B83"> </span><span style="color: #268BD2">configure</span><span style="color: #657B83">(</span><span style="color: #586E75; font-weight: bold">FeatureContext</span><span style="color: #657B83"> context) {</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #268BD2">context</span><span style="color: #657B83">.</span><span style="color: #268BD2">register</span><span style="color: #657B83">(</span></span>
<span class="line"><span style="color: #657B83"> (InvocationBuilderListener)(l)</span><span style="color: #586E75; font-weight: bold">-></span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #268BD2">l</span><span style="color: #657B83">.</span><span style="color: #268BD2">getHeaders</span><span style="color: #657B83">().</span><span style="color: #268BD2">add</span><span style="color: #657B83">(</span><span style="color: #2AA198">"MY_HEADER"</span><span style="color: #657B83">, </span><span style="color: #2AA198">"MY_VALUE"</span><span style="color: #657B83">));</span></span>
<span class="line"><span style="color: #657B83"> </span><span style="color: #859900">return</span><span style="color: #657B83"> </span><span style="color: #B58900">true</span><span style="color: #657B83">;</span></span>
<span class="line"><span style="color: #657B83"> }</span></span>
<span class="line"><span style="color: #657B83"> }</span></span>
And More!
Jersey 2.30 comes with more new features on the client, most notably a possible hanging when using the Apache Client Connector has been fixed and the Microprofile Rest Client implementation has been allowed to use connectors.