Understanding JAX-RS Filters

JAX-RS is the specification to provide support for web services according to the REST architectural pattern. With JAX-RS we can create RESTful services very easily by just adding a few of annotation to a Java class.

JAX-RS Filters are a way to extend the functionality and are executed before or after processing the web service request. We process metadata associated with a message: HTTP headers, Query parameters, media type and more, they have a capability to abort a message invocation, which is very useful to implement security features. They allow you to encapsulate common behaviour that cuts across large parts of your application. This behaviour is usually infrastructure- or protocol-related code that you don’t want to pollute your business logic with.

Filter Types

There are two types of filters: Request filters and Response filters.

  • Request Filters are executed before the invocation of the JAX-RS method.
  • Response Filters are executed after the invocation of the JAX-RS method.

How to create a JAX-RS Filter?

It’s very easy, we can create a simple class and implement ContainerRequestFilter and/or ContainerResponseFilter and then override its methods. You can choose to only implement one of them or both it depends on your requirements.

package com.orthanc.jaxrsfilterexample.rest.filter;

import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Adam M. Gamboa G.
 */
@Provider
public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class);

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        LOGGER.info("Before the method invocation");
		LOGGER.debug("Entering in Resource : /{} ", requestContext.getUriInfo().getPath());
        //requestContext.getHeaders()
        //requestContext.getParameters()
        //requestContext.getMethod()
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
                                 throws IOException {
        LOGGER.info("After the method invocation");
        responseContext.getHeaders().add("X-Powered-By", "My Awesome APP");
    }
}

To register the filter there are different approaches, many preferred is to add the @Provider annotation to the filter class (as the above example). That annotation enable the class to be scan during the application startup and be registered automatically.

Also, you can omit the annotation and register the filter manually.

public class MyRequestFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
    }
}
@ApplicationPath("/")
public class MyApplication extends ResourceConfig {

    @Override
    public Set<Class<?>> getClasses() {
        final Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(MyRequestFilter.class);
        return classes;
    }
}

Applying the Filter to only some Resources

By default, the filters are execution for all the HTTP requests, but you can bound a filter to a specific Resource Class, or method. This is possible thanks to the @NameBinding annotation.

We first need to create a custom annotation and annotate it with @NameBinding.

import javax.ws.rs.NameBinding;

@NameBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggeable {}

Let’s use the previous LoggingFilter class, and apply it only to one method in a JAX-RS resource class. So, let’s add the @Loggeable annotation to the filter.

@Provider
@Loggeable
public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {
  ...
}

And then we add the @Loggeable custom annotation to the method we want to execute the Filter.

@Path("/customers")
public class CustomerResource {

   @GET
   @Path("{id}")
   @Loggeable
   public String getCustomer(@PathParam("id") String id) {...}

   @GET
   public List<String> findCustomers() {...}
}

And voila! The LoggingFilter will be only executed for the getCustomer(String id) method.

You can also annotate the Resource Class and all its methods will execute it, so in a resource class with many methods, you don’t have to annotate them one by one.

Filtering Order

If we have multiple filters in that application the execution order is uncertain. There are situations where we would like to have some filters to be executed before or after other ones. Prioritization of Filters can be done by using the @javax.annotation.Priority("<a number>") annotation.

The priority value can take any integer value, as lower is this value it will be executed first than the other ones. As a reference you can use these constants to have an idea of which values to use:

package javax.ws.rs;

public final class Priorities {

    private Priorities() {
        // prevents construction
    }  
    /**
     * Security authentication filter/interceptor priority.
     */
    public static final int AUTHENTICATION = 1000;
    /**
     * Security authorization filter/interceptor priority.
     */
    public static final int AUTHORIZATION = 2000;
    /**
     * Header decorator filter/interceptor priority.
     */
    public static final int HEADER_DECORATOR = 3000;
    /**
     * Message encoder or decoder filter/interceptor priority.
     */
    public static final int ENTITY_CODER = 4000;
    /**
     * User-level filter/interceptor priority.
     */
    public static final int USER = 5000;
}

If no Priority is indicated to the filter it will have a default value of 5000. Therefore, you can use a value greater or lower than 5000 according to your purpose.

@Provider
@Priority(5001)
public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {
  ...
}

As an alternative to using the annotation @Priority, we can indicate the priority by registering the Filter manually. In my personal opinion, I prefer to use the annotation approach.

import javax.ws.rs.core.Configurable;

@ApplicationPath("/")
public class MyApplication {
   public MyApplication(@Context Configurable configurable) {
      configurable.register(MyFilter.class, 4500);
   }
}

References

Share

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *