Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

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

S1 Action

Code Block
ActionForward execute(ActionMapping, ActionForm, ServletRequest, ServletResponse)

S2 Action

Code Block
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:

Code Block
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

One of the handier feature in Webwork, in my limited experience, is the ActionContext object. This is a ThreaLocal object that is a wrapper around many common objects (Request, Response, ServletContext, etc.).

The thing that makes it so handy is that you can get a reference to it from any class at any time using:

Code Block
ActionContext context = ActionContext.getContext();

Think about that for a moment... you can get it from '''any''' class at '''any'''. This means that you can have a helper class 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), you can do so, you '''do not''' have to explicitly pass that information to the helper.

Two other important things about this is that (a) you should always be able to go to the same place to get the information you need, the !ActionContext, as opposed to Struts where it is scattered across the Request, Response, !ActionMapping, etc. objects, and (b) your Actions can be POJOs because there are no requirements for any method to have a specific signature. Think of the method signature of the Struts Action class:

Code Block

ActionForward execute(ActionMapping, ActionForm, ServletRequest, ServletResponse) 

In Webwork, no parameters are passed to the called method (whether the default execute() method or one you define in the Action mapping) because you can get at the same information via ActionContext within the method.

There is, however, one potential negative here. You have to be careful that you resist the temptation to write your business delegates to use this feature. In other words, generally-accepted "good" architecture says that your Actions should be very lightweight and should call on classes that perform your "business logic". You may be tempted, because it is so very easy, to grab request parameters directly from within those classes for instance. This is something you probably will want to avoid as it does, in a way, couple those business delegates to the webapp.

I said "in a way" there because the ActionContext actually abstracts away some of the web-centric objects. For instance, you can call getParameters() on ActionContext and you will get a Map back. This is of course a generic collection, not something web-centric, so in a sense that is "safe". Still, my feeling is this is one potential gotcha to at least be aware of.

That concern aside though, there is little doubt in my mind at least that the ActionContext, because of the fact that it is ThreadLocal, makes for cleaner, more generic code throughout your application, and that can only be a good thing!

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.