Struts2: Actions and Results

Java

Action Classes
Every operation that an application can perform is referred to as an action. Displaying a Login form, for example, is an action. So is saving a product’s details. Creating actions is the most important task in Struts application development. Some actions are as simple as forwarding to a JSP. Others perform logic that needs to be written in action classes.
An action class is an ordinary Java class. It may have properties and methods and must comply with these rules.
• A property must have a get and a set methods. Action property names follow the same rules as JavaBeans property names. A property can be of any type, not only String. Data conversion from String to non-String happens automatically.
• An action class must have a no-argument constructor. If you don’t have a constructor in your action class, the Java compiler will create a no-argument constructor for you. However, if you have a constructor that takes one or more arguments, you must write a no-argument constructor. Or else, Struts will not be able to instantiate the class.
• An action class must have at least one method that will be invoked when the action is called.
• An action class may be associated with multiple actions. In this case, the action class may provide a different method for each action. For example, a User action class may have loginand logout methods that are mapped to the User_login and User_logout actions, respectively.
• Since Struts 2, unlike Struts 1, creates a new action instance for every HTTP request, an action class does not have to be thread safe.
• Struts 2, unlike Struts 1, by default does not create an HttpSession object. However, a JSP does. Therefore, if you want a completely session free action, add this to the top of all your JSPs:
90
<%@page session=”false”%>
The Employee class below is an action class. It has four properties (firstName, lastName, birthDate, and emails) and one method (register).

package app03a;
import java.util.Collection;
import java.util.Date;

public class Employee {
private String firstName;
private String lastName;
private Date birthDate;
private Collection emails;

public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public Collection getEmails() {
return emails;
}
public void setEmails(Collection emails) {
this.emails = emails;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}

public String register() {

// do something here
return “success”;
}
}

As you can see above, an action class does not have to extend a certain parent class or implement an interface. Having said that, most of your action classes will implement thecom.opensymphony.xwork2.Action interface indirectly by extending a convenience class named ActionSupport.
If you implement Action, you will inherit the following static fields:
• SUCCESS. Indicates that the action execution was successful and the result view should be shown to the user.
• NONE. Indicates that the action execution was successful but no result view should be shown to the user.
• ERROR. Indicates that that action execution failed and an error view should be sent to the user.
• INPUT. Indicates that input validation failed and the form that had been used to take user input should be shown again.
• LOGIN. Indicates that the action could not execute because the user was not logged in and the login view should be shown.
You need to know the values of these static fields as you will use the values when configuring results. Here they are.

public static final String SUCCESS = “success”;
public static final String NONE = “none”;
public static final String ERROR = “error”;
public static final String INPUT = “input”;
public static final String LOGIN = “login”;

Accessing Resources
From an action class, you can access resources such as the ServletContext, HttpSession, HttpServletRequest, and HttpServletResponse objects either through theServletActionContext object or by implementing Aware interfaces. The latter is an implementation of dependency injection and is the recommended way as it will make your action classes easier to test.
This section discusses the techniques to access the resources.
The ServletActionContext Object
There are two classes that provide access to the aforementioned resources, com.opensymphony.xwork2.ActionContext and org.apache.struts2.ServletActionContext. The latter wraps the former and is the easier to use between the two. ServletActionContext provides the following static methods that you will often use in your career as a Struts developer. Here are some of them.

public static javax.servlet.http.HttpServletRequest getRequest()

Returns the current HttpServletRequest.

public static javax.servlet.http.HttpServletResponse getResponse()

Returns the current HttpServletResponse object.

public static javax.servlet.ServletContext getServletContext()

Returns the ServletContext object.

You can obtain the HttpSession object by calling one of the getSession methods on the HttpServletRequest object. The HttpSession object will be created automatically if you use thebasicStack or defaultStack interceptor stack.
Note
You should not call the methods on the ServletActionContext from an action class’s constructor because at this stage the underlying ActionContext object has not been passed to it. Calling ServletActionContext.getServletContext from an action’s constructor will return null.

As an example,below shows an action method that retrieves the HttpServletRequest and HttpSession objects through ServletActionContext.

public String execute() {
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
if (session.getAttribute(“user”) == null) {
return LOGIN;
} else {
// do something
return SUCCESS;
}
}

Aware Interfaces
Struts provides four interfaces that you can implement to get access to the ServletContext, HttpServletRequest, HttpServletResponse, and HttpSession objects, respectively: The interfaces are

• org.apache.struts2.util.ServletContextAware
• org.apache.struts2.interceptor.ServletRequestAware
• org.apache.struts2.interceptor.ServletResponseAware
• org.apache.struts2.interceptor.SessionAware

