View Javadoc
1   /*
2    * $Id$
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts.extras.actions;
22  
23  import java.util.StringTokenizer;
24  
25  import jakarta.servlet.ServletException;
26  import jakarta.servlet.http.HttpServletRequest;
27  import jakarta.servlet.http.HttpServletResponse;
28  
29  import org.apache.struts.action.ActionForm;
30  import org.apache.struts.action.ActionForward;
31  import org.apache.struts.action.ActionMapping;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  /**
36   * <p>An <strong>Action</strong> that dispatches to to one of the public methods
37   * that are named in the <code>parameter</code> attribute of the corresponding
38   * ActionMapping and matches a submission parameter. This is useful for
39   * developers who prefer to use many submit buttons, images, or submit links
40   * on a single form and whose related actions exist in a single Action class.</p>
41   *
42   * <p>The method(s) must have the same signature (other than method name) of the
43   * standard Action.execute method.</p>
44   *
45   * <p>To configure the use of this action in your
46   * <code>struts-config.xml</code> file, create an entry like this:</p>
47   *
48   * <pre><code>
49   *   &lt;action path="/saveSubscription"
50   *           type="org.example.SubscriptionAction"
51   *           name="subscriptionForm"
52   *          scope="request"
53   *          input="/subscription.jsp"
54   *      parameter="save,back,recalc=recalculate,default=save"/&gt;
55   * </code></pre>
56   *
57   * <p>where <code>parameter</code> contains three possible methods and one
58   * default method if nothing matches (such as the user pressing the enter key).</p>
59   *
60   * <p>For utility purposes, you can use the <code>key=value</code> notation to
61   * alias methods so that they are exposed as different form element names, in the
62   * event of a naming conflict or otherwise. In this example, the <em>recalc</em>
63   * button (via a request parameter) will invoke the <code>recalculate</code>
64   * method. The security-minded person may find this feature valuable to
65   * obfuscate and not expose the methods.</p>
66   *
67   * <p>The <em>default</em> key is purely optional. If this is not specified
68   * and no parameters match the list of method keys, <code>null</code> is
69   * returned which means the <code>unspecified</code> method will be invoked.</p>
70   *
71   * <p>The order of the parameters are guaranteed to be iterated in the order
72   * specified. If multiple buttons were accidently submitted, the first match in
73   * the list will be dispatched.</p>
74   *
75   * @since Struts 1.2.9
76   */
77  public class EventDispatchAction extends DispatchAction {
78      private static final long serialVersionUID = -5067513904785952541L;
79  
80      /**
81       * The {@code Log} instance for this class.
82       */
83      private transient final Logger log =
84          LoggerFactory.getLogger(EventDispatchAction.class);
85  
86      /**
87       * The method key, if present, to use if other specified method keys
88       * do not match a request parameter.
89       */
90      private static final String DEFAULT_METHOD_KEY = "default";
91  
92      // --------------------------------------------------------- Protected Methods
93  
94      /**
95       * Method which is dispatched to when there is no value for specified
96       * request parameter included in the request.  Subclasses of
97       * <code>DispatchAction</code> should override this method if they wish to
98       * provide default behavior different than throwing a ServletException.
99       *
100      * @param mapping  The ActionMapping used to select this instance
101      * @param form     The optional ActionForm bean for this request (if any)
102      * @param request  The non-HTTP request we are processing
103      * @param response The non-HTTP response we are creating
104      * @return The forward to which control should be transferred, or
105      *         <code>null</code> if the response has been completed.
106      * @throws Exception if the application business logic throws an
107      *                   exception.
108      */
109     protected ActionForward unspecified(ActionMapping mapping, ActionForm form,
110         HttpServletRequest request, HttpServletResponse response)
111         throws Exception {
112         String message =
113             messages.getMessage("event.parameter", mapping.getPath(),
114                 mapping.getParameter());
115 
116         log.error("{} {}", message, mapping.getParameter());
117 
118         throw new ServletException(message);
119     }
120 
121     /**
122      * Returns the method name, given a parameter's value.
123      *
124      * @param mapping   The ActionMapping used to select this instance
125      * @param form      The optional ActionForm bean for this request (if
126      *                  any)
127      * @param request   The HTTP request we are processing
128      * @param response  The HTTP response we are creating
129      * @param parameter The <code>ActionMapping</code> parameter's name
130      * @return The method's name.
131      * @throws Exception if an error occurs.
132      */
133     protected String getMethodName(ActionMapping mapping, ActionForm form,
134             HttpServletRequest request, HttpServletResponse response,
135             String parameter) throws Exception {
136 
137         StringTokenizer st = new StringTokenizer(parameter, ",");
138         String defaultMethodName = null;
139 
140         while (st.hasMoreTokens()) {
141             String methodKey = st.nextToken().trim();
142             String methodName = methodKey;
143 
144             // The key can either be a direct method name or an alias
145             // to a method as indicated by a "key=value" signature
146             int equals = methodKey.indexOf('=');
147             if (equals > -1) {
148                 methodName = methodKey.substring(equals + 1).trim();
149                 methodKey = methodKey.substring(0, equals).trim();
150             }
151 
152             // Set the default if it passes by
153             if (methodKey.equals(DEFAULT_METHOD_KEY)) {
154                 defaultMethodName = methodName;
155             }
156 
157             // If the method key exists as a standalone parameter or with
158             // the image suffixes (.x/.y), the method name has been found.
159             if ((request.getParameter(methodKey) != null)
160                   || (request.getParameter(methodKey + ".x") != null)) {
161                 return methodName;
162             }
163         }
164 
165         return defaultMethodName;
166     }
167 }