Struts: avoiding some common problems
daniel bray, 11th May 2003

A lot of what I do is web-applications, and the framework for serving up these web-applications that I've chosen has been tomcat and struts: tomcat serves up the pages; struts organizes and controls the flow of the web-app. This essay is about struts, and how to avoid some of the pitfalls that a novice can fall into.

Okay, I'll be honest: avoiding the pitfalls that I fell into.

I'm not going to go into the whole strut's ouevre, there are hundreds of sites out there that desrcibe the whole ins and outs of the frame work, what I am going to do is give you some advice. This is really most useful if you're about to start, or have only just started, to implement your web-app. If you've been hacking away at it for a while now, then it'll be less useful, if only because the changes that you'd to have to make to follow this guideline are liable to be HUGE, and perhaps not worth it immediately, but even still, there are a few ideas here that will probably appeal to you, if for no ther reason than they centralise code and you've probably spent ages fixing the same bug all over the place.


I'm new to this whole essay-writing thing, so I'd be delighted to receive any comments, additions or clarifications you have on any of this. I live at daniel@braindelay.com.

I've written some more of these: thay can be found at danielbray.com.



Define a base Action for your application.

Struts comes with a base action, org.apache.struts.action.Action that you can extend to define all the actions you need in your web-application. What I'm advising is that the first thing you do when you're writing your web-app is to extend from this Action your own base action and then extend all of your web-app's actions from this. Even if initially you have nothing to generalize you should do it.

I can hear you thinking: Whaaaat? Are you seriously suggesting that I extend a class for no reason? This is the problem with you object-oriented guys: indirection all over the place. I know that's what it looks like, but you are going to find both business and navigation functionality that will need to be centralised at some time. The business code can, of course, be generalized as you see fit, but the code concerning navigation is system-wide and has to be put into one place, and when, not if, you discover that you have some kind of system-wide navigation bug to fix you're not going to want to retro-fit a hundred or so actions to make them extend your new web-app action.

I know I didn't, but I had to, and trust me: it's painful. Since we've never met, I'm going to assume that you don't trust me and still need some convincing, so here's an example, and it has to do with something that you'd think struts would do, but doesn't.

Protection from multiple form submits

Software users aren't like coders: they aren't particularly willing to accept that computers can be slow; and this really shows itself in web-apps. When a user hits some button on their browsers and the page is taking a bit too long to load, a user can almost never resist the temptation to keep hitting the button, to "speed it up," in much the same way that you'll see someone at a pedestrian crossing hitting the traffic light button repeatedly in a vain effort to affect the traffic system.

Usually what happens when a user submits a form twice in a browser all hell breaks loose, and all manner of faults, from wasted server activity to broken database constraint, come to the surface... and usually come to the surface in the form of a rather ugly stack trace.

Now, struts' Action class does have functionality to implement this protection, but it has to be defined programatically in your web-apps actions, and can't be defined in the struts-config. Again, you're probably thinking: So what? I still don't see a need for a single base action class for my web-app: all I need to do is define some class that implements this protection and extend the actions that need it from this base. Well, patience grasshopper... it will all become clear soon enough. First I'll say how this protection functionality in org.apache.struts.action.Action works.

It's split up into two parts, one on the client, and one on the server:

Strut's Action class contains the following operations to do this:

... and that's it. It's nice and simple, all you need to do is call setToken() in the action that renders your "menu" page, and call validateToken() in the action that executes the event that you want to protect; piece of cake, really. The problem isn't how to protect single actions 'though; the problem is how to protect single actions throughout the entire web-app. The web-app I'm working on right now has over a hundred and fifty actions, so you can bet your bottom dollar that I'm not writing this into every action.

So you can see a need now for generalising code like this into a single base class, I hope. In case you're still thinking that you can get away with only extending actions that need this protection from this base, consider that in the real world, because requirements tend to change on a whim, the implementation of a piece of software needs to be completely malleable. When your product manager decides that he wants some new functionality to be added to the application and that, as a result, a big chunk of layout of the site-map needs to be changed, you're not going to want to root through your code and change where your actions exist within the class hierarchy, just because you want to switch on, or off, some navigation functionality... do you? Now, this is where the real beauty of having a single application action comes into play.

Allowing for application-level functionality to be activated by the struts-config

Like I said, you'd think that a requirement as common as multiple form submission protection would be an integral part of the declarative parts of struts, and not something that needs to be coded programatically, but it's not. Well, not yet anyway, and until it is, you're going to have to cope with it as best you can, and the best way to do it is to implement this code programatically once in a base web-app action of your own, and have the execution of this code be optional on the presence of something that can be defined declaratively.

