struts-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Duffey, Kevin" <KDuf...@BUYMEDIA.com>
Subject RE: Multi-page forms ?
Date Mon, 09 Oct 2000 18:29:51 GMT
There are many ways you can do this, but two really stick to mind. One, you
can have a single action per PAGE of a form. That is, if your overall
transaction requires 5 pages, each with forms on them, and each "next" page
being built up possibly from input values of the prevous page (for example,
the first page asks gender, and if the user selects male, the next page
shows a list of options for selection that males would be likely to select),
each of those pages would have a different action name. Thus, in your
action.xml, you would define 5 separate actions, possibly to the same one
action class, or 5 separate action classes. The other method is to use a
single action per series of forms, but specify a command attribute in the
submit such as:
 
<input type="submit" value="ADD"
onclick="this.form.action='/MyAction.do?command=1'">
<input type="submit" value="DELETE"
onclick='this.form.actio='/MyAction.do>command=2'">
 
An alternative, which is what I decided to do, is use Reflection to call a
method directly based on the command passed in. I wrote a DefaultAction
class, that implements the perform method. It looks like this:
 
 
public abstract class DefaultAction
  implements Action, Serializable
{
  /**
   *  0-arg constructor
   */
 
  public DefaultAction()
  {
  }
 
  /**
   * This method is called upon by the
<code>org.apache.struts.action.ActionServlet</code>
   * for every incoming request. Each class that extends this class does not
have to
   * implement this method, but instead provide methods that begin with
   * executeCommandXXX where XXX is the name of what the method does (or any
name if you like).
   * In the action.xml config file, and in JSP pages (forms mostly, but href
links can do this
   * as well) you would specify a request parameter by the name of 'command'
and set it to the
   * name of the method to call. For example, if you have a LoginAction
class that extends
   * DefaultAction (this class), it should contain an
executeCommandDefault() method to do
   * any default action(s). If it is not present, the default method in this
class is called
   * upon instead. Its job in this class is to init the bean (if one is
present), and
   * return "DefaultOK". For other methods, you simply provide the
executeCommandXXX() signature
   * and replace XXX with a name you choose. Just remember that this is the
name used in JSP/HTML
   * pages in forms and links to call upon this specific method in the
action.
   *
   * @param servlet - ActionServlet as defined in the STRUTS framework
   * @param mapping - ActionMapping as defined in the STRUTS framework
   * @param form    - ActionForm as defined in the STRUTS framework
   * @param request - HttpServletRequest passed in from the browser
   * @param response - HttpServletResponse going back to the browser
   *
   * @return ActionForward - the forwarding object that defines the page to
forward to.
   */
   public ActionForward perform(ActionServlet servlet, ActionMapping
mapping, ActionForm form, HttpServletRequest request, HttpServletResponse
response)
     throws IOException, ServletException
   {
     ActionContext context = new ActionContext(servlet, mapping, form,
request, response);
 
     try
     {
       Method method = this.getClass().getMethod("executeCommand" +
context.getCommand(), new Class[] {context.getClass()});
       return mapping.findForward( (String) method.invoke(this, new
Object[]{context}) );
     }
     catch(NoSuchMethodException nm)
     {
       System.out.println("No such method exception. MESSAGE = " +
nm.getMessage() );
     }
     catch(IllegalAccessException ia)
     {
       System.out.println("Illegal argument. MESSAGE = " + ia.getMessage());
     }
     catch(Exception e)
     {
       e.printStackTrace(System.out);
       System.out.println("Exception. MESSAGE = " + e.getMessage());
     }
 
     return null;
   }
 
 
 
  /**
   * This is the default call by all actions if a command= paramter is not
passed along with the
   * request. Descendant classes should override this method to carry out
default behavior that
   * should occure BEFORE the initial JSP page is displayed. This is most
useful when the initial
   * page needs to show a list of items that are populated from the
database, such as a drop-down
   * list of activity for a client, etc.
   */
  public String executeCommandDefault(ActionContext context)
  {
    initBean( context );
    return "DefaultOK";
  }
 
 
 
 
In the above, my DefaultAction is extended by all actions in my application.
They only have to add methods executeCommandXXX(ActionContext context)
 
So, on a form, I have this:
 
<input type="submit" value="ADD"
onclick="this.form.action='/MyAction.do?command=Add'">
<input type="submit" value="DELETE"
onclick='this.form.action='/MyAction.do?command=Delete'">
 
And in the MyAction.java file I would have methods like so:
 
public class MyAction
  extends DefaultAction
{
  public String executeCommandAdd(ActionContext context)
  {
    return "SomeValue";
  }
 
  public String executeCommandDelete(ActionContext context)
  {
    return "AnotherValue";
  }
}
 
in action.xml, the action is set up the same as any other action, with the
values SomeValue and AnotherValue being mapped to jsp pages (in the <forward
name="SomeValue" path="/path/page.jsp" redirect="false" /> syntax).
 
Feel free to use this code if you like. Oh..ActionContext is nothing more
than a class that takes on the parameters passed to perform() and also
provides some helper methods. This way, if we want to pass more parameters
to each action, we simply add them into ActionContext instead. Its a REQUEST
scope object..created at each incoming request for each user, and available
for memory deallocation at the end of the request.
 
Feel free to ask any questions.
 
P.S. I know using Reflection slows things down a bit..but the added benefit
of easy implementation and maintenance of the action classes is more
important to me than overall speed. First, for about $4K you can get a nice
Dell server that can handle 100's, if not 1000's of requests at one time
depending on your J2EE app server. Second, most J2EE app servers allow some
sort of fail-over and scalability where you can add more of them to increase
performance handling (thus, 100's to 1000's of more people per server
added). I was reading an article that said Monster.com has recently set up
literally 400 web servers (I imagine pretty beefy systems too) to handle the
1+ million people a month it gets to its site. If you are expecting 1000's
of people a day, most likely forgoing reflection in your code isn't going to
dramatically improve your sites performance, but adding more servers will.
So I say, and I have heard many others say this too..screw performance (to a
point)..make it good code, maintainable, and easy to implement and throw
more hardware at it if you have to.
 
On the other hand..I am a performance freak, so it took alot to convert me
into this way of thinking..but after coming to work for a company that the
code was solidly UGLY, I realize the value in making the code more
maintainable and easier to work with, even at the cost of a bit of
performance.
 
Just my 2 cents worth on this particular topic. Hope that helps.

Mime
View raw message