Struts 2 and Zero Configuration for Actions and Results

Struts 2 (WebWork on drugs?) comes equipped with some pretty nice features, one of them being Zero Configuration where you don’t have to write any XML configuration or muck around with property files to get your application up and running. You simply specify a path to your action packages and Struts will read the classes, create actions based on the class name and register it with XWork. Once you combine this with the annotation support for action classes which enables you to define results and namespaces, the struts.xml (or should I say xwork.xml) goes out the window.

To start with, you must specify the path your action packages reside in, this can be a comma separated list:

<filter>
  <filter-name>action2</filter-name>
  <filter-class>
  org.apache.struts2.dispatcher.FilterDispatcher
  </filter-class>
  <init-param>
  <param-name>actionPackages</param-name>
  <param-value>
  com.arsenalist.action,com.arsenalist.otheractions
  </param-value>
  </init-param>
</filter>

Here’s an example of how the classes in com.arsenalist.action that extend ActionSupport (or implement Action) get mapped to a URL:

  • LoginAction: Gets mapped to login. Since this class ends with the word Action, Sturts strips Action out of the name and converts it to lowercase. If you don’t want the action to be converted to lowercase, you must set the forceLowerCase to false for the ClasspathConfigurationProvider.
  • SubmitOrder: This gets innocently mapped to submitorder.
  • SubmitActionClass: Since the action is in the middle of the class name and not at the end, this gets mapped to submitactionclass.

So the URL to call the execute() method of the LoginAction class becomes:

http://<hostname&gt;:<port>/login.action

Remember you can call different methods in the action class provided you set the struts.enable.DynamicMethodInvocation property to true in struts.properties. The usual syntax for invoking dynamic methods applies here too, so to call the attemptLogin() method in the LoginAction class the URL will be:

http://<hostname>:<port>/login!attemptLogin.action

Now lets talk about specifying results in the action class via annotations instead of XML.

@Result(name=Action.SUCCESS,
  value="/WEB-INF/pages/login.ftl",
  type=FreemarkerResult.class)
public class LoginAction extends ActionSupport {

  public String execute() {
    return SUCCESS;
  }
  public String attemptLogin() {
    return SUCCESS;
  }
}

You can specify multiple Results using the @Results annotation. You may also specify namespaces for your actions classes using the @Namespace annotation, for example if we had declared @Namespace(“/security”) at the class level in addition to the @Result annotation, the URL to execute LoginAction.execute() would be:

http://<hostname>:<port>/security/login.action

Now if this is too much work for you and you would rather use default results instead of specifying them through annotations, fear not, there is something for you. It’s called the struts2-codebehind-plugin and instead of you specifying paths to results, it will implicitly assume the path to your result in the format of:

pathPrefix/actionName-resultCode.ext

where pathPrefix is specified in struts.xml. So an example path to a result would be /WEB-INF/pages/login-success.ftl. To use this, simply add this JAR to your classpath. Or alternately add the following to your Maven pom.xml:

<dependency>
  <groupId>org.apache.struts</groupId>
  <artifactId>struts2-codebehind-plugin</artifactId>
  <version>2.0.6</version>
</dependency>

You’ll also need to specify the pathPrefix in struts.xml or alternately declare a property in struts.properties.

<constant name="struts.codebehind.pathPrefix"
          value="/WEB-INF/pages/"/>

I don’t really like this method as its too too restrictive in its nature and sometimes the same result can be returned for different actions which would introduce a redundancy in the convention.

Important Note

The one thing struts.xml buys you is having multiple actions map to the same action class. For example, if you wanted to have /doThis.action and /doThat.action map to different methods in the same class, you wouldn’t be able to do it using annotations since they force you to have one action URL per class. In most of the development I’ve done, having one class per action URL added way too many action classes to the application. If I want to process CRUD operations for a user, it would make sense to have only one UserAction class with different action paths (/user/update, /user/delete etc.) which correspond to different methods in UserAction. Of course, you can always use the /actionName!methodName.action syntax to get around this restriction of annotations but once you start integrating something like Sitemesh into your app and want your paths nicely mapped to views, you might end up wishing you had used struts.xml.

Advertisements

2 thoughts on “Struts 2 and Zero Configuration for Actions and Results

  1. Pingback: Struts » Blog Archive » Struts 2 and Zero Configuration for Actions and Results

  2. Pingback: use curl for api documentation - Red Leopard

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