001: /*
002: * $Id: EventActionDispatcher.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: package org.apache.struts.actions;
023:
024: import java.util.StringTokenizer;
025: import java.lang.reflect.Method;
026:
027: import javax.servlet.ServletException;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.apache.struts.action.Action;
034: import org.apache.struts.action.ActionForm;
035: import org.apache.struts.action.ActionMapping;
036: import org.apache.struts.action.ActionForward;
037:
038: /**
039: * <p>An Action helper class that dispatches to to one of the public methods
040: * that are named in the <code>parameter</code> attribute of the corresponding
041: * ActionMapping and matches a submission parameter. This is useful for
042: * developers who prefer to use many submit buttons, images, or submit links
043: * on a single form and whose related actions exist in a single Action class.</p>
044: *
045: * <p>The method(s) in the associated <code>Action</code> must have the same
046: * signature (other than method name) of the standard Action.execute method.</p>
047: *
048: * <p>To configure the use of this action in your
049: * <code>struts-config.xml</code> file, create an entry like this:</p>
050: *
051: * <pre><code>
052: * <action path="/saveSubscription"
053: * type="org.example.SubscriptionAction"
054: * name="subscriptionForm"
055: * scope="request"
056: * input="/subscription.jsp"
057: * parameter="save,back,recalc=recalculate,default=save"/>
058: * </code></pre>
059: *
060: * <p>where <code>parameter</code> contains three possible methods and one
061: * default method if nothing matches (such as the user pressing the enter key).</p>
062: *
063: * <p>For utility purposes, you can use the <code>key=value</code> notation to
064: * alias methods so that they are exposed as different form element names, in the
065: * event of a naming conflict or otherwise. In this example, the <em>recalc</em>
066: * button (via a request parameter) will invoke the <code>recalculate</code>
067: * method. The security-minded person may find this feature valuable to
068: * obfuscate and not expose the methods.</p>
069: *
070: * <p>The <em>default</em> key is purely optional. If this is not specified
071: * and no parameters match the list of method keys, <code>null</code> is
072: * returned which means the <code>unspecified</code> method will be invoked.</p>
073: *
074: * <p>The order of the parameters are guaranteed to be iterated in the order
075: * specified. If multiple buttons were accidently submitted, the first match in
076: * the list will be dispatched.</p>
077: *
078: * <p>To implement this <i>dispatch</i> behaviour in an <code>Action</code>,
079: * class create your custom Action as follows, along with the methods you require
080: * (and optionally "cancelled" and "unspecified" methods):</p> <p/>
081: * <pre>
082: * public class MyCustomAction extends Action {
083: *
084: * protected ActionDispatcher dispatcher = new EventActionDispatcher(this);
085: *
086: * public ActionForward execute(ActionMapping mapping,
087: * ActionForm form,
088: * HttpServletRequest request,
089: * HttpServletResponse response)
090: * throws Exception {
091: * return dispatcher.execute(mapping, form, request, response);
092: * }
093: * }
094: * </pre>
095: * <p/>
096: *
097: * @since Struts 1.2.9
098: */
099: public class EventActionDispatcher extends ActionDispatcher {
100:
101: /**
102: * Commons Logging instance.
103: */
104: private static final Log LOG = LogFactory
105: .getLog(EventActionDispatcher.class);
106:
107: /**
108: * The method key, if present, to use if other specified method keys
109: * do not match a request parameter.
110: */
111: private static final String DEFAULT_METHOD_KEY = "default";
112:
113: /**
114: * Constructs a new object for the specified action.
115: * @param action the action
116: */
117: public EventActionDispatcher(Action action) {
118: // N.B. MAPPING_FLAVOR causes the getParameter() method
119: // in ActionDispatcher to throw an exception if the
120: // parameter is missing
121: super (action, ActionDispatcher.MAPPING_FLAVOR);
122: }
123:
124: /**
125: * <p>Dispatches to the target class' <code>unspecified</code> method, if
126: * present, otherwise throws a ServletException. Classes utilizing
127: * <code>EventActionDispatcher</code> should provide an <code>unspecified</code>
128: * method if they wish to provide behavior different than throwing a
129: * ServletException.</p>
130: *
131: * @param mapping The ActionMapping used to select this instance
132: * @param form The optional ActionForm bean for this request (if any)
133: * @param request The non-HTTP request we are processing
134: * @param response The non-HTTP response we are creating
135: * @return The forward to which control should be transferred, or
136: * <code>null</code> if the response has been completed.
137: * @throws Exception if the application business logic throws an
138: * exception.
139: */
140: protected ActionForward unspecified(ActionMapping mapping,
141: ActionForm form, HttpServletRequest request,
142: HttpServletResponse response) throws Exception {
143: // Identify if there is an "unspecified" method to be dispatched to
144: String name = "unspecified";
145: Method method = null;
146:
147: try {
148: method = getMethod(name);
149: } catch (NoSuchMethodException e) {
150: String message = messages.getMessage("event.parameter",
151: mapping.getPath());
152:
153: LOG.error(message + " " + mapping.getParameter());
154:
155: throw new ServletException(message);
156: }
157:
158: return dispatchMethod(mapping, form, request, response, name,
159: method);
160: }
161:
162: /**
163: * Returns the method name, given a parameter's value.
164: *
165: * @param mapping The ActionMapping used to select this instance
166: * @param form The optional ActionForm bean for this request (if
167: * any)
168: * @param request The HTTP request we are processing
169: * @param response The HTTP response we are creating
170: * @param parameter The <code>ActionMapping</code> parameter's name
171: * @return The method's name.
172: * @throws Exception if an error occurs.
173: */
174: protected String getMethodName(ActionMapping mapping,
175: ActionForm form, HttpServletRequest request,
176: HttpServletResponse response, String parameter)
177: throws Exception {
178:
179: StringTokenizer st = new StringTokenizer(parameter, ",");
180: String defaultMethodName = null;
181:
182: while (st.hasMoreTokens()) {
183: String methodKey = st.nextToken().trim();
184: String methodName = methodKey;
185:
186: // The key can either be a direct method name or an alias
187: // to a method as indicated by a "key=value" signature
188: int equals = methodKey.indexOf('=');
189: if (equals > -1) {
190: methodName = methodKey.substring(equals + 1).trim();
191: methodKey = methodKey.substring(0, equals).trim();
192: }
193:
194: // Set the default if it passes by
195: if (methodKey.equals(DEFAULT_METHOD_KEY)) {
196: defaultMethodName = methodName;
197: }
198:
199: // If the method key exists as a standalone parameter or with
200: // the image suffixes (.x/.y), the method name has been found.
201: if ((request.getParameter(methodKey) != null)
202: || (request.getParameter(methodKey + ".x") != null)) {
203: return methodName;
204: }
205: }
206:
207: return defaultMethodName;
208: }
209: }
|