blob: 18ecee39ec9f7f9bdb2888fe26e84d4f0a58c87d [file] [log] [blame]
<?xml version="1.0"?>
<!--
Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0, which is available at
http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary
Licenses when the conditions for such availability set forth in the
Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
version 2 with the GNU Classpath Exception, which is available at
https://www.gnu.org/software/classpath/license.html.
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-->
<!DOCTYPE chapter [<!ENTITY % ents SYSTEM "jersey.ent" > %ents; ]>
<chapter xmlns="http://docbook.org/ns/docbook"
version="5.0"
xml:lang="en"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd"
xml:id="filters-and-interceptors">
<title>Filters and Interceptors</title>
<section>
<title>Introduction</title>
<para>This chapter describes filters, interceptors and their configuration. Filters and interceptors
can be used on both sides, on the client and the server side. Filters can modify inbound and outbound requests
and responses including modification of headers, entity and other request/response parameters. Interceptors
are used primarily for modification of entity input and output streams. You can use interceptors for example
to zip and unzip output and input entity streams.
</para>
</section>
<section>
<title>Filters</title>
<para>
Filters can be used when you want to modify any request or response parameters like headers. For example
you would like to add a response header "X-Powered-By" to each generated response. Instead of adding this header
in each resource method you would use a response filter to add this header.
</para>
<para>
There are filters on the server side and the client side.
</para>
<para>
Server filters:
<simplelist>
<member>&jaxrs.container.ContainerRequestFilter;
</member>
<member>&jaxrs.container.ContainerResponseFilter;
</member>
</simplelist>
Client filters:
<simplelist>
<member>&jaxrs.client.ClientRequestFilter;
</member>
<member>&jaxrs.client.ClientResponseFilter;
</member>
</simplelist>
</para>
<section>
<title>Server filters</title>
The following example shows a simple container response filter adding a header to each response.
<para>
<example>
<title>Container response filter</title>
<programlisting language="java" linenumbering="numbered">import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
public class PoweredByResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("X-Powered-By", "Jersey :-)");
}
}</programlisting>
</example>
</para>
<para>
In the example above the <literal>PoweredByResponseFilter</literal>
always adds a header "X-Powered-By" to the
response. The filter must inherit from the &jaxrs.container.ContainerResponseFilter; and must be registered
as a provider. The filter will be executed for every response which is in most cases after the resource method
is executed. Response filters are executed even if the resource method is not run, for example when
the resource method is not found and 404 "Not found" response code is returned by the Jersey runtime. In this case
the filter will be executed and will process the 404 response.
</para>
<para>
The <literal>filter()</literal> method has two arguments, the container request and container response. The
&jaxrs.ContainerRequestContext; is accessible only for read only purposes as the filter is executed already
in response phase. The modifications can be done in the &jaxrs.ContainerResponseContext;.
</para>
<para>
The following example shows the usage of a request filter.
</para>
<example>
<title>Container request filter</title>
<programlisting language="java" linenumbering="numbered">import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
public class AuthorizationRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
final SecurityContext securityContext =
requestContext.getSecurityContext();
if (securityContext == null ||
!securityContext.isUserInRole("privileged")) {
requestContext.abortWith(Response
.status(Response.Status.UNAUTHORIZED)
.entity("User cannot access the resource.")
.build());
}
}
}</programlisting>
</example>
<para>
The request filter is similar to the response filter but does not have access to the ContainerResponseContext
as no response is accessible yet. Response filter inherits from &jaxrs.container.ContainerResponseFilter;.
Request filter is executed before the resource method is run and before the
response is created. The filter has possibility to manipulate the request parameters including request
headers or entity.
</para>
<para>
The <literal>AuthorizationRequestFilter</literal> in the example checks whether the
authenticated user is in the privileged role. If it is not then the request is <emphasis>aborted</emphasis>
by calling <literal>ContainerRequestContext.abortWith(Response response)</literal> method. The method
is intended to be called from the request filter in situation when the request should not be processed further in the standard processing chain.
When the <literal>filter</literal> method is finished the response passed as a parameter to the
<literal>abortWith</literal> method is used to respond to the request. Response filters, if any are registered,
will be executed and will have possibility to process the aborted response.
</para>
<section>
<title>Pre-matching and post-matching filters</title>
</section>
<para>
All the request filters shown above was implemented as post-matching filters. It means that the filters
would be applied only after a suitable resource method has been selected to process the actual request
i.e. after request matching happens. Request matching is the process of finding a resource method that
should be executed based on the request path and other request parameters. Since post-matching request
filters are invoked when a particular resource method has already been selected, such filters can not
influence the resource method matching process.
</para>
<para>
To overcome the above described limitation, there is a possibility to mark
a server request filter as a <emphasis>pre-matching</emphasis> filter,
i.e. to annotate the filter class with the &jaxrs.Prematching; annotation.
Pre-matching filters are request filters that are executed before
the request matching is started. Thanks to this, pre-matching request filters have
the possibility to influence which method will be matched. Such a pre-matching request filter example is shown
here:
</para>
<para>
<example>
<title>Pre-matching request filter</title>
<programlisting language="java" linenumbering="numbered">...
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
...
@PreMatching
public class PreMatchingFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
// change all PUT methods to POST
if (requestContext.getMethod().equals("PUT")) {
requestContext.setMethod("POST");
}
}
}</programlisting>
</example>
</para>
<para>
The <literal>PreMatchingFilter</literal> is a simple pre-matching filter which changes all PUT HTTP
methods to POST. This might be useful when you want to always handle these PUT and POST HTTP methods
with the same Java code. After the <literal>PreMatchingFilter</literal> has been invoked, the rest
of the request processing will behave as if the POST HTTP method was originally used.
You cannot do this in post-matching filters
(standard filters without &lit.jaxrs.Prematching; annotation)
as the resource method is already matched (selected). An attempt to tweak the original HTTP method in
a post-matching filter would cause an <literal>IllegalArgumentException</literal>.
</para>
<para>
As written above, pre-matching filters can fully influence the request matching process, which means
you can even modify request URI in a pre-matching filter by invoking
the <literal>setRequestUri(URI)</literal> method of &lit.jaxrs.container.ContainerRequestFilter;
so that a different resource would be matched.
</para>
<para>
Like in post-matching filters you can abort a response in pre-matching filters too.
</para>
</section>
<section>
<title>Client filters</title>
<para>
Client filters are similar to container filters. The response can also be aborted
in the &jaxrs.client.ClientRequestFilter; which would cause that no request will actually be sent to the server at all.
A new response is passed to the <literal>abort</literal> method. This response will be used and delivered
as a result of the request invocation. Such a response goes through the client response filters.
This is similar to what happens on the server side. The process is shown in the following example:
</para>
<para>
<example>
<title>Client request filter</title>
<programlisting language="java" linenumbering="numbered">public class CheckRequestFilter implements ClientRequestFilter {
@Override
public void filter(ClientRequestContext requestContext)
throws IOException {
if (requestContext.getHeaders(
).get("Client-Name") == null) {
requestContext.abortWith(
Response.status(Response.Status.BAD_REQUEST)
.entity("Client-Name header must be defined.")
.build());
}
}
}</programlisting>
</example>
</para>
<para>
The <literal>CheckRequestFilter</literal> validates the outgoing request. It is checked for presence of
a <literal>Client-Name</literal> header. If the header is not present the request will be aborted
with a made up response with an appropriate code and message in the entity body. This will cause that
the original request will not be effectively sent to the server but the actual invocation
will still end up with a response as if it would be generated by the
server side. If there would be any client response filter it would be executed on this response.
</para>
<para>
To summarize the workflow, for any client request invoked from the client API
the client request filters (&jaxrs.client.ClientRequestFilter;)
are executed that could manipulate the request.
If not aborted, the outcoming request is then physically sent over to the server side
and once a response is received back from the server the client response
filters (&jaxrs.client.ClientResponseFilter;)
are executed that might again manipulate the returned response.
Finally the response is passed back to the code that invoked the request.
If the request was aborted in any client request filter then the
client/server communication is skipped and the aborted response
is used in the response filters.
</para>
</section>
</section>
<section>
<title>Interceptors</title>
<para>
Interceptors share a common API for the server and the client side. Whereas filters are primarily intended to manipulate
request and response parameters like HTTP headers, URIs and/or HTTP methods, interceptors are intended to manipulate entities, via manipulating
entity input/output streams. If you for example need to encode entity body of a client request then you could
implement an interceptor to do the work for you.
</para>
<para>
There are two kinds of interceptors, &jaxrs.ReaderInterceptor; and &jaxrs.WriterInterceptor;.
Reader interceptors are used to manipulate inbound entity streams. These are the streams coming from
the "wire". So, using a reader interceptor you
can manipulate request entity stream on the server side (where
an entity is read from the client request) and response entity stream on the client side (where an entity
is read from the server response). Writer interceptors are used for cases where entity is written to the
"wire" which on the server means when writing out a response entity and on the client side when writing
request entity for a request to be sent out to the server. Writer and reader interceptors are executed before message body
readers or writers are executed and their primary intention is to wrap the entity streams that will be used in message body
reader and writers.
</para>
<para>
The following example shows a writer interceptor that enables GZIP compression of the whole entity body.
</para>
<para>
<example>
<title>GZIP writer interceptor</title>
<programlisting language="java" linenumbering="numbered">public class GZIPWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new GZIPOutputStream(outputStream));
context.proceed();
}
}</programlisting>
</example>
</para>
<para>
The interceptor gets a output stream from the &jaxrs.WriterInterceptorContext; and sets
a new one which is a GZIP wrapper of the original output stream. After all interceptors are executed the
output stream lastly set to the &lit.jaxrs.WriterInterceptorContext; will be used for serialization of the entity. In the
example above the entity bytes will be written to the GZIPOutputStream which will compress the stream data
and write them to the original output stream. The original stream is always the stream which writes the data to
the "wire". When the interceptor is used on the server, the original output stream is the stream into which writes
data to the underlying server container stream that sends the response to the client.
</para>
<para>
The interceptors wrap the streams and they itself work as wrappers. This means that each interceptor is a wrapper
of another interceptor and it is responsibility of each interceptor implementation to call the wrapped interceptor.
This is achieved by calling the <literal>proceed()</literal> method on the &lit.jaxrs.WriterInterceptorContext;.
This method will call the next registered interceptor in the chain, so effectivelly this will call all remaining registered interceptors. Calling <literal>proceed()</literal> from the last
interceptor in the chain will call the appropriate message body reader. Therefore every interceptor must call the
<literal>proceed()</literal> method otherwise the entity would not be written. The wrapping principle is reflected
also in the method name, aroundWriteTo, which says that the method is wrapping the writing of the entity.
</para>
<para>
The method aroundWriteTo() gets &lit.jaxrs.WriterInterceptorContext; as a parameter. This context contains getters
and setters for header parameters, request properties, entity, entity stream and other properties. These are the
properties which will be passed to the final &lit.jaxrs.ext.MessageBodyWriter;. Interceptors are allowed to modify
all these properties. This could influence writing of an entity by &lit.jaxrs.ext.MessageBodyWriter; and even
selection of such a writer. By changing media type (&lit.jaxrs.WriterInterceptorContext;.setMediaType())
the interceptor can cause that different message body writer will be chosen. The interceptor can also
completely replace the entity if it is needed. However, for modification of headers, request
properties and such, the filters are usually more preferable choice. Interceptors are executed
only when there is any entity and when the entity is to be written. So, when you always want to add a new
header to a response no matter what, use filters as interceptors might not be executed when no entity is
present. Interceptors should modify properties only for entity serialization
and deserialization purposes.
</para>
<para>
Let's now look at an example of a &lit.jaxrs.ReaderInterceptor;
</para>
<para>
<example>
<title>GZIP reader interceptor</title>
<programlisting language="java" linenumbering="numbered">public class GZIPReaderInterceptor implements ReaderInterceptor {
@Override
public Object aroundReadFrom(ReaderInterceptorContext context)
throws IOException, WebApplicationException {
final InputStream originalInputStream = context.getInputStream();
context.setInputStream(new GZIPInputStream(originalInputStream));
return context.proceed();
}
}</programlisting>
</example>
</para>
<para>
The <literal>GZIPReaderInterceptor</literal> wraps the original input stream with the
<literal>GZIPInputStream</literal>. All further reads from the entity stream will cause that data will be decompressed
by this stream. The interceptor method <literal>aroundReadFrom()</literal> must return an entity. The entity
is returned from the <literal>proceed</literal> method of the &jaxrs.ReaderInterceptorContext;. The
<literal>proceed</literal> method internally calls the wrapped interceptor which must also return an entity.
The <literal>proceed</literal> method invoked from the last interceptor in the chain calls message body reader which deserializes
the entity end returns it. Every interceptor can change this entity if there is a need but in the most cases
interceptors will just return the entity as returned from the <literal>proceed</literal> method.
</para>
<para>
As already mentioned above, interceptors should be primarily used to manipulate entity body.
Similar to methods exposed by &lit.jaxrs.WriterInterceptorContext; the &lit.jaxrs.ReaderInterceptorContext;
introduces a set of methods for modification of request/response properties like HTTP headers,
URIs and/or HTTP methods (excluding getters and setters for entity as entity has not been read yet).
Again the same rules as for &lit.jaxrs.WriterInterceptor; applies for changing these properties (change only
properties in order to influence reading of an entity).
</para>
</section>
<section>
<title>Filter and interceptor execution order</title>
<para>
Let's look closer at the context of execution of filters and interceptors. The following steps describes scenario
where a JAX-RS client makes a POST request to the server. The server receives an entity and sends a response back
with the same entity. GZIP reader and writer interceptors are registered on the
client and the server. Also filters are registered on client and server which change the headers of request
and response.
<orderedlist>
<listitem>Client request invoked: The POST request with attached entity is built on the client and invoked.</listitem>
<listitem>ClientRequestFilters: client request filters are executed on the client and they
manipulate the request headers.</listitem>
<listitem>Client &lit.jaxrs.WriterInterceptor;: As the request contains an entity, writer interceptor registered on the client is executed before
a MessageBodyWriter is executed. It wraps the entity output stream with the GZipOutputStream.</listitem>
<listitem>Client MessageBodyWriter: message body writer is executed on the client which serializes the entity
into the new GZipOutput stream. This stream zips the data and sends it to the "wire".</listitem>
<listitem>Server: server receives a request. Data of entity is compressed which means that pure read from
the entity input stream would return compressed data.</listitem>
<listitem>Server pre-matching ContainerRequestFilters: ContainerRequestFilters are executed that can manipulate
resource method matching process.</listitem>
<listitem>Server: matching: resource method matching is done.</listitem>
<listitem>Server: post-matching ContainerRequestFilters: ContainerRequestFilters post matching filters are executed.
This include execution of all global filters (without name binding) and filters name-bound to the matched
method.</listitem>
<listitem>Server &lit.jaxrs.ReaderInterceptor;: reader interceptors are executed on the server. The GZIPReaderInterceptor
wraps the input stream (the stream from the "wire") into the GZipInputStream and set it to context.</listitem>
<listitem>Server MessageBodyReader: server message body reader is executed and it deserializes the entity
from new GZipInputStream (get from the context). This means the reader will read unzipped data and not
the compressed data from the "wire".</listitem>
<listitem>Server resource method is executed: the deserialized entity object is passed to the matched resource
method as a parameter. The method returns this entity as a response entity.</listitem>
<listitem>Server ContainerResponseFilters are executed: response filters are executed on the server and
they manipulate the response headers. This include all global bound filters (without name binding) and all filters name-bound to the resource method.</listitem>
<listitem>Server &lit.jaxrs.WriterInterceptor;: is executed on the server. It wraps the original
output stream with a new GZIPOuptutStream. The original stream is the stream that "goes to the wire" (output
stream for response from the underlying server container).
</listitem>
<listitem>Server MessageBodyWriter: message body writer is executed on the server which serializes the entity
into the GZIPOutputStream. This stream compresses the data and writes it to the original stream which sends
this compressed data back to the client.
</listitem>
<listitem>Client receives the response: the response contains compressed entity data.</listitem>
<listitem>Client ClientResponseFilters: client response filters are executed and they manipulate the response headers.</listitem>
<listitem>Client response is returned: the javax.ws.rs.core.Response is returned from the request invocation.</listitem>
<listitem>Client code calls response.readEntity(): read entity is executed on the client to extract the entity from the response.</listitem>
<listitem>Client &lit.jaxrs.ReaderInterceptor;: the client reader interceptor is executed when readEntity(Class) is called. The interceptor
wraps the entity input stream with GZIPInputStream. This will decompress the data from the original input stream.
</listitem>
<listitem>Client MessageBodyReaders: client message body reader is invoked which reads decompressed data from
GZIPInputStream and deserializes the entity.</listitem>
<listitem>Client: The entity is returned from the readEntity().</listitem>
</orderedlist>
It is worth to mention that in the scenario above the reader and writer interceptors are invoked only if the
entity is present (it does not make sense to wrap entity stream when no entity will be written). The same behaviour
is there for message body readers and writers. As mentioned above, interceptors are executed before
the message body reader/writer as a part of their execution and they can wrap the input/output stream
before the entity is read/written. There are exceptions when interceptors are not run before message body
reader/writers but this is not the case of simple scenario above. This happens for example when the entity is
read many times from client response using internal buffering. Then the data are intercepted only once and kept
'decoded' in the buffer.
</para>
</section>
<section>
<title>Name binding</title>
<para>
Filters and interceptors can be <emphasis>name-bound</emphasis>. Name binding is a concept that allows to say to a JAX-RS
runtime that a specific filter or interceptor will be executed only for a specific resource method. When a filter or
an interceptor is limited only to a specific resource method we say that it is <emphasis>name-bound</emphasis>.
Filters and interceptors that do not have such a limitation are called <emphasis>global</emphasis>.
</para>
<para>
Filter or interceptor can be assigned to a resource method using the &jaxrs.NameBinding; annotation. The annotation
is used as meta annotation for other user implemented annotations that are applied to a providers and resource
methods. See the following example:
</para>
<para>
<example>
<title>&lit.jaxrs.NameBinding; example</title>
<programlisting language="java" linenumbering="numbered">...
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.zip.GZIPInputStream;
import javax.ws.rs.GET;
import javax.ws.rs.NameBinding;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
...
// @Compress annotation is the name binding annotation
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}
@Path("helloworld")
public class HelloWorldResource {
@GET
@Produces("text/plain")
public String getHello() {
return "Hello World!";
}
@GET
@Path("too-much-data")
@Compress
public String getVeryLongString() {
String str = ... // very long string
return str;
}
}
// interceptor will be executed only when resource methods
// annotated with @Compress annotation will be executed
@Compress
public class GZIPWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
context.setOutputStream(new GZIPOutputStream(outputStream));
context.proceed();
}
}</programlisting>
</example>
</para>
<para>
The example above defines a new <literal>@Compress</literal> annotation which is a name binding annotation as
it is annotated with &lit.jaxrs.NameBinding;. The <literal>@Compress</literal> is applied on the
resource method <literal>getVeryLongString()</literal> and on the interceptor
<literal>GZIPWriterInterceptor</literal>. The interceptor will be executed only if any resource method
with such an annotation will be executed. In our example case the interceptor will be executed only for
the <literal>getVeryLongString()</literal> method. The interceptor will not be executed for method
<literal>getHello()</literal>. In this example the reason is probably clear. We would like to compress
only long data and we do not need to compress the short response of "Hello World!".
</para>
<para>
Name binding can be applied on a resource class. In the example <literal>HelloWorldResource</literal>
would be annotated with <literal>@Compress</literal>. This would mean that all resource
methods will use compression in this case.
</para>
<para>
There might be many name binding annotations defined in an application. When any provider (filter
or interceptor) is annotated with more than one name binding annotation, then it will be executed for
resource methods which contain ALL these annotations. So, for example if our interceptor would be
annotated with another name binding annotation @GZIP then the resource method would need to have both annotations attached,
@Compress and @GZIP, otherwise the interceptor would not be executed. Based on the previous paragraph we can
even use the combination when the
resource method <literal>getVeryLongString()</literal> would be annotated with @Compress and resource class
<literal>HelloWorldResource</literal> would be annotated from with @GZIP. This would also trigger the interceptor as
annotations of resource methods are aggregated from resource method and from resource class. But this is probably
just an edge case which will not be used so often.
</para>
<para>
Note that <emphasis>global filters are always executed</emphasis>, even for resource methods
which have any name binding annotations.
</para>
</section>
<section>
<title>Dynamic binding</title>
<para>Dynamic binding is a way how to assign filters and interceptors to the resource methods in a dynamic
manner. Name binding from the previous chapter uses a static approach and changes to binding require source
code change and recompilation. With dynamic binding you can implement code which defines bindings during the application
initialization time. The following example shows how to implement dynamic binding.</para>
<para>
<example>
<title>Dynamic binding example</title>
<programlisting language="java" linenumbering="numbered">...
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.container.DynamicFeature;
...
@Path("helloworld")
public class HelloWorldResource {
@GET
@Produces("text/plain")
public String getHello() {
return "Hello World!";
}
@GET
@Path("too-much-data")
public String getVeryLongString() {
String str = ... // very long string
return str;
}
}
// This dynamic binding provider registers GZIPWriterInterceptor
// only for HelloWorldResource and methods that contain
// "VeryLongString" in their name. It will be executed during
// application initialization phase.
public class CompressionDynamicBinding implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if (HelloWorldResource.class.equals(resourceInfo.getResourceClass())
&amp;&amp; resourceInfo.getResourceMethod()
.getName().contains("VeryLongString")) {
context.register(GZIPWriterInterceptor.class);
}
}
}</programlisting>
</example>
</para>
<para>
The example contains one <literal>HelloWorldResource</literal> which is known from the previous name binding example.
The difference is in the <literal>getVeryLongString</literal> method, which now does not define
the <literal>@Compress</literal> name binding annotations. The binding is done
using the provider which implements &jaxrs.container.DynamicFeature; interface. The interface defines
one <literal>configure</literal>
method with two arguments, <literal>ResourceInfo</literal> and <literal>FeatureContext</literal>.
<literal>ResourceInfo</literal> contains information about the resource and method to which the binding can be done.
The <literal>configure</literal> method will be executed once for each resource method that is defined in the application.
In the example above the provider will be executed twice, once for the <literal>getHello()</literal> method
and once for <literal>getVeryLongString()</literal> (
once the resourceInfo will contain information about getHello() method and once it will point to
getVeryLongString()). If a dynamic binding provider wants to register any provider for the actual resource method
it will do that using provided <literal>FeatureContext</literal> which extends
JAX-RS <literal>Configurable</literal> API. All methods for registration of filter or interceptor classes or instances can be used.
Such dynamically registered filters or interceptors will be bound only to the actual resource method. In the example above the
<literal>GZIPWriterInterceptor</literal> will be bound only to the method <literal>getVeryLongString()</literal>
which will cause that data will be compressed only for this method and not for the method
<literal>getHello()</literal>. The code of <literal>GZIPWriterInterceptor</literal> is in the examples above.
</para>
<para>
Note that filters and interceptors registered using dynamic binding are only additional filters run for the
resource method. If there are any name bound providers or global providers they will still be executed.
</para>
</section>
<section>
<title>Priorities</title>
<para>In case you register more filters and interceptors you might want to define an exact order in which
they should be invoked. The order can be controlled by the <literal>@Priority</literal> annotation defined
by the <literal>javax.annotation.Priority</literal> class. The annotation accepts an integer parameter of priority.
Providers used in request processing (&lit.jaxrs.container.ContainerRequestFilter;,
&lit.jaxrs.client.ClientRequestFilter;) as well as entity interceptors (&lit.jaxrs.ReaderInterceptor;,
&lit.jaxrs.WriterInterceptor;) are sorted based on the priority in an ascending manner. So, a request filter with
priority defined with <literal>@Priority(1000)</literal>
will be executed before another request filter with priority defined as <literal>@Priority(2000)</literal>. Providers
used during response processing (&lit.jaxrs.container.ContainerResponseFilter;,
&lit.jaxrs.client.ClientResponseFilter;) are executed
in the reverse order (using descending manner), so a provider with the priority defined with
<literal>@Priority(2000)</literal> will be executed before another provider with
priority defined with <literal>@Priority(1000)</literal>.
</para>
<para>
It's a good practice to assign a priority to filters and interceptors. Use &jaxrs.Priorities; class which
defines standardized priorities in JAX-RS for different usages, rather than inventing your
own priorities. For example, when you write an authentication filter you would assign a priority 1000 which
is the value of &lit.jaxrs.Priorities;<literal>.AUTHENTICATION</literal>. The following example
shows the filter from the beginning
of this chapter with a priority assigned.
</para>
<para>
<example>
<title>Priorities example</title>
<programlisting language="java" linenumbering="numbered">...
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
...
@Priority(Priorities.HEADER_DECORATOR)
public class ResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("X-Powered-By", "Jersey :-)");
}
}</programlisting>
</example>
</para>
<para>
As this is a response filter and response filters are executed in the reverse order,
any other filter with priority lower than 3000 (<literal>Priorities.HEADER_DECORATOR</literal> is 3000) will be executed after
this filter. So, for example <literal>AUTHENTICATION</literal> filter (priority 1000) would be run after this filter.
</para>
</section>
</chapter>