xwork / docs / interceptors.html

Full commit
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="" lang="en_US" xml:lang="en_US">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <title>OpenSymphony Wiki (Offline Version) :: Xwork Documentation</title>
  <link type="text/css" href="main.css" rel="STYLESHEET"/>
  <div id="page-logo">
    <a href="index.html">XWork Documentation</a>
    <div class="snip-title">
	  <h1 class="snip-name">Xwork Interceptors
<div id="snip-content" class="snip-content">

 <div class="snip-attachments"></div>
 <h3 class="heading-1">Overview</h3><p class="paragraph"/>Interceptors 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.<p class="paragraph"/>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.<p class="paragraph"/>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.<p class="paragraph"/>Here is the invoke() method from ActionInvocation, which calls the Interceptors and the Action:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> <span class="java&#45;object">String</span> invoke() <span class="java&#45;keyword">throws</span> Exception &#123;
        <span class="java&#45;keyword">if</span> (executed) &#123;
            <span class="java&#45;keyword">throw</span> <span class="java&#45;keyword">new</span> IllegalStateException(<span class="java&#45;quote">"Action has already executed"</span>);
        &#125;<p class="paragraph"/>        <span class="java&#45;keyword">if</span> (interceptors.hasNext()) &#123;
            Interceptor interceptor = (Interceptor);
            result = interceptor.intercept(<span class="java&#45;keyword">this</span>);
        &#125; <span class="java&#45;keyword">else</span> &#123;
            result = action.execute();
            executed = <span class="java&#45;keyword">true</span>;
        &#125;<p class="paragraph"/>        <span class="java&#45;keyword">return</span> result;
    &#125;</pre></div><p class="paragraph"/>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 <b class="bold">AroundInterceptor</b>:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> <span class="java&#45;object">String</span> intercept(ActionInvocation invocation) <span class="java&#45;keyword">throws</span> Exception &#123;
        before(invocation);<p class="paragraph"/>        result = invocation.invoke();
        after(invocation);<p class="paragraph"/>        <span class="java&#45;keyword">return</span> result;
    &#125;</pre></div><p class="paragraph"/>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.<p class="paragraph"/>It is also important to know what the AroundInterceptor is doing when you extend it to implement your own Interceptors.<p class="paragraph"/>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. 
<h3 class="heading-1"><a name="Utility"/><a href="interceptors.html#Utility" title="Permalink to Utility"><img src="" alt="" border="0"/></a> Utility Interceptors</h3><p class="paragraph"/>The TimerInterceptor and LoggingInterceptor are provided as simple examples and utilities. 
<ul class="star">
<li>The <b class="bold">LoggingInterceptor</b> simply logs before and after executing the rest of the ActionInvocation.</li>
<li>The <b class="bold">TimerInterceptor</b> times the execution of the remainder of the ActionInvocation.</li>
The TimerInterceptor does not extend AroundInterceptor because it needs to keep some state (the start time) from before the rest of the execution. Interceptors must be stateless, so it is impossible to save this in an instance field. It is a good rule of thumb to say that if your interceptor needs to maintain information from the beginning to the end of the interceptor call, it should implement Interceptor directly, not subclass AroundInterceptor. Here is the code for <b class="bold">intercept()</b> in TimerInterceptor:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> <span class="java&#45;object">String</span> intercept(ActionInvocation dispatcher) <span class="java&#45;keyword">throws</span> Exception &#123;
        <span class="java&#45;object">long</span> startTime = <span class="java&#45;object">System</span>.currentTimeMillis();
        <span class="java&#45;object">String</span> result = dispatcher.invoke();
        <span class="java&#45;object">long</span> executionTime = <span class="java&#45;object">System</span>.currentTimeMillis() &#45; startTime;<span class="java&#45;quote">"Processed action "</span> + dispatcher.getProxy().getActionName() + <span class="java&#45;quote">" in "</span> + executionTime + <span class="java&#45;quote">"ms."</span>);<p class="paragraph"/>        <span class="java&#45;keyword">return</span> result;
    &#125;</pre></div><p class="paragraph"/>It is important to remember to call <b class="bold">invoke()</b> on the ActionInvocation if you directly implement Interceptor, otherwise the rest of the Interceptors and the Action will not be executed.
