Intercepting Jersey resource method calls

Sometimes it would be nice to have a possibility to wrap a resource method call. Imagine a use-case in which you need to process business code, placed in your resource method, in a transaction (open transaction before resource method, commit or rollback transaction at the end) or apply some advanced security constraints. If you’re using JAX-RS together with CDI in your application it’s not a problem to do such things. Fortunately, with Jersey it’s also possible to do those things outside of CDI.

In Jersey 1 there are dedicated SPI classes to achieve this task, ResourceMethodDispatchProvider and RequestDispatcher. Once you register your dispatch provider by placing an identifier in the resource directory (META-INF/services) you can start creating request dispatchers that wraps and affects the invocation of resource methods.

Jersey 2 doesn’t expose similar SPI classes but users are be able to leverage means provided by HK2, an injection framework Jersey uses internally. HK2 supports some AOP capabilities which means that Jersey 2 applications can take advantage of these capabilities as well.

In general, constructors and methods of each instance managed by HK2 can be intercepted but, in this article, we’re going to focus on intercepting just JAX-RS Resources and Providers.

To demonstrate the feature I’ve created a simple example, jersey-intercepting-resource-methods, excerpts of which I’d be showing in the following sections.

Setup

Interception entry point in HK2, that identifies instances containing constructors and methods to be intercepted, is called InterceptionService.

In the simplest case you just need to, in your implementation, provide a filter (getDescriptorFilter() method, see line 5 in the example below) to filter out managed objects on the class level and then you can assign each given constructor or method a list of interceptors to be invoked before the actual constructor or method is invoked.

 

A working implementation of the service is available in MyInterceptionService class.

Now, when we have our custom implementation of the interception service we need to let HK2 know about it. There are two ways how to do the job. The first approach is to extend AbstractBinder and register it’s instance in Jersey ResourceConfig (or sub-class of JAX-RS Application).

MyInterceptionService has to be bound to HK2’s InterceptionService contract in the Singleton scope.

The second way is to annotate our custom interception service with @Service annotation and use hk2-inhabitant-generator to look for all HK2 services in the application. References to HK2 services are saved during build into a file bundled with the application. The locator file is later used to automatically register services during runtime.

To try the latter approach in the demo application just uncomment the mentioned plugin in pom.xml and disable the registration of MyInterceptionBinder in the Application class.

Resources

Let’s take a look at intercepting JAX-RS Resources first. Assume that we have a simple resource with two resource methods that return plain String.

The first method, get(), is not intercepted so when we invoke a request that is handled by this method, either via test (see InterceptingTest) or via curl, we’d get:

As expected we received the message defined in the resource method on the client side and nothing more.

The second method, getIntercepted(), is intercepted by a custom resource method interceptor, ResourceInterceptor, which appends an additional line at the end of the message returned from the method. The @Intercept annotation placed above this method tells our interception/filtering service that this method should be considered for intercepting.

When we fetch this resource the resulting entity would look like:

In the output we can clearly see that the method was intercepted and the additional line was added by our method interceptor.

Providers

I wrote that all types/objects managed by HK2 can be intercepted. This also includes JAX-RS Providers that are registered in your application. For instance, lets create a message body provider (MessageBodyReader) that would be reading input stream and transforming it into String. (Yes, there is such a provider available in every JAX-RS implementation but with our implementation we’re going to suppress the default one and slightly modify its behavior.)

As you can see we’re intercepting the processing here in two places – constructor and isReadable(…) method (notice the presence of the @Intercept annotation). The reason the constructor is intercepted is because we’d like to set the providerName field, after an instance is created, directly in the interceptor.

Then we have the isReadable(…) method with default implementation that always returns false. This prevents our custom StringProvider to do something useful in readFrom(…) method as it is never called if isReadable(…) returns false. However, the isReadable(…) method is intercepted and we can alter its behavior in our interceptor and that’s exactly what we do.

The whole implementation of ProviderInterceptor looks like the following.

Client

Since we can intercept HK2 managed JAX-RS Providers we can affect behavior of the client to the some degree as well. If the custom StringProvider and MyInterceptionBinder are registered in the client the constructor and isReadable(…) method of the provider would be intercepted as well.

Lets create a client resource on the server as illustrated above. At first, a configured instance of JAX-RS WebTarget is injected into our resource and then this instance is used to retrieve a message from (non-intercepted) server resource method. Entity returned from server is used to assemble a new response returned from this ClientResource‘s resource method.

Injecting configured client (web target) is a Jersey custom feature. You can find more in Managed JAX-RS Client article.

On the other side of the wire the response would look like:

The StringProvider was used on the client to transform the response coming from ServerResource and the last line was added to the result. A similar output (without the first line) is expected even in case when we use JAX-RS Client API outside of an application deployed on server – see InterceptingTest.clientFetchingServerResource test.

Transactions

Intercepting JAX-RS resources can be useful in situations when you need to wrap  business code present in a resource method into a JTA transaction. Rather than starting and committing (rollbacking) a transaction directly in your resource method you can abstract the code into a method interceptor and annotate the resource method with an appropriate annotation (e.g. @Transactional) to denote that the transactional processing should be started.

In the method interceptor you need an instance of a UserTransaction (e.g. provided by application server) obtained from e.g. JNDI via InitialContext. The method interceptor might in this case look like:

Now, for every method annotated with @Transactional a separate transaction would be started before the processing actually enters the resource method and committed (rollbacked) after we return from that method.

Further Reading

Resources

Thanks

To Jakub for reading draft of this.

  • avihai marchiano

    The problem with implement transaction filter as MethodInterceptor is that its called after Factory::inject and ContainerRequestFilter, so the transaction will not be available neither in injection nor ContainerRequestFilter.