A striking attribute of the framework is the brevity of its signatures. For example:

S1 Action

ActionForward execute(ActionMapping, ActionForm, ServletRequest, ServletResponse)

S2 Action

String execute()

How can this be? How can a key method take no arguments and yet perform useful work within an application?

To keep signatures brief, and methods useful, the framework uses two techniques: Dependency Injection and Thread Local, both of which, in turn, rely on the ActionContext.

Dependency Injection

Many Interceptors are used to populate propertes on an Action. For example, the Servlet Config Interceptor can set Map properties representing the HTTP Request, Session, and Appplication objects.

Though, the signature for an Interceptor is nearly as brief as Action:

String intercept(ActionInvocation invocation)

Looking through ActionInvocation, there are several interesting properties, but nothing that reveals the HTTP contexts. (Good thing! since it is a web-independant XWork class.)

So how does an Interceptor obtain the HTTP contexts to inject?

ThreadLocal

The ThreadLocal class is not a new kid on the block. It's been available to developers since Java 1.2. In effect, each thread has its own copy of the variables on a ThreadLocal class.

The framework uses ThreadLocal in connection with the ActionContext class to make servlet configuration and other runtime details available.

ActionContext

From anywhere within an Struts 2 application, you can obtain a reference to the ActionContext by calling

ActionContext context = ActionContext.getContext();

For example, if a helper class is called from an Action, and if it happens to need access to ServletContext (maybe it is writing a file and needs ServletContext to get a path to it), the helper can obtain the ActionContext directly. Nothing needs to be passed from the Action.

In frameworks like Struts 1, details like the runtime request and response are passed around like hot potatoes. In Struts 2, such details are bundled together in the ActionContext. In S1, Actions are bound to HTTP through the execute signature. In S2, Actions can be a plain old Java object, and each bound to HTTP only to the extent required.

Of course, the dark side of ThreadLocal is that it is easy to bind business logic classes to XWork. An otherwise pristine business class could grab request parameters directly from the ActionContext. The request parameters are exposed as a plain Map, so binding to HTTP is avoided, but the class still becomes bound to XWork. Of course, if you've decided to use XWork as your business logic framework, then binding to ActionContext might not be a problem.

The darker side is that classes that depend heavily on ThreadLocal can be diffcult to unit test. A cleaner design centralizes access to ThreadLocal variables, so that other classes are easier to test.

Back to FAQs


This material originally adopted from http://wiki.apache.org/struts/ActionContext?action=edit.

  • No labels