Jersey 2.x Filters

I have recently completed work on an HTTP request filter, and thought to share my jersey_logo
experiences with implementing and designing it. I will give a quick introduction to what filters are and finish with a small example of a filter implementation.

A filter is used to intercept request or response data within the HTTP request/response chain. It is described in the Java Servlet API here.  The filter object will inspect data either in the HttpServletRequest or HttpServletResponse objects, and take action on the data within those requests/responses.

invfilt

Figure 1: Servlet API Filter illustration

Filters are typically used, but not limited to, these cases:

  • Authentication
  • Security checks
  • Routing logic
  • etc.

Jersey

Jersey is a Java RESTful Web Services framework, implementing the JAX-RS API. You can read more on Jersey here. I will outline building filters, using this very popular framework.

The Jersey library helps you to manage filters with their ContextRequestFilter and ContextResponseFilter interfaces.  A class implements the ContextRequestFilter, in order to intercept HTTP requests.  Conversely, you would implement the ContextResponseFilter, in order to intercept and HTTP response.  See Figure 1 for the interception points in the request/response chains.

The code below illustrates a basic filter implementation with the Jersey library:

public class MyRequestFilter implements ContainerRequestFilter {

  @Override
  public void filter(ContainerRequestContext contextRequest) throws IOException {
        // Filter logic here
  }
}

We can see that the ContextRequestFilter class was implemented. The method parameter contextRequest holds the request-specific information, such as header or query information, which you can analyze to understand what respective logic your filter needs to implement.  For instance:

  ...
  @Override
  public  void filter(ContainerRequestContext contextRequest) throws IOException {
     MultivaluedMap<String, String> queryParameters =         containerRequestContext.getUriInfo().getQueryParameters();
     String queryValue = queryParameters.getFirst("some-key");
     if(queryValue != null) {
        // Execute some logic here
     }
  }
  ...

The code snippet above checks the query parameters in the HTTP request, to see if some-key was defined, i.e. http://someUrl.com?some-key=value. Typically, there is an object, or context object, that that is used to carry any additional information about the HTTP request to the respective resource. This is typically denoted with the @Context annotation. Or you can modify the header data in the request, much like you would with a CORS filter. This of course all depends on the type of filter you need to implement. I will illustrate passing information about the request with a context object, via the @Context annotation.

According to the Java JAX-RS specification, the @Context annotation is used to inject information into a class field, bean property or method parameter.  So we can use this to carry information about the request in the filter, down to the resource.  For instance:

  ...
  @Context
  protected RequestContext requestContext;
  ...
  @Override
  public void filter(ContainerRequestContext contextRequest)
     throws IOException {
    MultivaluedMap<String, String> queryParameters =
        containerRequestContext.getUriInfo().getQueryParameters();
    String queryValue = queryParameters.getFirst("query-key");
    if(queryValue != null) {
      // Set some value on the request object
      requestContext.setQueryValye(queryValue + " [filtered]");
    }
  }
...

Then the resource would have the RequestContext object injected into its endpoint, configured this way:

@Path("/SomeApiPath")
public class RequestFilter
  ...
  @Context
  private RequestContext requestContext;
  ...
  @GET
  public String getSomeString(@Context RequestContext requestContext) {
     // Inspect requestContext for expected data or further processing
  }
...
}

The HTTP Response would be managed in the same way, but the respective Response filter class would implement the ContainerResponseFilter.

Design Note

Although this pattern was not explored in this post, ideally the Decorator Pattern would be used for request and response filters, allowing your application to process multiple filters with each request. Please see code snippet below:

...
public class MyRequestFilterManager implements ContainerRequestFilter {

    @Context
    protected RequestContext requestContext;

    private List<IContextFilter> requestFilters;

    @Inject
    public MyRequestFilterManager(List<IContextFilter> requestFilters) {
        this.requestFilters = requestFilters;
    }

    @Override
    public  void filter(ContainerRequestContext contextRequest)
        throws IOException {
        requestFilters.forEach(filter -> {
            try {
                filter.filter(requestContext, contextRequest);
            } catch (IOException e) {
                e.getStackTrace();
            }
        });
    }
}

You can read more about the Decorator Pattern here, but you basically have a Filter Manager that would take list of filter interfaces, and it would execute the filter functionality of each filter in your application and/or the ones initialized with this manager class.

Note that the IContextFilter interface here is simply a POJO interface that you would define, forcing all the filter implementations to have the same filter method signature.

Hope this information can help someone build a filter with the Jersey REST library.

About Rick

I am a father, husband and software engineer, trying to stay active, both in my personal and professional communities. I enjoy programming and building Web applications.
This entry was posted in java, Jersey, REST, Web and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s