Page 1 of 2
The first tutorial concentrate on creating a basic interceptor, but useful in a typical web application.
This is very common that developer wants to restrict the browser to cache rendered pages. And the way we used to achieve is having code similar to the following mostly in the JSP pages to prevent caching. Either we have this code in all the JSP pages or more friendly way is to have a common JSP page where we have this code and include it in all the main JSP pages.If we are using tiles or any such framework we follow the same strategy.
response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0);
Struts2 helps us achieve this through interceptors and we don‘t want to have a JSP page to do this. This way we keep the code clean in JSP pages.
A little bit about interceptor before we start coding, interceptors are much like Servlet Filters as interceptors
follows the same design patters that a Servlet filter; intercepting filter.
Properties of Interceptors:
- Interceptors executes twice per action execution. One before the action execute and after the action executes.
- The order in which interceptors execute matters a lot, and interceptors do mean it. Though not all interceptors.
- Interceptors are not thread safe, since it is shared among all the action execution context ;so care should be taken.
Writing the interceptor:
Now that we know about the interceptors lets start.
package com.bullraider.apps.interceptors; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class ClearCacheInterceptor extends AbstractInterceptor{ @Override public String intercept(ActionInvocation invocation) throws Exception { return null; } }
- The typical and most trivial way of writing an interceptor is to extend AbstractInterceptor class which has an abstract method that we must override.
- The method intercept should execute invocation.invoke() in the intercept method. The reason is pretty similar to doFilter() method call of Servlet Filter inside doFilter() method. In plain English it means that if invocation.invokde() call is not there then the next interceptor lined up will not execute.
- The String return of the method is also very important, as this will help identify which result is returned
from the action. This is could also be a place to modify the result value ( "success","error" etc) if required and a lot of interceptor do.If not sure then always return the value coming from invocation.invokde().
After these steps in consideration here is how the intercept method looks like.
public String intercept(ActionInvocation invocation) throws Exception { String result=invocation.invoke(); return result; }
Page 2 of 2
Now coming back to our clear cache implementation, we need to execute the setHeader() method on response object, but
we need to have the response object. BUT HOW ?
The answer is in the method signature :
public String intercept(ActionInvocation invocation)
To get response object we will make use of invocation object reference. Here is how.
ActionContext ctx=invocation.getInvocationContext();
ActionContext is the context in which the actions execute. So it has encapsulated many behavior and properties that represents the action execution context.
ActionContext context=(ActionContext)invocation.getInvocationContext(); HttpServletResponse response=(HttpServletResponse)context.get(StrutsStatics.HTTP_RESPONSE);
And we are done, finally my class looks like this:
package com.bullraider.apps.interceptors; import javax.Servlet.http.HttpServletResponse; import org.apache.struts2.StrutsStatics; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class ClearCacheInterceptor extends AbstractInterceptor{ @Override public String intercept(ActionInvocation invocation) throws Exception { ActionContext context=(ActionContext)invocation.getInvocationContext(); HttpServletResponse response=(HttpServletResponse)context.get(StrutsStatics.HTTP_RESPONSE); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); String result=invocation.invoke(); return result; } }
One more important thing we skipped here is that 1st property of interceptor. When the interceptor is in pre processing state(before executing the next interceptor or action), it executes the lines of code that comes before the call to invocation.invoke() in the intercept method. And in post processing state it execute the lines comes after the invoke method.
In our code it seems meaningful only when we should have the code after the invocation.invoke() because the response is given back only in the post processing time. But If you try recollecting the Servlets request and response model, then it becomes clear that no matter where you keep the our code (before or after the call to invoke()).It does the same thing. So the 1st property of interceptor doesn’t apply to us in this situation.
Lets now stitch our interceptor in struts.xml.
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="test" extends="struts-default" > <interceptors> <interceptor name="clear-cache" class="com.bullraider.apps.interceptors.ClearCacheInterceptor" /> </interceptors> <action name="Test" class="com.bullraider.apps.actions.TestAction" > <interceptor-ref name="clear-cache" /> <result name="success">/success.jsp</result> </action> </package> </struts>
Lets have a look at the action element, there is an element <interceptor-ref> and name with value clear-cache. What it means is ; apply this interceptor clear-cache in this action named Test.
The "clear-cache" is defined inside the <interceptor> element with value "clear-cache" and class as "com.bullraider.apps.interceptors.ClearCacheInterceptor". And this declaration has to be kept inside the <interceptors> element. We will have more discussion in the 3rd tutorial later.
And for obvious reason I am not going to discuss about the action class TestAction as follows.
package com.bullraider.apps.actions; public class TestAction { public String execute(){ System.out.println("Inside Action"); return "success"; } }
Download cacheinterceptorexample.war and run in your container to test it. Well if you are wondering how will you know if the headers applied in the response i.e success page. I use firebug extension for Firefox.