Follow-up on IoC with Struts

Don Brown made a very good point about a gotcha with the IoC technique I demonstrated with Struts: it’s potentially not threadsafe.

For me, that wasn’t a problem: the dependencies I inject are threadsafe (and are because Struts Actions aren’t). However, in general, it is certainly worth remembering that Struts Actions are potentially invoked by multiple threads at once. This means, for example, that you can’t store data in private variables nicely.

One way to get around this is the java.lang.ThreadLocal class; this lets you store instance variables on a per-thread basis, which is really kinda nifty.

For another way to get around it… well, it was the actionCreate method I demonstrated overriding, after all. While I personally don’t recommend doing this, here’s a way to get Struts to create a new Action class:

/**
* Return an Action instance that will be used to process
* the current request, creating a new one all the time.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param mapping The mapping we are using
*
* @exception IOException if an input/output error occurs
*/
protected Action processActionCreate(HttpServletRequest request,
      HttpServletResponse response, ActionMapping mapping)
      throws IOException {
  // Acquire the Action instance we will be using (if there is one)
  String className = mapping.getType();
  if (log.isDebugEnabled()) {
    log.debug(" Looking for Action instance for class " + className);
  }
  Action instance = null;
  // Create and return a new Action instance
  if (log.isTraceEnabled()) {
    log.trace("  Creating new Action instance");
  }
  try {
    instance = (Action) RequestUtils.applicationInstance(className);
    // TODO Maybe we should propagate this exception instead of returning
    // null.
  } catch (Exception e) {
    log.error(getInternal().getMessage("actionCreate", mapping.getPath()), e);
    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
    getInternal().getMessage("actionCreate", mapping.getPath()));
    return (null);
  }
  instance.setServlet(this.servlet);
  actions.put(className, instance);
  return (instance);
}

This code (which is the same Struts code just with the caching stripped out) will give you a new instance each time. Now, like I said, I don’t recommend this, particularly if you’re doing it to make the lives of junior developers easier… they’ll get used to the idea that they can keep variables in their action classes, and when they go onto another project (that doesn’t have this hack), they’ll be letting bugs escape into the codebase. But each to their own.

For a third way of getting around this problem, we can simply check for the action existence before injecting the dependency. I’ve updated my earlier post for this.

Advertisements

Author: Robert Watkins

My name is Robert Watkins. I am a software developer and have been for over 18 years now. I currently work for people, but my opinions here are in no way endorsed by them (which is cool; their opinions aren’t endorsed by me either). My main professional interests are in Java development, using Agile methods, with a historical focus on building web based applications. I’m also a Mac-fan and love my iPhone, which I’m currently learning how to code for. I live and work in Brisbane, Australia, but I grew up in the Northern Territory, and still find Brisbane too cold (after 16 years here). I’m married, with two children and one cat. My politics are socialist in tendency, my religious affiliation is atheist (aka “none of the above”), my attitude is condescending and my moral standing is lying down.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s