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 * <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 * @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 }