Words like "declaratively" shouldn't be bandied about too lightly, so I think another example is needed. Back to our multiple form submission protection problem. What you'd want to do is define a base action like this:

abstract public class WebAppAction extends Action
{
  /**
  Here's the magic, what this operation's method does is take a peek at this
  action's action mapping to see if there's a forward called CONTROL_isViewAction,
  and returning true if there is.

  This action mapping is defined by the struts-config, and so whether this
  action is a "view" action or not, can be defined declaratively,
  the struts-config. I've put the big CONTROL at the beginning of the forward
  so that nobody will confuse it with other forwards that actually manage the
  navigation of the web-app.
  */
  private boolean isViewAction(ActionMapping mapping)
  {
    return null != mapping.findForward("CONTROL_isViewAction");
  }

  /**
  This is the same as isViewAction()
  */
  private boolean isEventAction(ActionMapping mapping)
  {
    return null != mapping.findForward("CONTROL_isEventAction");
  }

  /**
  What this guy does is tests whether or not this is an "view" action, and
  if it is, it sets the token in the session so that when the following "Event"
  action is executed it won't fail.
  */
  public ActionForward doPostCondition(ActionMapping mapping, ActionForm form, HttpServletRequest request)
  {
   if (isViewAction(mapping))
   {
    setToken(request)
   }
   return ...;   // the "successful" action forward
  }

  /**
  What this guy does is tests whether or not this is an "event" action, and
  if it is, it checks to see if it has been executed already. If it has it will
  return an appropriately alarming ActionForward.
  */
  public ActionForward doPreCondition(ActionMapping mapping, ActionForm form, HttpServletRequest request)
  {
   if (isEventAction(mapping))
   {
    if (false == validateToken(request))
    {
     return new ActionForward("INVALID_TOKEN");
    }
    resetToken();  // subsequent calls musn't find the token in the session
   }
   return ...;   // the "successful" action forward
  }

  /**
  This class is an example of a "Template" design pattern. What you'd do when you
  extend it is you'd implement this operation with the method that you'd normally
  have used for the action's "execute" operation.

  See, Design patterns: Elements of reusable object-oriented softwarew, by Erich Gamma et al. Addison Wesley
  */
  abstract public ActionForward doTask(ActionMapping mapping, ActionForm form, HttpServletRequest request);

  /**
  And this is your execute operation. As you can see, it implements the multiple
  form submission protection that we needed and that this protection can be
  switched on, or off, for very specific actions, without the need to recompile,
  or redefine the code.
  */
  public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request)
  {
   ActionForward success = ...;
   try
   {
    ActionForward conditionResult = doPreCondition(mapping, form, request);
    if (false == success.equals(conditionResult))
    {
     return conditionResult;
    }
    return doTask(mapping, form, request);
   }
   finally
   {
    ActionForward conditionResult = doPostCondition(mapping, form, request);
    if (false == success.equals(conditionResult))
    {
     return conditionResult;
    }
   }
  }
}

I've you've skipped over that code sample, you'll probably want to go back and skoot through it, since all the explanation is in it, if you've read it and you're still not convinced then there's nothing else I can do for you, except suggest that you buy a very comfortable seat and one of those ergonomic keyboards 'cause you're going to be doing an awful lot of needless typing.

The fact of the natter is that after a very short time there'll end up being a ton of functionality that will be I everywhere in your web-app: stuff that's going to be almost everywhere, like breadcrumbs, certain logging and database handling, the handling of business design contracts, etc., that you're going to want to be in one place; also there'll be stuff like this multiple form submission protection that you're going to want to be easily switched on and off.

Either way, you're not going to be able to do this without extending your actions from a single base, or at least, a single hierarchy. You can try if you want to but you'll only be making it hard for yourself.

Finally, in case you thought I was just going to toss that "at least, a single hierarchy" into that last paragraph and ignore it: I'm not denying that there'll be sections of the code where there'll be functionality that only makes sense in that function area, and in this case you'd create a separate subclass to generalize this behaviour; but that subclass will have to be a subclass of your web-app action... unless you fancy rewriting all of this code all over again.


Define a base Form for your application.

If I haven't persuaded you to extend your actions from a single base action of your own making, I'm not going to convince you that you need to do this for your forms as well; but you do.

It's less of a stroke-inducing mistake to extend all of your forms directly from org.apache.struts.form.ActionForm, but you're only making work for yourself, and unless you're in some kind of trade union that's usually not a good thing.

