Roll-Your-Own IoC with Struts

IoC, aka dependency injection, is the current trend. The idea is simple: create your normal class, and rather than going and fetching things you might want (like datasources, factories, and so on), get them given to you instead. This greatly facilitates things like unit testing, because you’ve got a lot less dependencies to set up.

Struts uses Actions to drive an application. This describes a way to use dependency injection techniques with Struts.

Struts is an almost completely pluggable architecture; you can replace pretty much any part of it with something of your own. For IoC, we want to replace the RequestProcessor with our own implementation. In particular, we want to “upgrade” the processActionCreate method.

For my example, I’ve used a very simple IoC technique: if the action that Struts creates for me implements a particular type, then I can call a setter method on it. This is an example of Interface Injection. Here’s the sample code:

public class IoCStrutsController extends TilesRequestProcessor {
  /**
  * Inject a factory into suitable actions. Actions are created as normal for
  * Struts, but if they implement {@link FactoryAware} then they get a factory
  * provided to them.
  *
  
  * TODO: It would probably be a nice improvement to inject the factory only
  * when the action is really created; Struts places actions into a
  * cache after creation the first time.
  * @see TilesRequestProcessor#processActionCreate
  */
  protected Action processActionCreate(HttpServletRequest request,
      HttpServletResponse response, ActionMapping mapping) throws IOException {
    // check for action existence
    String className = mapping.getType();
    Action instance = null;
    synchronized (actions) {
      // Return any existing Action instance of this class
      instance = (Action) actions.get(className);
      if (instance != null) {
        return instance;
      }
      instance = super.processActionCreate(request, response, mapping);
      // We know the instance is brand new.
      if instance instanceof FactoryAware) {
        FactoryAware factoryAwareAction = (FactoryAware) instance
        factoryAwareAction.injectFactory(createFactory());
      }
      return instance;
    }
  }
}

Okay, in this example, I am using the TilesRequestProcessor as a base class, as my application also uses Tiles. Then, whenever Struts creates an Action for me, I give it a factory instance if needed (I create a new factory instance ’cause it’s actually a handle to a Stateless Session Bean; in another context, it would be better to share an instance between the actions).

More sophisticated techniques are possible. At one extreme you can integrate a full-blown IoC environment like Spring fairly easily. However, for projects that don’t want Spring (for whatever reason), a technique like this can help make the various actions easier to code, and remove a lot of lookup code.

Note that you can bring in lifecycle management of the dependencies as well. By overriding the process method on RequestProcessor, you can wrap the whole processing step in a try/finally block to ensure that resources are cleaned up. For example:

/**
* Dispose of the factory resource when the action is finished processing.
* @see TilesRequestProcessor#process(HttpServletRequest, HttpServletResponse)
*/
public void process(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException {
  try {
    super.process(request, response);
  } finally {
    disposeFactory();
  }
}

This helps to move a lot of commonly duplicated code (locate a resource, use a resource, tidy up the resource) into one controlling class, with just the unique bit (use a resource) remaining in each action.

Note that the motivator here, at least for me, isn’t unit testing. The unit test frameworks around Struts Actions (such as StrutsTest are a bit higher up than unit tests, in that they work off a full Struts configuration. (Hint: if it needs your struts_config.xml file, then it isn’t unit testing!). The motivator is more about simplifiying the Action development and removing duplication.


Updated: The code example now checks to see if the instance already exists, and only injects the dependency for new Actions. Obviously, in this case, you wouldn’t want to dispose afterwards. See my follow-up post for more on 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.

2 thoughts on “Roll-Your-Own IoC with Struts”

  1. Extending RequestProcessor or TilesRequestProcessor is something that is under utilized in Struts apps. Your idea is cool! Although I think you would be better off talking management into using full blown Spring, and even Spring MVC.

  2. At the time, I was working for a mid-size bank – they were very conservative about adopting new technologies, and Spring was not a true option.

    These days, I use WebWork and Spring extensively. I also have a different employer.

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