Add new interfaces (PreInvocationInterceptor, PostInvocationInterceptor, and InvocationBuilderListener) documentation (#4411) * Add new PreInvocationInterceptor, PostInvocationInterceptor, and InvocationBuilderListener interfaces documentation Signed-off-by: tvallin <thibault.vallin@oracle.com>
diff --git a/docs/src/main/docbook/client.xml b/docs/src/main/docbook/client.xml index 90964a3..e0b01cb 100644 --- a/docs/src/main/docbook/client.xml +++ b/docs/src/main/docbook/client.xml
@@ -999,4 +999,135 @@ </section> + + <section> + <title>InvocationInterceptors</title> + <para> + Suppose a case that the start of the request is to be logged and even measured. + This can be done by <literal>ClientRequestFilter</literal>, 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 + <literal>#abortWith</literal>! + </para> + <section> + <title>PreInvocationInterceptor</title> + <para> + For this, <literal>PreInvocationInterceptor</literal>, the code that executes before the <literal>ClientRequestFilters</literal> + 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 <literal>#beforeRequest</literal> method, which corresponds to <literal>ClientRequestFilter</literal>: + </para> + <programlisting language="java"> + /** + * The method invoked before the request starts. + * @param requestContext the request context shared with + * ClientRequestFilter. + */ + void beforeRequest(ClientRequestContext requestContext); + </programlisting> + <para> + Note that only a single <literal>#abortWith</literal> is allowed in all <literal>PreInvocationInterceptors</literal>, + otherwise an <literal>IllegalStateException</literal> is thrown. + All the exceptions accumulated in <literal>PreInvocationInterceptors</literal> are thrown in a single Exception, + available through <literal>#getSuppressed()</literal>. + </para> + </section> + <section> + <title>PostInvocationInterceptor</title> + <para> + Similarly, <literal>ClientResponseFilter</literal> seems to be a good place where the total time of the HTTP request can be measured, + but similarly to <literal>ClientRequestFilter</literal>, the response filter may not be invoked at all. + For this, <literal>PostInvocationInterceptor</literal> has been introduced. Jersey runtime ensures that every + <literal>PostInvocationInterceptor</literal> is called. Since an exception can occur during the HTTP request, + <literal>PostInvocationInterceptor</literal> comes with two methods: + </para> + <programlisting language="java"> + /** + * The method is invoked after a request when no + * is thrown, or the Throwables are resolved + * by previous PostInvocationInterceptor. + * + * @param requestContext the request context. + * @param responseContext the response context + * of the original Response or response context + * defined by the new resolving Response. + */ + void afterRequest(ClientRequestContext requestContext, ClientResponseContext responseContext); + + /** + * The method is invoked after a Throwable is caught + * during the client request chain processing. + * + * @param requestContext the request context. + * @param exceptionContext the context available to handle the + * caught Throwables. + */ + void onException(ClientRequestContext requestContext, ExceptionContext exceptionContext); + </programlisting> + <para> + The <literal>#afterRequest</literal> method is executed when no exception has been thrown during the HTTP request, + <literal>#onException</literal> method is executed if the exception has been thrown during the request. + It is possible to set a response in <literal>#onException</literal>, and the consecutive <literal>PostInvocationInterceptor</literal> will + execute its <literal>#afterRequest</literal> method. + + The measuring example can looks as follows, then: + </para> + <programlisting language="java" linenumbering="numbered"> + String response = ClientBuilder.newClient().target("path") + .register(new PreInvocationInterceptor() { + @Override + public void beforeRequest(ClientRequestContext requestContext) { + startTime = System.currentTimeMillis(); + } + }) + .register(new PostInvocationInterceptor() { + @Override + public void afterRequest(ClientRequestContext requestContext, ClientResponseContext responseContext) { + logDuration(System.currentTimeMillis() - startTime); + } + @Override + public void onException(ClientRequestContext requestContext, ExceptionContext exceptionContext) { + logDuration(System.currentTimeMillis() - startTime); + } + }) + .request().get().readEntity(String.class); + </programlisting> + </section> + </section> + <section> + <title>InvocationBuilderListener</title> + <para> + InvocationBuilderListener is an interface that is inspired by Microprofile REST Client <literal>RestClientBuilderListener</literal> + and it contains a single method: + </para> + <programlisting language="java"> + /** + * Whenever an Invocation.Builder is created, (i.e. when + * WebTarget#request() is called, this method would be invoked. + * + * @param context the updated InvocationBuilderContext. + */ + void onNewBuilder(InvocationBuilderContext context); + </programlisting> + <para> + <literal>InvocationBuilderContext</literal> a subset of methods of the <literal>Invocation.Builder</literal>. It can be used to call the default + values of the <literal>Invocation.Builder</literal>. Since it is invoked at the time <literal>Invocation.Builder</literal> is instantiated, any consequent + calls of the <literal>Invocation.Builder</literal>‘s methods will replace the defaults set by the <literal>InvocationBuilderListener</literal>. + + 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: + </para> + <programlisting language="java" linenumbering="numbered"> + public static class MyFeature implements Feature { + @Override + public boolean configure(FeatureContext context) { + context.register( + (InvocationBuilderListener)(l)-> + l.getHeaders().add("MY_HEADER", "MY_VALUE") + ); + return true; + } + } + </programlisting> + </section> </chapter>