Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

15.8. RESTEasy Interceptors

15.8.1. Intercept JAX-RS Invocations

Summary

RESTEasy can intercept JAX-RS invocations and route them through listener-like objects called interceptors. This topic covers descriptions of the four types of interceptors.

Example 15.3. MessageBodyReader/Writer Interceptors

MessageBodyReaderInterceptors and MessageBodyWriterInterceptors can be used on the either the server or client side. They are annotated with @Provider, as well as either @ServerInterceptor or @ClientInterceptor so that RESTEasy knows whether or not to add them to the interceptor list.
These interceptors wrap around the invocation of MessageBodyReader.readFrom() or MessageBodyWriter.writeTo(). They can be used to wrap the Output or Input streams.
RESTEasy GZIP support has interceptors that create and override the default Output and Input streams with a GzipOutputStream or GzipInputStream so that gzip encoding can work. They can also be used to append headers to the response, or the outgoing request on the client side.
public interface MessageBodyReaderInterceptor
  {
     Object read(MessageBodyReaderContext context) throws IOException, WebApplicationException;

  }

public interface MessageBodyWriterInterceptor
  {
     void write(MessageBodyWriterContext context) throws IOException, WebApplicationException;

  }
The interceptors and the MessageBodyReader or Writer is invoked in one big Java call stack. MessageBodyReaderContext.proceed() or MessageBodyWriterContext.proceed() is called in order to go to the next interceptor or, if there are no more interceptors to invoke, the readFrom() or writeTo() method of the MessageBodyReader or MessageBodyWriter. This wrapping allows objects to be modified before they get to the Reader or Writer, and then cleaned up after proceed() returns.
The example below is a server side interceptor, that adds a header value to the response.
@Provider
@ServerInterceptor
public class MyHeaderDecorator implements MessageBodyWriterInterceptor {

    public void write(MessageBodyWriterContext context) throws IOException, WebApplicationException
    {
       context.getHeaders().add("My-Header", "custom");
       context.proceed();
    }
}

Example 15.4. PreProcessInterceptor

PreProcessInterceptors run after a JAX-RS resource method is found to invoke on, but before the actual invocation happens. They are annotated with @ServerInterceptor, and run in sequence.
These interfaces are only usable on the server. They can be used to implement security features, or to handle the Java request. The RESTEasy security implementation uses this type of interceptor to abort requests before they occur if the user does not pass authorization. The RESTEasy caching framework also uses this to return cached responses to avoid invoking methods again.
public interface PreProcessInterceptor
    {
       ServerResponse preProcess(HttpRequest request, ResourceMethod method) throws Failure, WebApplicationException;
    }
If the preProcess() method returns a ServerResponse then the underlying JAX-RS method will not get invoked, and the runtime will process the response and return to the client. If the preProcess() method does not return a ServerResponse, the underlying JAX-RS method will be invoked.

Example 15.5. PostProcessInterceptors

PostProcessInterceptors run after the JAX-RS method was invoked, but before MessageBodyWriters are invoked. They are used if a response header needs to be set when a MessageBodyWriter may not be invoked.
They can only be used on the server side. They do not wrap anything, and are invoked in sequence.
public interface PostProcessInterceptor
  {
    void postProcess(ServerResponse response);
  }

Example 15.6. ClientExecutionInterceptors

ClientExecutionInterceptors are only usable on the client side. They wrap around the HTTP invocation that goes to the server. They must be annotated with @ClientInterceptor and @Provider. These interceptors run after the MessageBodyWriter, and after the ClientRequest has been built on the client side.
RESTEasy GZIP support uses ClientExecutionInterceptors to set the Accept header to contain "gzip, deflate" before the request goes out. The RESTEasy client cache uses it to check to see if its cache contains the resource before going over the wire.
public interface ClientExecutionInterceptor
{
  ClientResponse execute(ClientExecutionContext ctx) throws Exception;
}

public interface ClientExecutionContext
{
  ClientRequest getRequest();

  ClientResponse proceed() throws Exception;
}

15.8.2. Bind an Interceptor to a JAX-RS Method

Summary

All registered interceptors are invoked for every request by default. The AcceptedByMethod interface can be implemented to fine tune this behavior.

Example 15.7. Binding Interceptors Example

RESTEasy will call the accept() method for interceptors that implement the AcceptedByMethod interface. If the method returns true, the interceptor will be added to the JAX-RS method's call chain; otherwise it will be ignored for that method.
In the example below, accept() determines if the @GET annotation is present on the JAX-RS method. If it is, the interceptor will be applied to the method's call chain.
@Provider
@ServerInterceptor
public class MyHeaderDecorator implements MessageBodyWriterInterceptor, AcceptedByMethod {

    public boolean accept(Class declaring, Method method) {
       return method.isAnnotationPresent(GET.class);
    }

