Sunday, January 10, 2010

Creating Custom Interceptors in Struts2

In this post, i am going demonstrate how to create your own custom interceptor in Struts 2.

In my previous post here I demonstrated how to declare and configure interceptors in the struts.xml file. In this post we will see how to create a simple interceptor that will execute and print a few lines to the server logs, both before and after an action is executed.

A few points need to be kept in mind before writing your own interceptors ;
  1. You can create your own custom interceptor in two ways
    Implement the Interceptor interface
    Extend the AbstractInterceptor class.

  2. If You are implementing the Interceptor interface, you have to provide implementations to three methods :
    void destroy();
    void init();String intercept(ActionInvocation invocation) throws Exception;

  3. The 'init' method is called before the 'intercept' method is called. An interceptor instance is created once and is reused over many requests. Hence the interceptor code must be written to be thread safe.

  4. The 'intercept' is the place where you write the business logic code of your interceptor.

  5. The 'destroy' method is executed on application shutdown and is used to release resources used by the interceptor that have been allocated in the init method.

  6. If you are extending the AbstractInterceptor class, you only need to override the method :
    String intercept(ActionInvocation invocation) throws Exception method to provide your own implementation.

  7. In our example we will be extending the AbstractInterceptor class.
I first begin by creating a dummy action.



package sample;

import com.opensymphony.xwork2.ActionSupport;

/**
 *
 * @author ryan
 */
public class DummyAction1 extends ActionSupport{

    public String execute()
    {
        System.out.println("Inside DummmyAction1");
        return ActionSupport.SUCCESS;
    }
}


Now i create two interceptor classes.


package interceptors;

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

/**
 *
 * @author ryan
 */
public class MyInterceptor1 extends AbstractInterceptor{

    @Override
    public String intercept(ActionInvocation ai) throws Exception {
        System.out.println("Inside MyInterceptor1- Before Executing Action");
        //System.out.println("Next Invocation will invoke : "+ai.invoke());
        String invocationResult = ai.invoke();

        System.out.println("Inside MyInterceptor1- After Executing Action");
        return invocationResult;
    }

}



package interceptors;

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

/**
 *
 * @author ryan
 */
public class MyInterceptor2 extends AbstractInterceptor{

    @Override
    public String intercept(ActionInvocation ai) throws Exception {
        System.out.println("Inside MyInterceptor2- Before Executing Action");
        
        String invocationResult = ai.invoke();

        System.out.println("Inside MyInterceptor2- After Executing Action");
        return invocationResult;
    }

}




The following code shows the configuration of struts.xml



 

        
            
                
                
            

            
                
                
                /success.jsp
            
        
        



You can test this code by adding the following two lines on your jsp page to invoke your action.



Click To Test Interceptors

Since the action is already configured such that the two interceptors will be called before and after it is executed, you can check your server logs to ensure that the interceptors are executing in the order as specified in the configuration.

My Tomcat logs show the following : 


Inside MyInterceptor1- Before Executing Action
Inside MyInterceptor2- Before Executing Action
Inside DummmyAction1
Inside MyInterceptor2- After Executing Action
Inside MyInterceptor1- After Executing Action


Now lets see what is happening in the code.

Consider the intercept method of MyInterceptor1. This method is called with an argument of type 'ActionInvocation'. The ActionInvocation object is created by the struts framework and is used as a handle to the execution environment of the interceptor. It contains several useful methods. You can find the list of methods in this extremely useful class at its documentation here.

The ActionInvocation object is the heart and soul of your interceptor class. As we have already seen earlier, we can have several interceptors for a given action. The information of the sequence in which these interceptors are executed for the action is stored in the ActionInvocation object. The next interceptor in the sequence is called by calling the invoke() method of the ActionInvocation object. For the first Interceptor, the invoke() method is called by the ActionProxy object.

Since the invoke method causes the next interceptor/action in the sequence to be executed, the interceptor is in absolute control of the execution and can decide at any moment whether to proceed with the next step in the sequence or to return an error to the caller without proceeding.

A point to be remembered is that String value returned by the the invoke() method is the value that is retrieved after the action and its result have been executed. In our example, when the myInterceptor1 interceptor calls its invoke, control is passed on to the myInterceptor2, which in turn calls the action. The action upon execution returns "success" as the string, which is in turn returned to the myInterceptor2, which is in turn returned to 'myInterceptor1'. At this point of time, the result has already been executed or your jsp page has already been rendered.

As done in the above example, you can save the return value in a local variable, perform some tasks. And if everything seems to be fine, you can simply return the value in the local variable at the end of the method.

So, we have finally created and configured a custom interceptor in a simple struts 2 application.

Signing Off
Ryan

2 comments:

Anonymous said...

Excellent ... good share..

Anonymous said...

It was a great explaination. Excellent job :)