Wednesday, October 26, 2011

How Do You Forward To An Action In Struts 2.0?


In the old Struts 1.0, you could forward from one action to another, letting the second action handle the request. There doesn't seem to be anything in Struts 2 that lets you do that. If you search on the Internet for answers, you get a lot of replies about using redirect. But redirection loses the input parameters. It has it's place, but it isn't the same as letting another action handle a request that comes in. So how do you forward?

The answer is you don't. Think about it. If one action is calling another, then they must have some code in common. Since we're good object-oriented programmers, we don't want to repeat code. This implies that the two actions are really the same class or one action extends the other. I'll show how to do either version in the example below. This example is somewhat contrived and simplified, but it will give you the basic idea.

Suppose you have a summary page, that lists documents the user owns. On the page is a number of drop down lists, that can be used to filter the document list.


Type: Date:
NumberDescriptionDateTypeAction
10-9345Order for 1000 #2 pencils10/11/2011PendingEdit | Delete
31-99AAHand wash mimeograph machine and rebuild12/31/1995CancelledEdit | Delete
AA-1239New hard drive09/30/2001CompletedEdit | Delete
08-1127Copies of latest security bulletin03/13/2010In ProcessEdit | Delete
11-2563Test drive of new car05/07/2009CancelledEdit | Delete



So the filters are sent into the action through the request, and used to control what documents get listed. But the users want the application to remember what the filters were set to, even after they navigate away from the page. This implies the filter values are stored in the session. So sometimes the summary action needs to take the filters from the request, and sometimes from the session.

Here's the action classes that do this.

The first action takes the filters from the session, and uses them to get the document summary to display.

public class SummaryAction extends ActionSupport
                           implements SessionAware {

   ...some skipped boilerplate...

  public String execute() throws Exception {
    this.docType = (DocumentType)this.session.get(DOC_TYPE);
    this.dateRange = (DateRange)this.session.get(DATE_RANGE);
    this.docList = this.service.getDocs(this.docType, this.dateRange);
    return ActionSupport.SUCCESS;
  }

}
The second action extends the first one, gets the filters from the request, and then calls super.execute():

public class FilterAction extends SummaryAction {

   ...some skipped boilerplate...

  public String execute() throws Exception {
    this.session.set(DOC_TYPE, this.docType);
    this.session.set(DATE_RANGE, this.dateRange);
    return super.execute();
  }
}
No repeated code, one action “forwards” to another, and as a plus, instead of going through the Struts framework to do it, we just use Java.

If you don't want to create a whole other class just for a little code, there's a Struts trick you can use. In your Struts configuration XML you can tell which method should be called on an action. So you can rewrite the first class to have another method like so:

public class SummaryAction extends ActionSupport
                           implements SessionAware {

   ...some skipped boilerplate...

  public String execute() throws Exception {
    this.docType = (DocumentType)this.session.get(DOC_TYPE);
    this.dateRange = (DateRange)this.session.get(DATE_RANGE);
    this.docList = this.service.getDocuments(this.docType);
    return ActionSupport.SUCCESS;
  }

  public String filter() throws Exception {
    this.session.set(DOC_TYPE, this.docType);
    this.session.set(DATE_RANGE, this.dateRange);
    return execute();
  }

}
Then in the struts.xml:

<action name="summary" class="org.hack.SummaryAction">
    <result type="tiles">hack.summary</result>
</action>

<action name="filter" class="org.hack.SummaryAction" method="filter">
    <result type="tiles">hack.summary</result>
</action>
Again, no repeated code, one action follows another, and you don't need to go through the framework to get there.

I've been having a hard time coming up with new things to say about the programming and the human mind, so I decided to branch out a little. Every once in a while I'll post a page giving a solution to a problem I had. I'll stick with problems that I couldn't find a solution to on the internet.