<h3 class="heading-1"><a name="Parameter"/><a href="interceptors.html#Parameter" title="Permalink to Parameter"><img src="" alt="" border="0"/></a> Parameter Interceptors - populating your Action</h3><p class="paragraph"/>The StaticParametersInterceptor and ParametersInterceptor populate your Action fields during the ActionInvocation execution. 
<ul class="star">
<li>The <b class="bold">StaticParametersInterceptor</b> applies the parameters defined in the Action configuration with the &#60;param&#62; elements.</li>
<li>The <b class="bold">ParametersInterceptor</b> populates the Action with the parameters passed in as part of the request.</li>
</ul><p class="paragraph"/>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.
<h3 class="heading-1"><a name="ModelDriven"/><a href="interceptors.html#ModelDriven" title="Permalink to ModelDriven"><img src="" alt="" border="0"/></a> ModelDrivenInterceptor - choosing your model</h3><p class="paragraph"/>Normally, the <b class="bold">StaticParameterInterceptor</b> and the <b class="bold">ParametersInterceptor</b> apply themselves directly to the Action.  Using the ModelDrivenInterceptor, you can specify an alternate object to have the parameters applied to instead.<p class="paragraph"/>Consider the following Action:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> class AddContactAction <span class="java&#45;keyword">implements</span> Action &#123;
  <span class="java&#45;keyword">private</span> <span class="java&#45;object">String</span> name;
  <span class="java&#45;keyword">private</span> <span class="java&#45;object">String</span> addr;
  <span class="java&#45;keyword">private</span> <span class="java&#45;object">String</span> city;<p class="paragraph"/>  <span class="java&#45;keyword">public</span> void setName(<span class="java&#45;object">String</span> name) &#123; <span class="java&#45;keyword">this</span>.name = name ; &#125;
  <span class="java&#45;keyword">public</span> void setAddr(<span class="java&#45;object">String</span> addr) &#123; <span class="java&#45;keyword">this</span>.addr = addr ; &#125;
  <span class="java&#45;keyword">public</span> void setCity(<span class="java&#45;object">String</span> city) &#123; <span class="java&#45;keyword">this</span>.city = city ; &#125;<p class="paragraph"/>  <span class="java&#45;keyword">public</span> <span class="java&#45;object">String</span> execute() &#123;
     Contact contact = <span class="java&#45;keyword">new</span> Contact();
     contact.setCity(city);<p class="paragraph"/>     // save contact information here
&#125;</pre></div><p class="paragraph"/>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.<p class="paragraph"/>Here's the ModelDriven interface:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> <span class="java&#45;keyword">interface</span> ModelDriven &#123;
  <span class="java&#45;keyword">public</span> <span class="java&#45;object">Object</span> getModel();
&#125;</pre></div><p class="paragraph"/>Let's apply the ModelDriven interface to Action above:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> class AddContactAction <span class="java&#45;keyword">implements</span> Action, ModelDriven &#123;
  <span class="java&#45;keyword">private</span> Contact contact = <span class="java&#45;keyword">new</span> Contact();<p class="paragraph"/>  <span class="java&#45;keyword">public</span> <span class="java&#45;object">Object</span> getModel() &#123; <span class="java&#45;keyword">return</span> <span class="java&#45;keyword">this</span>.contact ; &#125;<p class="paragraph"/>  <span class="java&#45;keyword">public</span> void execute() &#123;
    // save the contact information
&#125;</pre></div><p class="paragraph"/>Now the ParametersInterceptor and the StaticParametersInterceptor will be applied directly to our Contact so when execute gets called, will already be populated with all the information we need.  Neat, huh?<p class="paragraph"/>Behavior similar to model driven can be achieved just using the parameter interceptor.  For example, rather than implementing ModelDriven, we could have written:<p class="paragraph"/><div class="code"><pre><span class="java&#45;keyword">public</span> class AddContactAction <span class="java&#45;keyword">implements</span> Action &#123;
  <span class="java&#45;keyword">private</span> Contact contact = <span class="java&#45;keyword">new</span> Contact();<p class="paragraph"/>  <span class="java&#45;keyword">public</span> Contact getContact &#123; <span class="java&#45;keyword">return</span> <span class="java&#45;keyword">this</span>.contact ; &#125;<p class="paragraph"/>  <span class="java&#45;keyword">public</span> void execute() &#123;
    // save the contact information
&#125;</pre></div><p class="paragraph"/>The difference between this Action and the previous ModelDriven action is twofold:
<ul class="star">
<li>Using the ModelDriven action, we can reference our parameters name, addr, and city.  Also, the Model (Contact) will be pushed onto the ValueStack so we'll have Contact and AddContactAction on the value stack</li>
<li>In the more recent example, we need to reference our parameters as, contact.addr, and</li>
<h3 class="heading-1"><a name="Chaining"/><a href="interceptors.html#Chaining" title="Permalink to Chaining"><img src="" alt="" border="0"/></a> ChainingInterceptor</h3><p class="paragraph"/>The <b class="bold">ChainingInterceptor</b> 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.
<h3 class="heading-1"><a name="DefaultWorkflow"/><a href="interceptors.html#DefaultWorkflow" title="Permalink to DefaultWorkflow"><img src="" alt="" border="0"/></a> DefaultWorkflowInterceptor</h3><p class="paragraph"/>The <b class="bold">DefaultWorkflowInterceptor</b> implements the workflow that was found in ActionSupport in WebWork 1.x. These workflow steps are applied before the rest of the Interceptors and the Action are executed, and may short-circuit their execution:
<li>If the Action implements <b class="bold">com.opensymphony.xwork.Validateable</b>, the <b class="bold">validate()</b> method is called on it, to allow the Action to execute any validation logic coded into it.</li>
<li>If the Action implements <b class="bold">com.opensymphony.xwork.ValidationAware</b> the <b class="bold">hasErrors()</b> method is called to check if the Action has any registered error messages (either Action-level or field-level). If the ValidationAware Action has any errors, <b class="bold">Action.INPUT</b> ("input") is returned without executing the rest of the Interceptors or the Action.</li>
<li>If the execution did not short-circuit in step 2 above, the rest of the Interceptors and the Action are executed by calling <b class="bold">invocation.invoke()</b></li>