Intercept Execution

Stripes includes an Interceptor system designed to make it easy to add functionality to Stripes. For cross-cutting behaviour it is often simpler to write an interceptor than to extend Stripes' built in components.

The following classes form the core of the Stripes lifecycle and interceptor system:

  • LifecycleStage is an enum that describes the stages through which a request progresses. This is discussed in great detail in the Lifecycles Etc. page.
  • Interceptor defines the interface contract that interceptors use.
  • Intercepts is an annotation used to mark interceptors with the stages that they will intercept
  • ExecutionContext wraps up all the context associated with an ActionBean invocation, and is supplied to interceptors

An Example Interceptor

Let's look at an example. The following is a simple interceptor that "logs" a message before and after every lifecycle stage:

NoisyInterceptor.java
@Intercepts({LifecycleStage.ActionBeanResolution, 
    LifecycleStage.HandlerResolution, 
    LifecycleStage.BindingAndValidation, 
    LifecycleStage.CustomValidation, 
    LifecycleStage.EventHandling, 
    LifecycleStage.ResolutionExecution}) 
public class NoisyInterceptor implements Interceptor { 
    public Resolution intercept(ExecutionContext ctx) throws Exception { 
        System.out.println("Before " + ctx.getLifecycleStage()); 
        Resolution resolution = ctx.proceed(); 
        System.out.println("After " + ctx.getLifecycleStage()); 
        return resolution 
    } 
} 

While this interceptor does nothing of practical value, it does illustrate some basic concepts. Firstly the @Intercepts is used to specify what lifecycle stages are intercepted. In this case we specify all stages, so the interceptor will be invoked up to six times during every request to an ActionBean! Secondly, interceptors intercept around the lifecycle stage. As a result they can execute code before it and after it. When the interceptor is ready to execute the lifecycle code (and/or any downstream interceptors) it simply calls ExecutionContext.proceed().

In fact interceptors can decide to skip a stage entirely, or even abort execution early by returning a Resolution. When an interceptor does this, the resolution is executed and the rest of the lifecycle is omitted.

Configuring Interceptors

Configuring interceptors is fairly straightforward. With Stripes 1.5 and up, your interceptors will automatically be detected if they are in a package configured with the Extension.Packages parameter. You can also use an initialization parameter of the Stripes Filter:

Configuring Interceptors in the web.xml (Stripes 1.5 and up)
<init-param> 
    <param-name>Interceptor.Classes</param-name> 
    <param-value> 
        com.myco.NoisyInterceptor 
    </param-value> 
</init-param> 
</filter> 

Normally, you would only use the web.xml configuration method if the ordering of the interceptors is important. When multiple interceptors intercept at the same lifecycle stage they are executed in the order they are listed.

If you are using Stripes 1.4.x or earlier, you must use the web.xml configuration, and the only gotcha is that Stripes by default configures a single interceptor, the BeforeAfterMethodInterceptor. When specifying interceptors you need to also specify the BeforeAfterMethodInterceptor unless you wish to disable it:

Configuring Interceptors in the web.xml (Stripes 1.4.x and earlier)
 <init-param> 
    <param-name>Interceptor.Classes</param-name> 
    <param-value> 
        com.myco.NoisyInterceptor, 
        net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
    </param-value> 
</init-param> 
</filter> 

Another Example: Security

Another example of where an interceptor might be useful is in application security. A prototype interceptor might look like:

SecurityInterceptor.java
@Intercepts(LifecycleStage.HandlerResolution) 
public class SecurityInterceptor implements Interceptor { 
    /** Intercepts execution and checks that the user has appropriate permissions. */ 
    public Resolution intercept(ExecutionContext ctx) throws Exception { 
        Resolution resolution = ctx.proceed(); 

        if (isPermitted(ctx.getActionBean(), ctx.getActionBeanContext()) { 
            return resolution; 
        } 
        else if (loggedIn(ctx.getActionBeanContext()) { 
            return new RedirectResolution("/security/Unauthorized.jsp"); 
        } 
        else { 
            return new RedirectResolution("/security/Login.jsp"); 
        } 
    } 

    /** Returns true if the user is logged in. */ 
    protected boolean isLoggedIn(ActionBeanContext ctx) { 
        return ((MyActionBeanContext) ctx).getUser() != null; 
    } 

    /** Returns true if the user is permitted to invoke the event requested. */ 
    protected boolean isPermitted() { ... } 
} 

In this case the interceptor intercepts at one specific lifecycle stage: handler resolution. When this stage is complete the ExecutionContext contains information about which ActionBean is being called, and which event is being handled - sufficient information to determine if the user is allowed access or not. On top of this, the interceptor can, through the ActionBeanContext, access information in the HttpServletRequest for further checks.

The BeforeAfterMethodInterceptor

The BeforeAfterMethodInterceptor mentioned above is an interceptor which gives ActionBeans the ability to define methods to be execute before and/or after certain lifecycle stages. Methods marked with a @Before are run before the specified lifecycle stages. If no stage is specified then the method is run before the EventHandling stage. Methods marked with an @After annotation work similarly, but run after the specified lifecycle stages.

The SpringInterceptorSupport class

The SpringInterceptorSupport class is a simple base class that provides Spring bean injection into custom interceptors. Otherwise there is no difference between extending SpringInterceptorSupport and implementing Interceptor directly.