Friday, January 28, 2011

Creating Custom Struts2 Interceptors

Now we're into it. The meaty stuff. If you have not read my post on Struts2 Interceptors, I recommend you head over that way first. With me so far? Great, lets dig in.

So, why would you want to create your own interceptor? Granted, there are a whole bunch of provided interceptors. There are several that are provided but are not used in the default interceptor stack, that might be worth checking out. Things like the LoggingInterceptor (logs when you enter and leave an action) and the TimerInterceptor (tracks how long an Action take to execute) are wonderful tools that should be looked into. So, what is left?

Basically, if you ever find yourself adding code or wishing you could add code to each of your actions, you have a valid candidate for a custom interceptor.

The case study we are going to follow today is a problem I ran into a while back. I had a standard jsp header and footer that were included in every page of my site. I wanted to be able to turn on some debugging features in those pages whenever the struts.devMode constant was set to true. However, I couldn't find any way to check the struts.devMode constant from the jsp page. Could be that there is a way, but I couldn't find it. I did find that you could grab it from within your action class, and that you could set it to a member of your action class and access that from your jsp. Eck.

So I decided to make a custom interceptor. This interceptor would be responsible for getting the current value of the struts.devMode constant, and putting that on the Value Stack. In naming this interceptor, I went a step further, and called it GenerateConstants, figuring I could use this to inject any of the struts constants that I might need in the future. To kick things off, I started with the following code inside a file called GenerateConstants.java:

package mypackage.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class GenerateConstants extends AbstractInterceptor {
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // Perform pre-action stuff here
        String result = invocation.invoke();
        // Perform post-action stuff here
        return result;
    }
}

Excellent, we have the base structure. Time to fill it out a little bit. First we add the devMode property, and have struts inject it for us.

package mypackage.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class GenerateConstants extends AbstractInterceptor {
    private String devMode;

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // Perform pre-action stuff here
        String result = invocation.invoke();
        // Perform post-action stuff here
        return result;
    }

    @Inject("struts.devMode") 
    public void setDevMode(String devMode) {
        this.devMode = devMode;
    }
}

Finally, we push this value into the Value Stack

package my.package.name;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class GenerateConstants extends AbstractInterceptor {
    private String devMode;

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // Perform pre-action stuff here
        if ( devMode != null )
        {
            invocation.getInvocationContext().put("devMode", devMode);
        }

        String result = invocation.invoke();
        // Perform post-action stuff here
        return result;
    }

    @Inject("struts.devMode") 
    public void setDevMode(String devMode) {
        this.devMode = devMode;
    }
}

That is the entire amount of interceptor code needed. There is still a little more work to be done, though. Now comes the fun part - hooking it up! Crack on open your struts.xml file, and we'll work on the plumbing.

First thing that you have to do is to declare the interceptor, so that struts is aware that it exists. Inside your struts.xml file, if it does not already exist, add the interceptors tag, and add your interceptor to it, like so:


    
        
            
        
        
    


Next we need to create a new interceptor stack to use the new interceptor. Remember the default stack we talked about before? We are going to add to that now.


    
        
            

            
                
                
            
        
        
    


So here we are using the "interceptor-stack" tag to define a new stack. The child tag "interceptor-ref" just points to either an interceptor or an interceptor stack that is already defined. So in this case, we are defining our new stack with "generateConstants" as the first interceptor, followed by the "defaultStack" interceptor stack, which is the default stack provided with struts.

Finally, we have one more step to carry out. Right now, we have our shiny new stack, but we still have not told anything to use it. In order to do that, we can either declare our stack inside each Action, or we can override the default stack for all actions. The first method can be useful if you have a stack that only needs to be executed on a handful of Actions. However, our interceptor should be used by all Actions, so we are going to update the default. To do that, all we need to do is add a single tag called "default-interceptor-ref", outside of the "interceptors" tag, like so:


    
        
            

            
                
                
            
        

        

        
    


That's all there is to it. A few XML tags and a little bit of code, and you have a new interceptor.

1 comment:

  1. You explained how to create custom interceptors in a very easy to understand and concise manner. It's extremely helpful, thank you!

    ReplyDelete