If you create your own web-app form then you can do things like:


Always navigate through a form submit.

The mechanics of struts is founded on HTML forms being submitted, although it's possible, and I have to admit that it's so very much easier, to link directly to the action mappings you really shouldn't do this.

I'm sorry if I shouted there, but I usen't to mind so much, but now I'm starting to get militant about this. Everything that makes struts good is founded on its correct use, and, like I said, its correct use is form submissions. This needs an example, so, again, I'll go back to my multiple form submission protection problem.

If you remember, this works by placing a token in the session so that when a form is rendered by the struts taglb tag <html:form>, it places that token into a hidden form parameter. When this form is submitted the action can look at the form parameter and if the token is there and it matches the session's token then all is well.

If I sounded a bit testy, I'm sorry, but I really want this to sink in, so I'll say it again: this works by placing a token in the session so that when a form is rendered by the struts taglb tag <html:form>, it places that token into a hidden form parameter.

There are two things to take note of here:

  1. a form is rendered by the struts taglb tag <html:form>
    You have to use the struts taglib for any form that you're going to use to execute an action. Struts can do an awful lot of things with its forms, but only if it knows that you're using it; if you use the simplt HTML <form> tag then struts can't prepare the form so it won't know how to cope with it: the magical pixie dust will fail.
  2. it places that token into a hidden form parameter
    This should make things clear: if the token, or any other species of pixie dust, is placed as a hidden paramter into a form, if you expect struts to get this value then you have to submit the form, 'cause struts isn't going to be able to guess; it's not Uri Gellar.

I know what you're thinking, you're thinking: nice try, but a HTML form can only have one submit button in it, and my GUI has dozens of pages that have two or three buttons; some even have tables with two or three buttons per row: how can I make them all submit the form? Settle down, boys: I wouldn't have said you should do something unless I knew you could actually do it; and here's how:

Multiple submit buttons on a page: the relay action

This is doable, and it's pretty easy, it can just get a little finicky the first time you try. It's a solution in three parts:

  1. Write a "relay" action that will execute different actions depending on the value of a parameter.
  2. Define, in the struts config an action mapping that will be the "menu" for the pag your rendering.
  3. Write your JSP so that the only way from the page it's rednering is via a form submission.

The relay action

This is a bit like a struts DispatchAction, only a little bit easier. It only needs to be written one, you'll be glad to know and it can be reused for every action "menu." It also has the added bonus that it can be used to refactor old code that uses HTML links to directly call actions without the need to rewrite those actions at all.

It works simple enough by looking for a given parameter and forwarding to the action forward it named, the code looks like this:

