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>