Interceptors
Why intercept
Interceptors are implemented in order to execute tasks before and/or after the execution
of a business logic, being data validation, database connection and transaction control, logging and data cryptography/compression the most common use cases.
How to intercept
In VRaptor 3 we adopted an approach in which the interceptor defines who will be intercepted. This is closer to the intercepting style used in systems based on AOP (Aspect Oriented Programming) than the one that was implemented in VRaptor's previous version.
Therefore, to intercept a request, just implement the Interceptor interface and annotate
the class with @Intercepts.
Just like any other component, you can specify the interceptor's scope using the scope annotations.
public interface Interceptor {
void intercept(InterceptorStack stack, ResourceMethod method,
Object resourceInstance) throws InterceptionException;
boolean accepts(ResourceMethod method);
}
Simple example
The following class shows an example of how to intercept all requests using session scope and simply print the invocation to default output.
Remember that the interceptor is a component just like any other, so it can receive its
dependencies in the constructor through Dependency Injection.
@Intercepts
@SessionScoped
public class Log implements Interceptor {
private final HttpServletRequest request;
public Log(HttpServletRequest request) {
this.request = request;
}
public void intercept(InterceptorStack stack, ResourceMethod method,
Object resourceInstance) throws InterceptionException {
System.out.println("Intercepting " + request.getRequestURI());
stack.next(method, resourceInstance);
}
public boolean accepts(ResourceMethod method) {
return true;
}
}
Example using Hibernate
Probably one of the most common uses of an Interceptor is to implement the Open Session In View pattern,
which provides a database connection whenever a request is made to your application.
And in the end of that request, the connection is disposed.
This is specially useful to avoid exceptions like LazyInitializationException when rendering JSPs.
Here is a simple example that starts a database transaction in every request, and when
the logic execution ends and the page is rendered, it commits the transaction and
closes the database connection.
@RequestScoped
@Intercepts
public class DatabaseInterceptor implements br.com.caelum.vraptor.Interceptor {
private final Database controller;
private final Result result;
private final HttpServletRequest request;
public DatabaseInterceptor(Database controller, Result result, HttpServletRequest request) {
this.controller = controller;
this.result = result;
this.request = request;
}
public void intercept(InterceptorStack stack, ResourceMethod method,
Object instance) throws InterceptionException {
result.include("contextPath", request.getContextPath());
try {
controller.beginTransaction();
stack.next(method, instance);
controller.commit();
} finally {
if (controller.hasTransaction()) {
controller.rollback();
}
controller.close();
}
}
public boolean accepts(ResourceMethod method) {
return true;
}
}
This way, to use the available connection in your Resource, the following code would apply:
@Resource
public class EmployeeController {
public EmployeeController(Result result, Database controller) {
this.result = result;
this.controller = controller;
}
@Post
@Path("/employee")
public void add(Employee employee) {
controller.getEmployeeDao().add(employee);
...
}
}
How to ensure ordering: InterceptorSequence
If you want to ensure order of the execution of a set of interceptors, you can implement
the interface InterceptorSequence and return the order you want to execute the interceptors:
@Intercepts
public class MySequence implements InterceptorSequence {
public Class<? extends Interceptor>[] getSequence() {
return new Class[] { FirstInterceptor.class, SecondInterceptor.class };
}
}
You should not annotate the interceptors returned by InterceptorSequence.getSequence() with @Intercepts.