Multiple ActionForms in ActionMapping

There is no way in Struts to handle the most common use case and a huge percentage of the discussions on the Struts user and developer lists is about various hacks we build to do so. The answer, I would suggest, is multiple ActionForms. This is intended to be a forum for discussing that option in an intelligent way. Please add your comments at the end, so that the main problem is readable as defined, unless you want to redefine the problem. Please also feel free to offer different solutions to the most common use case problem, hacks allowed.

Dakota Jack

Most Common Use Case Described/Defined

The most common use case that I know of is when we go from one page to another page on the browser, sending the server certain request values from the first page and getting from the server certain response values on the second page. Most pages have both input data (setup) and output data (request name/value pairs).

I presume that we are using Struts, so the first page and the second page will each have different <html:form> values.

Problem Handling the Most Common Use Case Described/Defined

We are going go from firstPage.jsp to secondPage.jsp. Each of these pages has its own <html:form>. We need to process the input data from firstPage.jsp and to setup the output data for secondPage.jsp.

We need to both process input values and to provide setup values. I assume this is a given for the most common use case and, if this is wrong (which is sort of inconceivable to me), then I invite with open arms any statement showing how this is so. Until then, we will assume that no matter what we are going to have to do both of these things:

Process data from FirstPageActionForm

Setup data for SecondPageActionForm

How can we do this intelligently?

Limitation of ActionMappings in Struts Described/Defined with Respect to the Most Common Use Case

The obvious answer would be to have an Action (MyAction, say) process data from FirstPageActionForm and setup data for SecondPageActionForm. This would allow us to have a page oriented ActionForm, which makes sense, without having to use page-centric solutions. But, we cannot!

ActionMappings allow us to have only one ActionForm per Action. Since the <html:form> in firstPage.jsp and the <html:form> in secondPage.jsp logically would have different related ActionForms (FirstPageActionForm and SecondPageActionForm, say) this provides a difficulty because we have process data from FirstPageActionForm and setup data in SecondPageActionForm.

How We Do It Now

There is in fact no way, without subverting the basic framework, to handle the Most Common Use Case. And, there are only hacks available. There is no way at present to handle the Most Common Use Case without subverting the general way Struts is intended to process requests. The hacks are:

AVOID the Problem by Using One Page Only

We can create one JSP page doing the work of two which AVOIDs the problem with a "quasi-Portlet" response. There is only one ActionForm in this case, so the processing and setup can be done with This is a solution that will work in certain situations but really is a hack in most situations.

AVOID the Problem by Using One ActionForm Only

This is a common solution which is the one I usually use. I really hate this solution. This solution really has to misuse the ActionForm because it is setup to represent the <html:form> on a page. The result is that the application architecture is not that sensible.

AVOID the Problem by Chaining Actions

This is the worst in my opinion. Why would we want to use two actions and two action mappings to do one thing? There are lots of reasons to not like this.

Use setupItems

Another possible solution is the usage of the SetupItems proposal put forth by Frank W. Zammeti. Although it is currently only for version 1.2.6, it is another way to solve this problem, and one that gives you even more flexibility besides. There are important considerations to using this though, and they are documented on the Wiki page describing the solution. While I personally like this solution (as do a number of others), it is VERY IMPORTANT that you understand those considerations before choosing to use it.

Rather than chain Actions, treat them as what they are: CLASSES!

This is the solution I've found myself using most frankly... There has never been anything to stop someone from instantiating an Action and calling execute() on it manually. For some reason, when I talk to people and mention this, they seem to think there is someting magical about Actions. Not! (smile)

So, assume we have the "typical" organization of classes where for Page A there is a setup Action (maybe literally called SetupActionA) and a "processing" Action (ProcessActionA?), and the same organization for Page B... Now, let's say we submit a form from Page A that fires the processing Action for it. As a result, we want to move to Page B. Instead of forwarding to Page B's setup Action, just do this at the end of Page A's processing Action:

SetupActionB sab = new SetupActionB(); 
return sab.execute(m, f, req, rep);

That avoids the overhead of the forward while accomplishing the same thing. The down-side to this is that Actions are no longer independant units, and arguably you lose some of the declarative nature of Struts. Some Actions will "know about" others, which is considered bad by most (me included, generally). But, I agree with Dakota Jack that there doesn't seem to be a good, standard way of doing this at this point, so every "hack" in use today has its pluses and minuses, this one just like all the rest.

CONCLUSION

The result, I think, is that there is no way to handle the most common use case in Struts. How weird is that?

Dakota Jack

Another solution?

I have seen another solution, which prevents the need for actions to know about others.

Pre/Post Action

It started by using a parameter attribute on the action configuration within struts-config.xml, which has 2 possible values "pre" and "post", these are picked up by a common base class and call two methods, "preExecute" and "postExecute", for each page you define 2 entries in your struts-config:

    <!-- Pre action -->
    <action path="/PrePageA" type="ActionA" name="FormBeanA" parameter="pre">
      <forward name="Success" path="/pageA.jsp"/>
    </action>

    <!-- Post action -->
    <action path="/PostPageA" type="ActionA" name="FormBeanA" parameter="post">
      <forward name="Success" path="/PrePageB.do"/>
    </action>

The problem with this is that you can pretty much consider your struts-config.xml doubled. I don't claim this one as my own, I think it's quite widely used.

Prepopulate

To alleviate the struts-config explosion, this can be approached in another way. Instead of using a parameter to do the switch, do it automatically at page load. If you make a couple of 'tweaks' to the Request{{`Processor to make the "processMapping" and "processActionCreate" methods public, and another 'tweak' to the Action}}Servlet to allow access to the Request`Processor through an accessor. The html:form knows where it is going to submit, and using these changes can get the action mapping and thus the "Action" that will be invoked when the form is submitted. You can create an interface which defines a prePopulate method, and if your action implements this interface then call it:

ActionMapping mapping = requestProcessor.processMapping(request, response, action);
Action action = requestProcessor.processActionCreate(request, response, mapping);

if (action instanceof IPreProcessAction)
{
  IPreProcessAction preProcessAction = (IPreProcessAction) action;

  ActionForm actionForm = (ActionForm) pageContext.getAttribute(Constants.BEAN_KEY, PageContext.REQUEST_SCOPE);
  preProcessAction.preProcess(mapping, actionForm, (HttpServletRequest) request, (HttpServletResponse) response);
}

This approach still has problems with passing data from the output of ActionA to the "prePopulate" of ActionB, but with strict session management I still think this provides the best solution at the moment.

Simon Matic Langford

  • No labels