Java Deep-Dive: Fun with Struts 2

David Gorsline
Technology at NPR
Published in
3 min readMay 23, 2016

--

Last year, the team working on improving NPR’s Content Management System (CMS), called Seamus, built its first application using the Apache Struts 2 framework. It was actually a migration from a Struts 1 application, so we had a substantial suite of actions that were already coded, organized into modules. The Struts 2 package concept handles our modules well.

We have standard logic that we want to execute for the invocation of any action (authentication, logging, timing, etc.), so we recoded our Struts 1 filters as interceptors.

Then there is exception handling. It took me several readings of the documentation and various forum postings and some trial and error to get this working the way we need it, including being able to catch exceptions thrown by interceptors. I ended up deciding to pass the exception info directly to a view JSP, rather than an action class, but we might one day revisit that decision and do some refactoring.

The docs explain how to configure a handler to be applied globally to the entire application, and how to configure an action-specific handler. But in our case, we have one module that consists of actions that respond to Ajax requests, and hence render JSON, while the other modules render complete web pages with JSPs. The problem statement: how can you configure a Struts 2 global exception handler with the <global-exception-mapping> tag that will apply to a specific package, but will share the same interceptor stack with the rest of the application? For actions in our JSON module, we want error info returned as a JSON payload, not HTML.

Well, it turns out that you can do it, but you have to be a little creative with the package inheritance hierarchy. What follows is a simplified version of our actual application, so I can’t vouch that this code is complete and accurate, but it should give you the general idea.

The key is to make a package just for the interceptors. Then every other package extends that package. The core package defines exception handlers and global results that all packages use (here, alpha, bravo, and charlie), except the json package, which defines its own.

Here’s the struts.xml, slightly anonymized (again, in our production solution, every package is defined in a separate file that is brought in with <include>, but it’s easier to see what’s going on with everything in one place):

A couple of things to point out:

  • Notice that we use <global-results> to account for results returned by interceptors, as well as the exception handling mechanism.
  • I consider it a blot that you have to define <global-results> in two places, in both the core and json packages, but that’s apparently what you have to do.
  • I recommend creating a tracer interceptor as part of your tool kit. All that it does is log a message (here, set as a configurable parameter). Writing the code is a good tutorial for creating your first interceptor from scratch, and once you’ve got it, you can use it to debug your interceptor stacks. I’ve only shown one stack in this example, but when your requirements call for alternative stacks, it’s good to know that you’re executing the one you want.

Originally published, in somewhat different form, at iefbr14.blogspot.com on 1 July 2015.

--

--