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.