I discuss these interfaces in the following subsections and provide an example of an action that implements these interfaces in the next section.
ServletContextAware
You implement the ServletContextAware interface if you need access to the ServletContext object from within your action class. The interface has one method, setServletContext, whose signature is as follows.

void setServletContext(javax.servlet.ServletContext servletContext)

When an action is invoked, Struts will examine if the associated action class implements ServletContextAware. If it does, Struts will call the action’s setServletContext method and pass the ServletContext object prior to populating the action properties and executing the action method. In your setServletContext method you need to assign the ServletContext object to a class variable. Like this.

private ServletContext servletContext;
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}

You can then access the ServletContext object from any point in your action class through the servletContext variable.
ServletRequestAware
This interface has a setServletRequest method whose signature is as follows.

void setServletRequest(javax.servlet.http.HttpServletRequest
servletRequest)

Implementing ServletRequestAware allows you access to the HttpServletRequest object from within your action class. When an action is invoked, Struts checks to see if the action class implements this interface and, if it does, calls its setServletRequest method, passing the current HttpServletRequest object. Struts does this before it populates the action properties and before it executes the action method.
In the implementation of the setServletRequest method, you need to assign the passed HttpServletRequest object to a class variable:

private HttpServletRequest servletRequest;
public void setServletRequest(HttpServletRequest servletRequest) {
this.servletRequest = servletRequest;
}

Now you can access the HttpServletRequest object via the servletRequest reference.
ServletResponseAware
The setServletResponse method is the only method defined in ServletResponseAware. Here is its signature.

void setServletResponse(javax.servlet.http.HttpServletResponse
servletResponse)

Implement this interface if you need to access the HttpServletResponse object from your action class. When an action is invoked, Struts checks to see if the action class implementsServletResponseAware. If it does, Struts calls its setServletResponse method passing the current HttpServletResponse object. You need to assign the passed object to a class variable. Here is an example of how to do it.

private HttpServletResponse servletResponse;
public void setServletResponse(HttpServletResponse
servletResponse) {
this.servletResponse = servletResponse;
}

You can now access the HttpServletResponse object via the servletResponse variable.

SessionAware
If you need access to the HttpSession object from within your action class, implementing the SessionAware interface is the way to go. The SessionAware interface is a little different from its three other counterparts discussed earlier. Implementing SessionAware does not give you the current HttpSession instance but a java.util.Map. This may be confusing at first, but let’s take a closer look at the SessionAware interface.
This interface only has one method, setSession, whose signature is this.

void setSession(java.util.Map map)

In an implementing setSession method you assign the Map to a class variable:

private Map session;
void setSession(Map map) {
this.session = map;
}

Struts will call the setSession method of an implementing action class when the action is invoked. Upon doing so, Struts will pass an instance oforg.apache.struts2.dispatcher.SessionMap, which extends java.util.AbstractMap, which in turn implements java.util.Map.SessionMap is a wrapper for the current HttpSessionobject and maintains a reference to the HttpSession object.
The reference to the HttpSession object inside SessionMap is protected, so you won’t be able to access it directly from your action class. However, SessionMap provides methods that make accessing the HttpSession object directly no longer necessary. Here are the public methods defined in the SessionMap class.

public void invalidate()

Invalidates the current HttpSession object. If the HttpSession object has not been created, this method exits gracefully.
public void clear()

Removes all attributes in the HttpSession object. If the HttpSession object has not been created, this method does not throw an exception.

