1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.struts.actions;
20
21 import java.util.StringTokenizer;
22 import java.lang.reflect.Method;
23
24 import javax.servlet.ServletException;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.struts.action.Action;
31 import org.apache.struts.action.ActionForm;
32 import org.apache.struts.action.ActionMapping;
33 import org.apache.struts.action.ActionForward;
34
35 /***
36 * <p>An Action helper class 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) in the associated <code>Action</code> must have the same
43 * signature (other than method name) of the 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 * <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"/>
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 * <p>To implement this <i>dispatch</i> behaviour in an <code>Action</code>,
76 * class create your custom Action as follows, along with the methods you require
77 * (and optionally "cancelled" and "unspecified" methods):</p> <p/>
78 * <pre>
79 * public class MyCustomAction extends Action {
80 *
81 * protected ActionDispatcher dispatcher = new EventActionDispatcher(this);
82 *
83 * public ActionForward execute(ActionMapping mapping,
84 * ActionForm form,
85 * HttpServletRequest request,
86 * HttpServletResponse response)
87 * throws Exception {
88 * return dispatcher.execute(mapping, form, request, response);
89 * }
90 * }
91 * </pre>
92 * <p/>
93 *
94 * @since Struts 1.2.9
95 */
96 public class EventActionDispatcher extends ActionDispatcher {
97
98 /***
99 * Commons Logging instance.
100 */
101 private static final Log LOG = LogFactory.getLog(EventActionDispatcher.class);
102
103 /***
104 * The method key, if present, to use if other specified method keys
105 * do not match a request parameter.
106 */
107 private static final String DEFAULT_METHOD_KEY = "default";
108
109 /***
110 * Constructs a new object for the specified action.
111 * @param action the action
112 */
113 public EventActionDispatcher(Action action) {
114
115
116
117 super(action, ActionDispatcher.MAPPING_FLAVOR);
118 }
119
120 /***
121 * <p>Dispatches to the target class' <code>unspecified</code> method, if
122 * present, otherwise throws a ServletException. Classes utilizing
123 * <code>EventActionDispatcher</code> should provide an <code>unspecified</code>
124 * method if they wish to provide behavior different than throwing a
125 * ServletException.</p>
126 *
127 * @param mapping The ActionMapping used to select this instance
128 * @param form The optional ActionForm bean for this request (if any)
129 * @param request The non-HTTP request we are processing
130 * @param response The non-HTTP response we are creating
131 * @return The forward to which control should be transferred, or
132 * <code>null</code> if the response has been completed.
133 * @throws Exception if the application business logic throws an
134 * exception.
135 */
136 protected ActionForward unspecified(ActionMapping mapping, ActionForm form,
137 HttpServletRequest request, HttpServletResponse response)
138 throws Exception {
139
140 String name = "unspecified";
141 Method method = null;
142
143 try {
144 method = getMethod(name);
145 } catch (NoSuchMethodException e) {
146 String message =
147 messages.getMessage("event.parameter", mapping.getPath());
148
149 LOG.error(message + " " + mapping.getParameter());
150
151 throw new ServletException(message);
152 }
153
154 return dispatchMethod(mapping, form, request, response, name, method);
155 }
156
157 /***
158 * Returns the method name, given a parameter's value.
159 *
160 * @param mapping The ActionMapping used to select this instance
161 * @param form The optional ActionForm bean for this request (if
162 * any)
163 * @param request The HTTP request we are processing
164 * @param response The HTTP response we are creating
165 * @param parameter The <code>ActionMapping</code> parameter's name
166 * @return The method's name.
167 * @throws Exception if an error occurs.
168 */
169 protected String getMethodName(ActionMapping mapping, ActionForm form,
170 HttpServletRequest request, HttpServletResponse response,
171 String parameter) throws Exception {
172
173 StringTokenizer st = new StringTokenizer(parameter, ",");
174 String defaultMethodName = null;
175
176 while (st.hasMoreTokens()) {
177 String methodKey = st.nextToken().trim();
178 String methodName = methodKey;
179
180
181
182 int equals = methodKey.indexOf('=');
183 if (equals > -1) {
184 methodName = methodKey.substring(equals + 1).trim();
185 methodKey = methodKey.substring(0, equals).trim();
186 }
187
188
189 if (methodKey.equals(DEFAULT_METHOD_KEY)) {
190 defaultMethodName = methodName;
191 }
192
193
194
195 if ((request.getParameter(methodKey) != null)
196 || (request.getParameter(methodKey + ".x") != null)) {
197 return methodName;
198 }
199 }
200
201 return defaultMethodName;
202 }
203 }