   public void write(MessageBodyWriterContext context) throws IOException, WebApplicationException
   {
      context.getHeaders().add("My-Header", "custom");
      context.proceed();
   }
}

15.8.3. Register an Interceptor

Summary

This topic covers how to register a RESTEasy JAX-RS interceptor in an application.

Procedure 15.3. Register an Interceptor

  • To register an interceptor, list it in the web.xml file under the resteasy.providers context-param, or return it as a class or as an object in the Application.getClasses() or Application.getSingletons() method.

Example 15.8. Registering an interceptor by listing it in the web.xml file:

<context-param>
	<param-name>resteasy.providers</param-name>
	<param-value>my.app.CustomInterceptor</paramvalue>
</context-param>

Example 15.9. Registering an interceptor using the Application.getClasses() method:

package org.jboss.resteasy.example;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApp extends Application {

  public java.util.Set<java.lang.Class<?>> getClasses() {
    Set<Class<?>> resources = new HashSet<Class<?>>();
    resources.add(MyResource.class);
    resources.add(MyProvider.class);
    return resources;
  }
}

Example 15.10. Registering an interceptor using the Application.getSingletons() method:

package org.jboss.resteasy.example;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApp extends Application {

   protected Set<Object> singletons = new HashSet<Object>();

   public PubSubApplication() {
     singletons.add(new MyResource());
     singletons.add(new MyProvider());
   }
   
   @Override
   public Set<Object> getSingletons() {
     return singletons;
   }
}

15.8.4. Interceptor Precedence Families

15.8.4.1. About Interceptor Precedence Families

Summary

Interceptors can be sensitive to the order they are invoked. RESTEasy groups interceptors in families to make ordering them simpler. This reference topic covers the built-in interceptor precedence families and the interceptors associated with each.

There are five predefined families. They are invoked in the following order:
SECURITY
SECURITY interceptors are usually PreProcessInterceptors. They are invoked first because as little as possible should be done before the invocation is authorized.
HEADER_DECORATOR
HEADER_DECORATOR interceptors add headers to a response or an outgoing request. They follow the security interceptors as the added headers may affect the behavior of other interceptor families.
ENCODER
ENCODER interceptors change the OutputStream. For example, the GZIP interceptor creates a GZIPOutputStream to wrap the real OutputStream for compression.
REDIRECT
REDIRECT interceptors are usually used in PreProcessInterceptors, as they may reroute the request and totally bypass the JAX-RS method.
DECODER
DECODER interceptors wrap the InputStream. For example, the GZIP interceptor decoder wraps the InputStream in a GzipInputStream instance.
For complete type safety, there are convenience annotations in the org.jboss.resteasy.annotations.interception package: @DecoredPrecedence, @EncoderPrecedence, @HeaderDecoratorPrecedence, @RedirectPrecedence, @SecurityPrecedence. Use these instead of the @Precedence annotation. For more information, refer Section 15.4, “RESTEasy Defined Annotations”.

15.8.4.2. Define a Custom Interceptor Precedence Family

Summary

Custom precedence families can be created and registered in the web.xml file. This topic covers examples of the context params available for defining interceptor precedence families.

There are three context params that can be used to define a new precedence family.

Example 15.11. resteasy.append.interceptor.precedence

The resteasy.append.interceptor.precedence context param appends the new precedence family to the default precedence family list.
<context-param>
    <param-name>resteasy.append.interceptor.precedence</param-name>
    <param-value>CUSTOM_PRECEDENCE_FAMILY</param-value>
</context-param>

Example 15.12. resteasy.interceptor.before.precedence

The resteasy.interceptor.before.precedence context param defines the default precedence family that the custom family is executed before. The parameter value takes the form DEFAULT_PRECEDENCE_FAMILY/CUSTOM_PRECEDENCE_FAMILY, delimited by a ':'.
<context-param>
    <param-name>resteasy.interceptor.before.precedence</param-name>
    <param-value>DEFAULT_PRECEDENCE_FAMILY : CUSTOM_PRECEDENCE_FAMILY</param-value>
</context-param>

Example 15.13. resteasy.interceptor.after.precedence

The resteasy.interceptor.after.precedence context param defines the default precedence family that the custom family is executed after. The parameter value takes the form DEFAULT_PRECEDENCE_FAMILY/CUSTOM_PRECEDENCE_FAMILY, delimited by a :.
<context-param>
    <param-name>resteasy.interceptor.after.precedence</param-name>
    <param-value>DEFAULT_PRECEDENCE_FAMILY : CUSTOM_PRECEDENCE_FAMILY</param-value>
</context-param>
Precedence families are applied to interceptors using the @Precedence annotation. For the default precedence family list, refer to: Section 15.8.4.1, “About Interceptor Precedence Families”.