XWork : Interceptors
This page last changed on Aug 18, 2004 by unkyaku.
OverviewInterceptors allow you to define code to be executed before and/or after the execution of an action. They are defined outside the action class, yet have access to the action and the action execution environment at runtime, allowing you to encapsulate cross-cutting code and provide separation of concerns. Interceptors are given the ActionInvocation object at runtime, and may do whatever processing needed, then forward processing to the rest of the ActionInvocation, which will either call the next Interceptor or the Action, if there are no more Interceptors, and do whatever post-processing needed.Interceptors may also decide to short-circuit processing and return whatever result string desired WITHOUT forwarding processing, thus keeping the Action from executing. This ability should be used with caution, however, as any data loading or processing expected to be done by the Action will not happen. Here is the invoke() method from ActionInvocation, which calls the Interceptors and the Action:public String invoke() throws Exception { if (executed) { throw new IllegalStateException("Action has already executed"); } if (interceptors.hasNext()) { Interceptor interceptor = (Interceptor) interceptors.next(); result = interceptor.intercept(this); } else { result = action.execute(); executed = true; } return result; } It may not be immediately apparent how the rest of the Interceptors and the Action come to be called from the code snippet. For this we need to look at the Interceptor implementation in AroundInterceptor: public String intercept(ActionInvocation invocation) throws Exception { before(invocation); result = invocation.invoke(); after(invocation); return result; } Here we can see that the Interceptor calls back into the ActionInvocation.invoke() to tell the ActionInvocation to continue down the chain and eventually executes the Action. It is here that the Interceptor can decide not to forward to the rest of the Interceptors and the Action, and choose instead to return a return code. It is also important to know what the AroundInterceptor is doing when you extend it to implement your own Interceptors.The AroundInterceptor defines a base class for interceptor implementations. It delegates calls to subclasses, which must implement the abstract methods before() and after(). The before() call is first called, then the rest of the ActionInvocation is called and the String result is saved (and is available to the Interceptor implementation during the after() method). Finally, the after() method is called and the result is returned. ![]() Utility InterceptorsThe TimerInterceptor and LoggingInterceptor are provided as simple examples and utilities.
public String intercept(ActionInvocation dispatcher) throws Exception { long startTime = System.currentTimeMillis(); String result = dispatcher.invoke(); long executionTime = System.currentTimeMillis() - startTime; log.info("Processed action " + dispatcher.getProxy().getActionName() + " in " + executionTime + "ms."); return result; } It is important to remember to call invoke() on the ActionInvocation if you directly implement Interceptor, otherwise the rest of the Interceptors and the Action will not be executed. Parameter Interceptors - populating your ActionThe StaticParametersInterceptor and ParametersInterceptor populate your Action fields during the ActionInvocation execution.
The StaticParametersInterceptor should be applied before the ParametersInterceptor so that the static parameters may be set as the defaults and overridden by the request parameters. ModelDrivenInterceptor - choosing your modelNormally, the StaticParameterInterceptor and the ParametersInterceptor apply themselves directly to the Action. Using the ModelDrivenInterceptor, you can specify an alternate object to have the parameters applied to instead.Consider the following Action: public class AddContactAction implements Action { private String name; private String addr; private String city; public void setName(String name) { this.name = name ; } public void setAddr(String addr) { this.addr = addr ; } public void setCity(String city) { this.city = city ; } public String execute() { Contact contact = new Contact(); contact.setName(name); contact.setAddr(addr); contact.setCity(city); // save contact information here } } We can see that our action will be populated with name, addr, and city parameters if they are passed in. In the execute we copy these values to a contact object and save the contact. Here's the ModelDriven interface:public interface ModelDriven { public Object getModel(); } Let's apply the ModelDriven interface to Action above: public class AddContactAction implements Action, ModelDriven { private Contact contact = new Contact(); public Object getModel() { return this.contact ; } public void execute() { // save the contact information } } Now the ParametersInterceptor and the StaticParametersInterceptor will be applied directly to our Contact so when execute gets called, this.contact will already be populated with all the information we need. Neat, huh? Behavior similar to model driven can be achieved just using the parameter interceptor. For example, rather than implementing ModelDriven, we could have written:public class AddContactAction implements Action { private Contact contact = new Contact(); public Contact getContact { return this.contact ; } public void execute() { // save the contact information } } The difference between this Action and the previous ModelDriven action is twofold:
One potential drawback when using ModelDriven actions is that if you need to access some parameters in order to load the model for the ModelDriven action, you will need to call the ParametersInterceptor and/or the StaticParametersInterceptor twice (before and after the ModelDrivenInterceptor). The first time sets all parameters on the Action, the second time sets all parameters on the model. ChainingInterceptorThe ChainingInterceptor populates the Action it is being applied to with the results of the previous action. When actions are chained together, the action being chained FROM is on the ValueStack when the next action is called. This means that when the next ActionProxy is executed, the action that is being chained TO will be placed onto the valuestack, but the old action will also be there, just down one level. This interceptor works by traversing the ValueStack to find the parameters of any objects there and sets them onto the final action. |
![]() |
Document generated by Confluence on Dec 12, 2004 12:35 |