public java.util.Set entrySet() {

Returns a Set of attributes from the HttpSession object. If the HttpSession object is null, this method returns an empty set.

public java.lang.Object get(java.lang.Object key)

Returns the session attribute associated with the specified key. It returns null if the HttpSession object is null or if the key is not found.

public java.lang.Object put(java.lang.Object key,
java.lang.Object value)

Stores a session attribute in the HttpSession object and returns the attribute value. If the HttpSession object is null, it will create a new HttpSession object.

public java.lang.Object remove(java.lang.Object key)

Removes the specified session attribute and returns the attribute value. If the HttpSession object is null, this method returns null.
For example, to invalidate the session object, call the invalidate method on the SessionMap:

if (session instanceof org.apache.struts2.dispatcher.SessionMap) {
((SessionMap) session).invalidate();
}

SessionMap.invalidate is better than HttpSession.invalidate because the former does not throw an exception if the underlying HttpSession object is null.

Note
Unfortunately, the SessionMap class does not provide access to the session identifier. In the rare cases where you need the identifier, use the ServletActionContext to obtain theHttpSession object.
For this interface to work, the Servlet Config interceptor must be enabled. Since this interceptor is part of the default stack, by default it is already on.

Using Aware Interfaces to Access Resources
The below application shows how to use Aware interfaces to access resources. The application defines three actions as shown in previous code.

/jsp/Login.jsp
/jsp/Menu.jsp
/jsp/Login.jsp

/jsp/Login.jsp
The User_login and User_logout actions are based on the User action class defined below. This class has two properties (userName and password) and implementsServletContextAware, ServletRequestAware, ServletResponseAware, and SessionAware to provide access to resources. Note that to save space the get and set methods for the properties are not shown.

package mypckg;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.dispatcher.SessionMap;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.util.ServletContextAware;

public class User implements SessionAware, ServletRequestAware,
ServletResponseAware, ServletContextAware {
private String userName;
private String password;
private ServletContext servletContext;
private HttpServletRequest servletRequest;
private HttpServletResponse servletResponse;
private Map sessionMap;

// getters and setters not shown

public void setServletRequest(
HttpServletRequest servletRequest) {
this.servletRequest = servletRequest;
}
public void setSession(Map map) {
this.sessionMap = map;
}
public void setServletResponse(
HttpServletResponse servletResponse) {
this.servletResponse = servletResponse;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public String login() {
String referrer = servletRequest.getHeader(“referer”);
if (referrer != null && userName.length() > 0
&& password.length() > 0) {
int onlineUserCount = 0;
synchronized (servletContext) {
try {
onlineUserCount = (Integer) servletContext
.getAttribute(“onlineUserCount”);
} catch (Exception e) {
}
servletContext.setAttribute(“onlineUserCount”,
onlineUserCount + 1);
}
return “success”;
} else {
return “input”;
}
}

/*
* The onlineUserCount is accurate only if we also
* write a javax.servlet.http.HttpSessionListener
* implementation and decrement the
* onlineUserCount attribute value in its
* sessionDestroyed method, which is called by the
* container when a user session is inactive for
* a certain period of time.
*/
public String logout() {
if (sessionMap instanceof SessionMap) {
((SessionMap) sessionMap).invalidate();
}
int onlineUserCount = 0;
synchronized (servletContext) {
try {
onlineUserCount = (Integer) servletContext
.getAttribute(“onlineUserCount”);
} catch (Exception e) {
}
servletContext.setAttribute(“onlineUserCount”,
onlineUserCount – 1);
}
return “success”;
}

}

The User class can be used to manage user logins and maintain the number of users currently logged in. In this application a user can log in by typing in a non-empty user name and a non-empty password in a Login form.
You can access the HttpServletRequest object because the User class implements ServletRequestAware. As demonstrated in the login method, that gets invoked every time a user logs in, you retrieve the referer header by calling the getHeader method on the servletRequest object. Verifying that the referer header is not null makes sure that the action was invoked by submitting the Login form, not by typing the URL of the User_input action. Next, the login method increments the value of the application attribute onlineUserCount.
The logout method invalidates the HttpSession object and decrements onlineUserCount. Therefore, the value of onlineUserCount reflects the number of users currently logged in.
You can test this application by invoking the User_input action using this URL:

http://localhost:8080/myapp/User_input.action

Passing Static Parameters to An Action
Request parameters are mapped to action properties. However, there’s another way of assigning values to action properties: by passing the values in the action declaration.
An action element in a struts.xml file may contain param elements. Each param element corresponds to an action property. The Static Parameters (staticParams) interceptor is responsible for mapping static parameters to action properties.
Here is an example of how to pass static parameters to an action.
california01retail

Every time the action MyAction is invoked, its siteId property will be set to “california0l” and its siteType property to “retail.”

The ActionSupport Class
The com.opensymphony.xwork2.ActionSupport class is the default action class. Struts will create an instance of this class if an action declaration does not specify an action class. You may also want to extend this class when writing action classes.
Since ActionSupport implements the Action interface, you can use the static fields ERROR, INPUT, LOGIN, NONE, and SUCCESS from a class that extends it. There’s already an implementation of the execute method, inherited from Action, that simply returns Action.SUCCESS. If you implement the Action interface directly instead of extending ActionSupport, you have to provide an implementation of execute yourself. Therefore, it’s more convenient to extend ActionSupport than to implement Action.
In addition to execute, there are other methods in ActionSupport that you can override or use. For instance, you may want to override validate if you’re writing code for validating user input. And you can use one of the many overloads of getText to look up localized messages in properties files.

Results
An action method returns a String that determines what result to execute. An action declaration must contain result elements that each corresponds to a possible return value of the action method. If, for example, an action method returns either Action.SUCCESS or Action.INPUT, the action declaration must have two result elements like these

A result element can have these attributes:
• name. The name of the result that matches the output of the action method. For example, if the value of the name attribute is “input,” the result will be used if the action method returns “input.” The name attribute is optional and its default value is “success.”
• type. The result type. The default value is “dispatcher,” a result type that forwards to a JSP.
The default values of both attributes help you write shorter configuration. For example, these result elements

/Product.jsp
/ProductForm.jsp

are the same as these:

/Product.jsp
/ProductForm.jsp

The first result element does not have to contain the name and type attributes as it uses the default values. The second result element needs the name attribute but does not need thetype attribute.
Dispatcher is the most frequently used result type, but it’s not the only type available. The table below shows all standard result types. The words in brackets in the Result Type column are names used to register the result types in the configuration file. That’s right, you must register a result type before you can use it.
Result Type Description
Chain (chain) Used for action chaining
Dispatcher (dispatcher) The default result type, used for JSP forwarding
FreeMarker (freemarker) Used for FreeMarker integration
HttpHeader (httpheader) Used to send HTTP headers back to the browser
Redirect (redirect) Used to redirect to another URL
Redirect Action (redirect-action) Used to redirect to another action
Stream (stream) Used to stream an InputStream to the browser
Velocity (velocity) Used for Velocity integration
XSLT (xslt) Used for XML/XSLT integration
PlainText (plaintext) Used to send plain text, normally to show a JSP’s source.

Chain
The Chain result type is there to support action chaining, whereby an action is forwarded to another action and the state of the original action is retained in the target action. The Chaining interceptor makes action chaining possible and since this interceptor is part of defaultStack, you can use action chaining right away.
The following declarations show an example of action chaining.
action2

action3/namespace2

/MyView.jsp

action1 in package1 is chained to action2, which in turn is chained to action3 in a different package. Chaining to an action in a different package is allowed as long as you specify thenamespace parameter of the target action.
If action-x is chained to action-y, action-x will be pushed to the Value Stack, followed by action-y, making action-y the top object in the Object Stack. As a result, both actions can be accessed from the view. If action-x and action-y both have a property that shares the same name, you can access the property in action-y (the top object) using this OGNL expression:

[0].propertyName

or

propertyName

You can access the property in action-x using this expression:

[1].propertyName

Use action chaining with caution, though. Generally action chaining is not recommended as it may turn your actions into spaghetti code. If action1 needs to be forwarded to action2, for example, you need to ask yourself if there’s code in action2 that needs to be pushed into a method in a utility class that can be called from both action1 and action2.

Dispatcher
The Dispatcher result type is the most frequently used type and the default type. This result type has a location parameter that is the default parameter. Since it is the default parameter, you can either pass a value to it by using the param element like this:
90
resource

or by passing the value to the result element.

resource

Use this result type to forward to a resource, normally a JSP or an HTML file, in the same application. You cannot forward to an external resource and its location parameter cannot be assigned an absolute URL. To direct to an external resource, use the Redirect result type.
As almost all accompanying applications in this article utilize this result type, a separate example is not given here.
FreeMarker
This result type forwards to a FreeMarker template.
HttpHeader
This result type is used to send an HTTP status to the browser. For example, the application should have this action declaration:

 

404

The default-action-ref element is used to specify the default action, which is the action that will be invoked if a URI does not have a matching action. In the example above, the CatchAllaction is the default action. CatchAll uses a HttpHeader result to send a 404 status code to the browser. As a result, if there’s no matching action, instead of getting Struts’ error messages:

Struts Problem Report
Struts has detected an unhandled exception:
Messages: There is no Action mapped for namespace / and action name
blahblah

the user will get a 404 status report and will see a default page from the container.
Redirect
This result type redirects, instead of forward, to another resource. This result type accepts these parameters
• location. Specifies the redirection target.
• parse. Indicates whether or not the value of location should be parsed for OGNL expressions. The default value for parse is true.
The main reason to use a redirect, as opposed to a forward, is to direct the user to an external resource. A forward using Dispatcher is preferable when directing to an internal resource because a forward is faster. Redirection would require a round trip since the client browser would be forced to re-send a new HTTP request.
Having said that, there is a reason why you may want to redirect to an internal resource. You normally redirect if you don’t want a page refresh invokes the previously invoked action. For instance, in a typical application, submitting a form invokes a Product_save action, that adds a new product to the database. If this action forwards to a JSP, the Address box of the browser will still be showing the URL that invoked Product_save. If the user for some reason presses the browser’s Reload or Refresh button, the same action will be invoked again, potentially adding the same product to the database. Redirection removes the association with the previous action as the redirection target has a new URL.
Here is an example of redirecting to an external resource.

http://www.example.com/test.html
And this to an internal resource:
/jsp/Product.jsp

When redirecting to an internal resource, you specify a URI for the resource. The URI can point to an action. For instance,

User_input.action
In the last two examples, the target object was a resource relative to the current URL. Redirect does not care if the target is a JSP or an action, it always treat it as if the target is another page. Contrast this with the Redirect Action result type explained in the next section.
The underlying class for the Redirect result type calls HttpServletResponse.sendRedirect. Consequently, the action that was just executed is lost and no longer available. If you need the state of the source action available in the target destination, you can pass data through the session or request parameters. The RedirectTest action below redirects to the User_inputaction and passes the value of the userName property of the TestUser action class as a userName request parameter. Note that the dynamic value is enclosed in ${ and }.

User_input.action?userName=${userName}
Note also that you need to encode special characters such as & and + . For example, if the target is http://www.test.com?user=l&site=4, you must change the & to &.
http://www.test.com?user=1&site=4

Redirect Action
This result type is similar to Redirect. Instead of redirecting to a different resource, however, Redirect Action redirects to another action. The Redirect Action result type can take these parameters:
• actionName. Specifies the name of the target action. This is the default attribute.
• namespace. The namespace of the target action. If no namespace parameter is present, it is assumed the target action resides in the same namespace as the enclosing action.
For example, the following Redirect Action result redirects to a User_input action.
User_input

And since actionName is the default parameter, you can simply write:

User_input

Note that the value of the redirection target is an action name. There is no .action suffix necessary as is the case with the Redirect result type.
In addition to the two parameters, you can pass other parameters as request parameters. For example, the following result type
User_inputxyzga

will be translated into this URI:

User_input.action?userId=xyz&area=ga

Stream
This result type does not forward to a JSP. Instead, it sends an output stream to the browser.
Velocity
This result type forwards to a Velocity template.
XSLT
This result type uses XML/XSLT as the view technology.
PlainText
A PlainText result is normally used for sending a JSP’s source. For example, the action Source_show below displays the source of the Menu.jsp page.
/jsp/Menu.jsp

Exception Handling with Exception Mapping
In a perfect world, all computer programs would be bug-free. In the real world, however, this is not the case. No matter how you take care to handle your code, some bugs might still try to creep out. Sometimes it’s not even your fault. Third-party components you use in your code may have bugs that are not known at the time you deploy your application. Any uncaught exception will result in an embarrassing HTTP 500 code (internal error).
Fortunately for Struts programmers, Struts lets you catch whatever you cannot catch in your action classes by using the exception-mapping element in the configuration file.
This exception-mapping element has two attributes, exception and result. The exception attribute specifies the exception type that will be caught. The result attribute specifies a result name, either in the same action or in the global-results declaration, that will be executed if an exception is caught. You can nest one or more exception-mapping elements under your action declaration. For example, the following exception-mapping element catches all exceptions thrown by the User_save action and executes the error result.

/jsp/Error.jsp
/jsp/Thanks.jsp

You can also provide a global exception mapping through the use of the global-exception-mappings element. Any exception-mapping declared under the global-exception-mappings element must refer to a result in the global-results element. Here is an example of global-exception-mappings.
/jsp/Error.jsp
/jsp/SQLError.jsp

Behind the scenes is the Exception interceptor that handles all exceptions caught. Part of the default stack, this exception adds two objects to the Value Stack, for every exception caught by an exception-mapping element.
• exception, that represents the Exception object thrown
• exceptionStack, that contains the value from the stack trace.
This way, you can display the exception message or the stack trace in your view, if you so choose. The property tag can be used for this purpose:

Dynamic Method Invocation
In Struts jargon the ‘!’ character is called the bang notation. It is used to invoke a method dynamically. The method may be different from the one specified in the action element for that action.
For example, this action declaration does not have a method attribute.

As a result, the execute method on Book will be invoked. However, using the bang notation you can invoke a different method in the same action. The URI /Book!edit.action, for example, will invoke the edit method on Book.
You are not recommended to use dynamic method invocation because of security concerns. You wouldn’t want your users to be able to invoke methods that you do not expose.
By default, dynamic method invocation is enabled. The default.properties file specifies a value of true for struts.enable.DynamicMethodInvocation:

struts.enable.DynamicMethodInvocation = true

To disable this feature, set this key to false, either in a struts.properties file or in a struts.xml file using a constant element like this:

References:
Struts 2 Design and Programming: A Tutorial
By: Budi Kurniawan