1) Introduction
Action classes will be defined to handle requests. Actions exists between the Model and View of an application. This article will cover all of the standard actions and the helper methods of the Action class. The struts-config.xml file designates the Action classes that handle requests for various URLs. The Action objects do
- Invoke the appropriate business
- Data-accesslogic
- Store the results in beans
- Designate the type of situation (missing data, database error etc.)
The struts-config.xml file then decides which JSP page should apply to that situation. Generally Actions do
- Invoke business rules
- Return Model objects to the View to display.
Actions also prepare error messages to display in the View. We can also create utility actions. Utility actions can be linked to other actions using action chaining. This allows us to extend the behavior of an action without changing an action or having deeply nested class hierarchies of actions.
Actions have a life cycle similar to servlets. They are like servlets and they’re multithreaded. We must be careful when working with member variables of an action because they are not thread safe. Struts has a servlet called the ActionServlet. The ActionServlet inspects the incoming request and delegates the request to an action based on the incoming request path. The object that relates the action to the incoming request path is the Action Mapping. Our actions are part of the Struts Controller.
2) Using Action classes
Typically There are 3 steps to create an Action
- Create an action by subclassing org.apache.struts.action.Action
import org.apache.struts.action public class ExampleAction extends Action { … }
- Override the execute() method : This is the method where we define the behavior of the current action. It contains business logic and the way to next action. The general structure of the execute() method is as follows:
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
The Action Mapping contains everything configurable in the action element of the Struts config file. The ActionForm represents the incoming request parameters. The HttpServletRequest references the current request object. No compulsary use for HttpServletResponse. The execute() method returns an ActionForward. The ActionForward is the logical next View or the logical next step.
- Configure the Action in the Struts config file. As follows
<action path="/ExampleAction " type="ExampleAction "> <forward name="success" path="/Example.jsp"/> </action>
- org.apache.struts.actions
- Forward Action
- IncludeAction
- SwitchAction
- DispatchAction
- IncludeDispatchAction
The path attribute specifies the incoming request path that this action will handle.Here we have the Major Classification of Struts Actions.
Here mainly we are going to discuss about the above 5 actions.
3) ForwardAction
This is an Action that forwards to the context-relative URI specified by the parameter property of our associated ActionMapping. This can be used to integrate Struts with other business logic components that are implemented as servlets or JSP pages, but still take advantage of the Struts controller servlet’s functionality such as processing of form beans. To configure the use of this Action in our struts-config.xml file, create an entry like this:
<action path="/saveSubscription" type="org.apache.struts.actions.ForwardAction" name="subscriptionForm" scope="request" input="/subscription.jsp" parameter="/path to processing servlet" />
This one will forward control to the context-relative URI specified by the parameter attribute. Steps to use ForwardAction:
- Using the html:link tag with the action attribute, add a link to the JSP page that points to the action.
- Create an Action Mapping in the Struts configuration file that uses the ForwardAction with the parameter attribute to specify the JSP path.
Let’s consider, we have a JSP page that has a direct link to another JSP page:
<html:link page="/example.jsp">Home< /html:link>
Now we want to change the html:link tag to link to an action. Because we already have a link, simply change it as follows:
<html:link action="home">Home</html:link>
All we did is, removing the page attribute and adding an Action attribute that points to the home action. Now, we have to create the home action mapping. To add an action mapping to the home action that we referenced in our html:link tag, use this code:
<action path="/home" type="org.apache.struts.actions.ForwardAction" parameter="/example.jsp" />
3.1) Forward Attribute
ForwardActions get used quite a bit-so much so that the Struts configuration file includes support for them. So rather than doing this:
<action path="/home" type="org.apache.struts.actions.ForwardAction" parameter="/ example.jsp"/>
we can do this:
<action path="/home" forward="/example.jsp"/>
4) IncludeAction
This is an Action that includes the context-relative URI specified by the parameter property of our associated ActionMapping. This can be used to integrate Struts with other business logic components that are implemented as servlets or JSP pages, but still take advantage of the Struts controller servlet’s functionality such as processing of form beans. To configure the use of this Action in our struts-config.xml file, create an entry like this:
<action path="/saveSubscription" type="org.apache.struts.actions.IncludeAction" name="subscriptionForm" scope="request" input="/subscription.jsp" parameter="/path to processing servlet">
which will include the context-relative URI specified by the parameter attribute. This shorter form uses the include attribute:
<action path="/legacy" include="/legacy/roar" input="/form/userForm2.jsp" name="userForm" parameter="/legacy/roar" validate="true" scope="request"/>
The difference between the IncludeAction and the ForwardAction is that we need to use the IncludeAction only if the action is going to be included by another action or JSP. Therefore, if we have code in our JSP that looks like this:
<jsp:include page="/someWebApp/ someModule/someAction.do " />
The action could not use a ForwardAction because it would forward control to the new action rather than including its output within the output of the JSP-or throw a nasty IllegalStateException if the output buffer was already committed.
5) SwitchAction
This is a standard Action that switches to a new module and then forwards control to a URI within the new module. Valid request parameters for this Action are:
- page – Module-relative URI (beginning with “/”) to which control should be forwarded after switching.
- prefix – The module prefix (beginning with “/”) of the module to which control should be switched. Use a zero-length string for the default module. The appropriate ModuleConfig object will be stored as a request attribute, so any subsequent logic will assume the new module.
Say we have a web application that uses Struts. Struts has two modules: the default module and an admin module, both shown below:
<servlet> <servlet-name>action</servlet-name> <servlet-class> org.apache.struts.action.ActionServlet </servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml </param-value> </init-param> <init-param> <param-name>config/admin</param-name> <param-value>/WEB-INF/struts-config-admin.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
The init parameter config/admin defines the admin module. The config init parameter defines the default module. Now, let’s say we have an action in the default module that edits users and we want to delegate the display of those users to an action in the admin module. Perform the following steps:
- First, map a SwitchAction into the default moduleas shown here:
<action path="/switch" type="org.apache.struts.actions.SwitchAction"> </action>
- Now, we can set up a forwardin the action that edits the users as follows:
<action path="/userSubmit" attribute="userForm" input="/form/userForm.jsp" name="userForm" scope="request" type="action.UserAction"> <forward name="success" path="/switch.do?page=/listUsers.do&prefix=/admin"/> </action>
This forward passes two request parameters. The page parameter specifies the module relative action. The second parameter specifies the prefix of the module. We don’t have to use forwards to use the SwitchAction; any JSP can link to the SwitchAction to move to any module. The listUser.do action is not defined in the default module; it’s defined in the admin. The forward in this example forwards to the action at the path /admin/listUsers.do. The listUser.do is defined in /WEB-INF/struts-config-admin.xml, and the userSubmit. do action is defined in /WEB-INF/struts-config.xml.
6) DispatchAction
This is an abstract Action that dispatches to a public method that is named by the request parameter whose name is specified by the parameter property of the corresponding ActionMapping. This Action is useful for developers who prefer to combine many similar actions into a single Action class, in order to simplify their application design. To configure the use of this action in our struts-config.xml file, create an entry like this:
<action path="/saveSubscription" type="org.apache.struts.actions.DispatchAction" name="subscriptionForm" scope="request" input="/subscription.jsp" parameter="method"/>
which will use the value of the request parameter named “method” to pick the appropriate “execute” method, which must have the same signature of the standard Action.execute method. For example, we might have the following three methods in the same action:
- Public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception
- Public ActionForward insert(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception
- Public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception
and call one of the methods with a URL like this:
http://localhost:8080/myapp/saveSubscription.do?method=update
It should be noted that we don’t have to use the DispatchAction to group multiple actions into one Action class. We could just use a hidden field that we inspect to delegate to member() methods inside of our action. On the other hand, we could use the action element’s parameter attribute and decide which method to invoke based on the value of the parameter attribute.
The DispatchAction uses a hidden request parameter to determine which method to invoke. Thus, subclasses of the DispatchAction have many methods with the same signature of the execute() method. The name of the hidden request parameter specifies the name of the method to invoke. We specify the name of the hidden parameter by using the parameter attribute of the action element.
Rather than having a single execute() method, we have a method for each logical action. The DispatchAction dispatches to one of the logical actions represented by the methods. It picks a method to invoke based on an incoming request parameter. The value of the incoming request parameter is the name of the method that the DispatchAction will invoke.
All of the other mapping characteristics of this action must be shared by the various handlers. This places some constraints over what types of handlers may reasonably be packaged into the same DispatchAction subclass. If the value of the request parameter is empty, a method named unspecified is called. The default action is to throw an exception. If the request was cancelled, the custom handler cancelled, will be used instead. We can also override the getMethodName method to override the action’s default handler selection. Here is an example that groups multiple actions into one action.
- Create an action handlerclass that subclasses DispatchAction:
public class UserRegistrationMultiAction extends DispatchAction { ... }
- Create a method to represent each logical related action:
public ActionForward processPage1(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ... } public ActionForward processPage2(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ... }
These methods have the same signature other than the method name of the standard Action method.
- Create an Action Mapping for this action handler using the parameter attributeto specify the request parameter that carries the name of the method that we want to invoke:
<action path="/userRegistrationMultiPage1" type="strutsTutorial.UserRegistrationMultiAction" name="userRegistrationForm" attribute="user" parameter="action" input="/userRegistrationPage1.jsp"> ... </action> <action path="/userRegistrationMultiPage2" type="strutsTutorial.UserRegistrationMultiAction" name="userRegistrationForm" attribute="user" parameter="action" input="/userRegistrationPage2.jsp"> ... </action>
Based on this code, the DispatchAction that we created uses the value of the request parameter named method to pick the appropriate method to invoke. The parameter attribute specifies the name of the request parameter that is inspected by the DispatchAction.
- Pass the action a request parameter that refers to the method we want to invoke. The userRegistrationPage1.jsp contains this hidden parameter:
<html:hidden property="action" value="processPage1"/> while the userRegistrationPage2.jsp contains this hidden parameter: <html:hidden property="action" value="processPage2"/>
This implementation sends a hidden field parameter instead that specifies which method to invoke.
6.1) LookupDispatchAction
This is an abstract Action that dispatches to the subclass mapped execute method. This is useful in cases where an HTML form has multiple submit buttons with the same name. The button name is specified by the parameter property of the corresponding ActionMapping. To configure the use of this action in our struts-config.xml file, create an entry like this:
<action path="/test" type="org.example.MyAction" name="MyForm" scope="request" input="/test.jsp" parameter="method"/>
which will use the value of the request parameter named “method” to locate the corresponding key in ApplicationResources. For example, we might have the following ApplicationResources.properties:
button.add=Add Record button.delete=Delete Record
And our JSP would have the following format for submit buttons:
<html:form action="/test"> <html:submit property="method"> <bean:message key="button.add"/> </html:submit> <html:submit property="method"> <bean:message key="button.delete"/> </html:submit> </html:form>
Our subclass must implement both getKeyMethodMap and the methods defined in the map. An example of such implementations are:
protected Map getKeyMethodMap() { Map map = new HashMap(); map.put("button.add", "add"); map.put("button.delete", "delete"); return map; } public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // do add return mapping.findForward("success"); } public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // do delete return mapping.findForward("success"); }
If duplicate values exist for the keys returned by getKeys, only the first one found will be returned. If no corresponding key is found then an exception will be thrown. We can override the method unspecified to provide a custom handler. If the submit was cancelled, the custom handler cancelled will be used instead.
For this example, we use the registration form to use the LookupDispatchAction. We will add two buttons. One button will be labeled Save; this button will save the user to the system. The second button will be called Remove to use the LookupDispatchAction, perform the following steps:
- Create an action handler class that subclasses LookupDispatchAction:
public class UserRegistrationAction extends LookupDispatchAction {...}
- Next, we create a method to represent each logical related action: save and remove:
public class UserRegistrationAction extends LookupDispatchAction { private static Log log = LogFactory.getLog(UserRegistrationAction.class); public ActionForward remove( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { log.debug("IN REMOVE METHOD"); return mapping.findForward("success"); } public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { /* Create a User DTO and copy the properties from the userForm */ User user = new User(); BeanUtils.copyProperties(user, form); DataSource dataSource = getDataSource(request, "userDB"); Connection conn = dataSource.getConnection(); try { /* Create UserDAO */ UserDAO dao = DAOFactory.createUserDAO(conn); /* Use the UserDAO to insert the new user into the system */ dao.createUser(user); } finally { conn.close(); } return mapping.findForward("success"); } }
The two action methods, save and remove, have identical signatures to the standard Action.execute() method except for the method name.
- Implement the getKeyMethodMap()method to map the resource keys to method names:
protected Map getKeyMethodMap() { Map map = new HashMap(); map.put("userRegistration.removeButton", "remove"); map.put("userRegistration.saveButton", "save"); return map; }
- Create an action mapping for this action handler using the parameter attribute to specify the request parameter that carries the name of the method we want to invoke:
<action path="/userRegistration" type="strutsTutorial.UserRegistrationAction" name="userRegistrationForm" attribute="user" input="/userRegistration.jsp" parameter="action" > ... <forward name="success" path="/regSuccess.jsp" /> <forward name="failure" path="/regFailure.jsp" /> </action>
Here we specify the parameter to the action. This has a similar meaning to the DispatchAction. Essentially, this means the LookupDispatchAction inspects the label of the button called action. The label will be looked up from the resource bundle, the corresponding key will be found, and the key will be used against the method map to find the name of the method to invoke.
- Set up the messages in the resource bundle for the labels and values of the buttons. Inside our resource bundle, add the following two entries:
userRegistration.removeButton=Remove userRegistration.saveButton=Save
Here the keys are the same keys we used in the getKeyMethodMap.
- Use the bean:messagetag to display the labels of the button and associate the buttons with the name action:
<%@ taglib uri="/tags/struts-html" prefix="html"%> <%@ taglib uri="/tags/struts-bean" prefix="bean"%> ... <html:submit property="action"> <bean:message key="userRegistration.removeButton"/> </html:submit> ... <html:submit property="action"> <bean:message key="userRegistration.saveButton"/> </html:submit> ...
The final result depends on the user clicks.
7) Some More Actions and Action Helper Methods
Some more actions are there. Those are
- BaseAction: BaseAction is provided as an intermediate class for shared funtionality between Action and any stock implementation provided in this package.
- DownloadAction: This is an abstract base class that minimizes the amount of special coding that needs to be written to download a file.
- EventDispatchAction: An Action that dispatches to to one of the public methods that are named in the parameter attribute of the corresponding ActionMapping and matches a submission parameter.
- LocaleAction: Implementation of Action that changes the user’s Locale and forwards to a page, based on request level parameters that are set (language, country, & page).
- MappingDispatchAction: An abstract Action that dispatches to a public method that is named by the parameter attribute of the corresponding ActionMapping.
The Action class contains many helper methods, which enable us to add advanced functionality to our Struts applications. Some of them are
- Action saveTokenand isTokenValid: Make sure that a form is not submitted twice
- Action saveMessages and getResources: Display dynamic messages that are i18n-enabled
- Action isCancelled: Allow users to cancel an operation
- Action getLocale and setLocale: Allow users to change their locale.
also read:
8) Summary
Herewith we discussed in detail about the Struts Actions named: ForwardAction, IncludeAction, SwitchAction and DispatchAction in addition with LookupDispatchAction with some examples. Read it clearly and don’t get confused the functionalities.Try those examples and try to work with remaining actions also. I will come up in detail about the Action Class Helper Methods in my next article.