Tuesday, July 2, 2013

creating resource filters with jersey and jaxrs 2.0

in what situations would I use this feature?

Let's say you have a series of preconditions for certain resources in a web application. For the sake of this example, let's assume we're using Jersey as the controller for a web application rather than just a purely RESTful web service. How can we solve this requirement?

For some simple cases it's not unheard of to see an inheritance model leveraged for this, where resources extend other resources that provide methods for certain preconditions. If single inheritance started to make this unwieldy, a composition or functional approach could be used instead, but you may end up inundating your resource with additional logic. In certain cases, it may be preferable to define this precondition with metadata, perhaps with an annotation.

In cases like this, you can leverage Jersey's dynamic binding of resource filters along with your own annotations to both define conditions as metadata and decouple resources from other business logic in your code base. Below is source code to demonstrate this.

the annotation

Pretty straight forward: no fields, just retained at runtime and able to annotate methods.

the filter

This is a little more interesting. Here we check the user agent to see if it's IE6, and if it is, we abort the request directly in the filter. Aborting the request is a new feature in jaxrs 2.0. In this case, we're sending a 412 response code (PRECONDITION_FAILED), and passing back an error page as the entity via the response.

As you might have guessed, since we're showing a ContainerRequestFilter here, there's also a ContainerResponseFilter that can also be used with resources. The response counterpart is passed both the ContainerRequestContext and a ContainerResponseContext. A typical example of a use for response filters is attaching supplemental headers to a response.

Now that we have our filter, we need a way of binding it to any resources annotated with @IE6NotSupported

the binding

Now we're getting somewhere. Here we have an implementation of a DynamicFeature, another class that's new to jaxrs 2.0. For those unfamiliar with this class, here's a quick snippet of the javadoc:

/**
 * A JAX-RS meta-provider for dynamic registration of post-matching providers
 * during a JAX-RS application setup at deployment time.
 *
 * Dynamic feature is used by JAX-RS runtime to register providers that shall be applied
 * to a particular resource class and method and overrides any annotation-based binding
 * definitions defined on any registered resource filter or interceptor instance.

This is invoked for every method of every resource in your application when the application starts up. In the example above, we're specifically looking if the method is annotated with @IE6NotSupported. We could also change our annotation to support targets of TYPE as well as METHOD, and call resourceInfo.getResourceClass() and perform the same check.

the bootstrap

Now that we have an annotation, a filter, and a way to link the filter to a resource, we need to tell our application to invoke this upon startup.

Typically you'd have many classes mapped here for all the resources and providers in your application, but for the sake of the example we're just mapping the ResourceFilterBindingFeature class.

summary

I hope this helps introduce you to the world of Jersey and jaxrs 2.0 filters. Personally, I find them hugely beneficial for mapping out both pre and post conditions that should be honored for resources without coupling them to the resource itself. I'm a big fan of annotations, and for things like resources I think there are several cases where a precondition can be expressed via metadata rather than in the resource itself.

No comments:

Post a Comment