Recipe6.8.Managing Related Operations from a Central Action


Recipe 6.8. Managing Related Operations from a Central Action

Problem

You want to use a single Action class to handle related operations instead of having to write separate Action classes for each operation.

Solution

Extend the DispatchAction with your own class. Provide methods for each operation that you wish to be accessible as an Action. Each method should have the same signature as the execute( ) method except for the method name. The Action class shown in Example 6-4 provides three related operations in one class: create( ), update(), and delete( ).

Example 6-4. Dispatch action for related operations
package com.oreilly.strutsckbk.ch06; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DispatchAction; public class MyDispatchAction extends DispatchAction {         public ActionForward create( ActionMapping mapping,                                   ActionForm form,                                  HttpServletRequest request,                                   HttpServletResponse response)                                   throws Exception {         // create data         request.setAttribute("dispatchedTo","create");         return mapping.findForward("success");     }             public ActionForward update( ActionMapping mapping,                                   ActionForm form,                                  HttpServletRequest request,                                   HttpServletResponse response)                                   throws Exception {         // update data         request.setAttribute("dispatchedTo","update");         return mapping.findForward("success");     }             public ActionForward delete( ActionMapping mapping,                                   ActionForm form,                                  HttpServletRequest request,                                   HttpServletResponse response)                                   throws Exception {         // delete data         request.setAttribute("dispatchedTo","delete");         return mapping.findForward("success");     }         }

In the actions that use your DispatchAction, specify the request parameter whose value will be the method to call:

<action    path="/DispatchActionTest"            name="TestForm"           scope="request"            type="com.oreilly.strutsckbk.ch06.MyDispatchAction"       parameter="methodToCall">   <forward name="success" path="/dispatch_test.jsp"/> </action>

On a form that submits to this action, use JavaScript to set the parameter for the method to call. The name of the request parameter must match the value of the parameter attribute from the action mapping. The value of the request parameter is the name of the method. To dispatch to a method from a hyperlink, set the method parameter on the URL. Example 6-5 (dispatch_test.jsp) shows the different ways that you can specify the method parameter for a DispatchAction.

Example 6-5. JSP for submitting to a DispatchAction
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <head>   <title>Struts Cookbook - Chapter 6 : Dispatch Action Test</title> </head> <body bgcolor="white">   <h2>Dispatch Action Test</h2>   <html:form method="get" action="/DispatchActionTest">     Name:&nbsp;<html:text property="name"/>     <input type="hidden" name="methodToCall">     <script>       function set(target) {         document.forms[0].methodToCall.value=target;       }     </script>     <p>     <html:submit onclick="set('create');">New</html:submit>      <html:submit onclick="set('update');">Edit</html:submit>     <html:link href="javascript:set('touch');document.forms[0].submit( );">         <html:img border="0" srcKey="image.touch"/>     </html:link>     </p>   </html:form>   <html:link page="/DispatchActionTest.do?methodToCall=delete">     Remove</html:link> </body> </html>

Discussion

The DispatchAction enables a single custom class to process multiple requests for similar operations. Performing create/read/update/delete(CRUD) operations for the same business object exemplifies the classic use case for the DispatchAction. traditionally, four custom Action classes would be written, one for each operation. Since the operations are for the same business object, a significant amount of code would be duplicated.

The DispatchAction allows you to use a more natural programming approach. Instead of creating separate classes, you create one class with methods corresponding to the desired operations. A DispatchAction is created by subclassing org.apache.struts.actions.DispatchAction. You don't override the execute( ) method as you would for a normal custom Action. Instead, you implement methods corresponding to the operations you wish to support. Common behavior can be implemented by private methods that you call from the main dispatch operations. The method signature of each operation must be the same as the signature as the execute method:

public ActionForward someOperation(          ActionMapping mapping,          ActionForm form,         HttpServletRequest request,          HttpServletResponse response) throws Exception {       // custom code     return mapping.findForward("success"); }

The execute( ) method of the base DispatchAction calls a given method of your subclass using the value of a specific HTTP request parameter.

Is the DispatchAction Safe?

A common misconception is that the DispatchAction allows any method of your subclass to be called. In fact, the only methods that can be called are those that accept the same arguments as those passed to the execute() method.

However, the DispatchAction initially shipped with Struts 1.1 had a serious flaw because it didn't test if the "method to call" was the execute( ) or perform( ) method. This oversight opened the door for an accidental or malicious recursive call. This bug was identified and has been eliminated in Struts 1.2. If you are using Struts 1.1, you can eliminate this issue in your own DispatchAction subclass by overriding the dispatchMethod( ) method as follows:

protected ActionForward dispatchMethod(         ActionMapping mapping,         ActionForm form,         HttpServletRequest request,         HttpServletResponse response,         String name) throws Exception {     if ("execute".equals(name) || "perform".equals(name)) {         throw new ServletException(             "Accidental recursive call in DispatchAction"         );     }     return super.dispatchMethod(mapping,                                 form,                                 request,                                 response,                                 name); }


The parameter attribute of the action element in the struts-config.xml file dictates the name of that request parameter:

<action    path="/DispatchActionTest"            type="com.foo.MyDispatchAction"       parameter="methodToCall">   <forward name="success" path="/some_page.jsp"/> </action>

If the action is called on form submission, use a hidden field with the value set using JavaScript. You'll want to use the HTML <input type="hidden" name="methodToCall"> tag instead of the Struts <html:hidden property="methodToCall"/>. Using the Struts tag forces you to create an artificial property in your ActionForm to hold the methodToCall parameter.

If you want to use an image to perform the submission instead of a button, wrap the image in a link. Set the href attribute of the link so it sets the methodToCall property appropriately and submits the form:

<script>     function set(target) {         document.forms[0].methodToCall.value=target;     } </script> <html:link href="javascript:set('create');document.forms[0].submit( );">     <html:img border="0" srcKey="image.create"/> </html:link>

Finally, all DispatchActions support the ability to define default behavior if the name of the method to call can't be resolved; that is, no matching method is defined in the DispatchAction subclass. The default behavior is implemented by overriding the protected method named unspecified( ). This method, like other custom operational methods of DispatchActions, takes the same arguments as the execute( ) method. Use the unspecified( ) method if the DispatchAction has a primary default operation; then you won't have to use an additional request parameter to access this primary flow. If you don't provide an implementation of unspecified( ), then a ServletException will be thrown if an unknown method is specified.

See Also

Recipe 6.9 provides similar functionality as the DispatchAction without requiring the use of JavaScript on the JSP page.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net