public class RelayAction extends Action
{
  /**
  Very simple really, it looks for an "operation" parameter and uses it to find
  a forward in the mapping and then uses it.
  */
  public static ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServlerResponse response)
  {
    String operation = request.getParameter("operation");
    if (false == isDefined(operation)
    {
      return new ActionForward("error");
    }

    String forward = mapping.findForward(operation);
    if (false == isDefined(forward)
    {
      return new ActionForward("error");
    }

    return new ActionForward(forward);
  }
}

The action mapping

Now, to make this rather generalized RelayAction useful. This is done in the struts-config. This example would be the menu action for the following sample JSP.

<action-mapping
  path = "/webapp/subapp/monkeyActionMenu"
  type = "com.braindelay.struts.action.RelayAction"
  forwards>
  <forwards>
    <forward name = "create" path = "/webapp/subapp/createMonkeyAction" />
    <forward name = "delete" path = "/webapp/subapp/deleteMonkeyAction" />
  </forwards>
</action-mapping>

There's nothing too complicated here:

The link-free JSP

This is best explained with an example. Here there are two buttons, neither of which are "natural" submit buttons, nut which both define an onsubmit action; the functions that they call set any parameters that need setting and then call a submitFrom operation with an operation parameter: this parameter is the key.

If you look at the form, a hidden operation parameter is defined at the end; now compare this with the implemetation of the RelayAction: it is this operation parameter that the RelayAction is looking for as a key to determine whcih forward to go to. So all this submitForm is doing is setting this hidden parameter to the appropriate value and submitting the form: easy as pie.


<%-- suppose this action is using a form called monkeyForm --%>
<html:form path = "/webapp/subapp/monkeyActionMenu">
  <script type = "text/javascript">
  <!--
  // this is the internet explorer version, but do something like this
  // for the browsers you're supporting
  function submitForm(operation)
  {
    document.monkeyForm.element['operation'].value = operation;
    document.monkeyForm.submit;
  }

  function createMonkey()
  {
   // set the parameters for creating the monkey - these will
   // also be hidden
   setCreateParameters();
   submitForm('create');
  }

  function deleteMonkey()
  {
   // set the parameters for deleting the monkey - these will
   // also be hidden
   setDeleteParameters();
   submitForm('delete');
  }
  -->
  </script>

   <form:hidden name = "operation" value = "error" />
   <form:button styleId = "Create" onclick = "createMonkey()" />
   <form:button styleId = "Delete" onclick = "deleteMonkey()" />
</html:form>

If you absolutely, positively, must have links in your page, but you still want this multiple form submission lark that we mentioned before; you can set the token in the request (in JSPs only in one of the following two ways:

One last thing: DOM already places a create operation on some of its elements, so you can't have a javascript function in your JSP called create 'cause it simply won't be called. This hasn't anything to do with always using forms, it's just a problem that took me ages to find and I figured I'd warn you about it.

This looks like an awful lot of work

Well, I won't lie to you: compared to just linking directly, going through a form submission is, but it's a minor inconvenience when you consider how much control over your web-app you can leverage from struts if you always work with its protocols rather than slide past them.

Another real bonus of always navigating through the GUI with form submissions is that your entire site map and navigation trail will be defined in the struts-config, and not, as will otherwise be the case, partly in the struts-config and mostly ('cause we're all that lazy) defined in a completely unstructured and invisible way in a hundred or so JSPs... try figuring all that out. It's just so much nicer to be able to fire a XSL stylesheet at your struts-config to see what kind of mess you've gotten yourself into.


Avoid writing forms that are only rows in a database.

This is less a struts problem, and more of a GUI problem in general: that of defining a GUI that merely mimics the model; but this is its struts manifestation, and if you see it happening in your web-app then it's time to start screaming ICEBERG... but like the Titanic, you're probably too late to save yourself.

The problem here is that if you've defined a form that's a row in your database then it's almost certain that you haven't defined your form as matching what the user thinks the underlying concept is. Let's be honest, GUI coders are seen as pastry chefs, and it's rare indeed that they've given an API for the model that isn't just a set of SQL queries disguised as operations... sometimes they're even put into things called beans, that aren't beans, just to lull you into a false sense of security.

Jeez, that was bitter...

Anyway, when your forms are rows in the database, then, whenever your users have to play about with any kind of even slightly complex GUI element, chances are they're going to be forced to submit a load of different pages just to edit one thing, and they're not going to understand, or care, why.


Use an exception handler

It's a sad fact in web-apps that if an exception is thrown up as far as the presentation layer, then there usually isn't very much that you can do about it. Usually, you'll see code all over the place that catches all manner of exceptions that you cannot possibly fix, just for the case of logging them and setting some flag so that a presentable error page is shown to the user.

You know, this sort of thing:

public class DoItAction extends Action
{
  private ActionForward doItToIt()
  throws IOException, SQLException, ApplicationException
  {...};

  public ActionForward execute(...) throws Exception
  {
    try
    {
      // whatever needs doing
      try
      {
        return doItToIt();
      }
      catch (FixableException hmm)
      {
        return trySomethingElse();
      }
      return new ActionForward("ERROR");
    }
    catch (IOException badIO)
    {
      errors.add("exception.io", new ExceptionActionError(badSpecificTask));
      return new ActionForward("ERROR");
    }
    catch (SQLException badSQL)
    {
      errors.add("exception.sql", new ExceptionActionError(badSQL));
      return new ActionForward("ERROR");
    }
    catch (ApplicationException badSpecificTask)
    {
      errors.add("exception.application", new ExceptionActionError(badSpecificTask));
      return new ActionForward("ERROR");
    }
    finally
    {
      closeDatabaseConnections();
      clearOutSession();
      closeLogger();
            
      // Report any errors we have discovered back to the original form
      if (!errors.empty())
      {
        saveErrors(request, errors);
      }
    }
  }
}

The problem here is that we're in an action, and up here these isn't really much we can do with these exceptions except tell the user that something's gone wrong; and the user is never going to want to know that there's been some SQL error, so ten times out of ten the error page is only going to say something like

Something broke: talk to your sysadmin.

This code is fairly awkward, and it does tend to clutter up what's going on in the action. You may be thinking that we could just catch one java.lang.Exception and be done with it in one place, but that kind of catch all can be fairly heartbreaking as it tends to swallow up an awful lot of problems that would normally be caught in development unit tests.

Thankfully struts has a fairly natty solution, and it all comes down to that Exception that is permitted to be thrown by the org.apache.struts.action.Action.execute(..) operation, and it's a solution that comes in three parts.

  1. Your action classes that only ever catch exceptions that they're actually going to be able to fix.
  2. A fairly swanky struts construct called an ExceptionHandler.
  3. A standard JSP for rendering errors.

As an example, we'll take the action above, and remove all the exception that we weren't doing anything with.

public class DoItAction extends Action
{
  private ActionForward doItToIt()
  throws IOException, SQLException, ApplicationException
  {...};

  public ActionForward execute(...) throws Exception
  {
    try
    {
      // whatever needs doing
      try
      {
        return doItToIt();
      }
      catch (FixableException hmm)
      {
        return trySomethingElse();
      }
      return new ActionForward("ERROR");
    }
    finally
    {
      closeDatabaseConnections();
      clearOutSession();
      closeLogger();
    }
  }
}

Much simpler, no? Then we define out ExceptionHandler. This can be viewed as a special kind of action that gets called whenever an exception is thrown by an action; it will do whatever cleaning up or logging it needs to and will then forward to an appropriate page.

ExceptionHandler might look like this:

public class ExceptionHandler extends org.apache.struts.action.ExceptionHandler
{

 private String getFailedRequest(HttpServletRequest request)
 {
   return "Error executing action : " + request.getRequestURI() +"?"+ request.getQueryString()
 }
 private String getInterestingStackTrace(Exception exception)
 {
	 return // filter out the bits we don't want
 }
  /**
   * @see org.apache.struts.action.ExceptionHandler
   */
  public ActionForward execute(
  		Exception exception, ExceptionConfig config,
  		ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response)
        throws ServletException
  {
    Logger logger = LoggerFactory.createLogger(request);
    String userMessage = null;
    // if we can log this error somehow, then create a unique reference code
    // for it, and onclude it in the error message so that the user's problem
    // can be solved quicker.
    if (logger.isLoggable(Level.SEVERE))
    {
	    logger.log(Level.SEVERE, referenceCode, getFailedRequest(request));
	    logger.log(Level.SEVERE, referenceCode, getInterestingStackTrace(exception));

      // this UID will be unique on the server
      String referenceCode = new java.rmi.server.UID().toString();
      userMessage = "An internal error has occured:"
      	+ " please inform the system administrator"
        + " (with this reference:"+referenceCode+")";
    }
    else
    {
      userMessage = "An internal error has occured:"
        +" please inform the system administrator.";
    }

    // get any existing errors from the session, if there are any. This
    // long string can be found in the struts "Action" class
	  ActionErrors  errors  = null;
    if (null == request.setAttribute("org.apache.struts.action.ERROR"))
    {
      errors = new ActionErrors();
    }
    else
    {
      errors = (ActionErrors)request.getAttribute("org.apache.struts.action.ERROR");
    }

    // add the new error, and resubmit the errors list to the session.
    errors.add(exception.getClass().getName(), new ActionError(userMessage));
    request.setAttribute("org.apache.struts.action.ERROR", errors);

    // forward to the apprpriare errors page
    return mapping.findForward(Constants.FORWARD_ERROR);
  }



}
<global-exceptions>
  <exception type="java.lang.Exception" key="errors.heading"
      handler="com.braindelay.ExceptionHandler"
      path="/errors.jsp"
      scope="request" />
</global-exceptions>

Maintain your struts-config centrally.

This is a quick one, and if I were being honest I'd have to admit that it's more of a personal preference, so I'll pass this off ad advice, and not a warning.

With something like xdoclets and a preprocessor you can define the bits of your struts-config that are defining action-mappings for a specific action in the comments of that action. Architects and project managers love this kind of thing, 'cause at first glance it looks like a good thing: it might make coders actually update the rest of the comments if there are some comments that they have to maintain; also it will automatically remove action-mappings that are no longer in use - to be honest, these do tend to hang around for ages after they've been removed from the web-app. Note that I said at first glance, because that's all architects and project managers will do with the code, give it one glance: they don't have to work with the struts-config and these actions on a daily basis, so even if they know how sthey work, they sure as hell don't know how they're used.

The basic problem, in my opinion, with defining the struts-config piecemeal, is this: action-mappings are almost never edited on its own. Normally they're edited in relation to the other actions in its funtional area and it's going to drive you insane if you have to keep flicking back and forth between class files to see if a dozen or so actions are working in synch.


danielbray:essays:strutsTopTips
click here for log