1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts.actions;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.apache.struts.Globals;
26 import org.apache.struts.action.ActionForm;
27 import org.apache.struts.action.ActionForward;
28 import org.apache.struts.action.ActionMapping;
29 import org.apache.struts.config.MessageResourcesConfig;
30 import org.apache.struts.config.ModuleConfig;
31 import org.apache.struts.util.MessageResources;
32
33 import javax.servlet.ServletException;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Locale;
40 import java.util.Map;
41
42 /**
43 * <p> An abstract <strong>Action</strong> that dispatches to the subclass
44 * mapped <code>execute</code> method. This is useful in cases where an HTML
45 * form has multiple submit buttons with the same name. The button name is
46 * specified by the <code>parameter</code> property of the corresponding
47 * ActionMapping. To configure the use of this action in your
48 * <code>struts-config.xml</code> file, create an entry like this:</p> <pre>
49 * <action path="/test"
50 * type="org.example.MyAction"
51 * name="MyForm"
52 * scope="request"
53 * input="/test.jsp"
54 * parameter="method"/>
55 * </pre> <p>
56 *
57 * which will use the value of the request parameter named "method" to locate
58 * the corresponding key in ApplicationResources. For example, you might have
59 * the following ApplicationResources.properties:</p> <pre>
60 * button.add=Add Record
61 * button.delete=Delete Record
62 * </pre><p>
63 *
64 * And your JSP would have the following format for submit buttons:</p> <pre>
65 * <html:form action="/test">
66 * <html:submit property="method">
67 * <bean:message key="button.add"/>
68 * </html:submit>
69 * <html:submit property="method">
70 * <bean:message key="button.delete"/>
71 * </html:submit>
72 * </html:form>
73 * </pre> <p>
74 *
75 * Your subclass must implement both getKeyMethodMap and the methods defined
76 * in the map. An example of such implementations are:</p>
77 * <pre>
78 * protected Map getKeyMethodMap() {
79 * Map map = new HashMap();
80 * map.put("button.add", "add");
81 * map.put("button.delete", "delete");
82 * return map;
83 * }
84 *
85 * public ActionForward add(ActionMapping mapping,
86 * ActionForm form,
87 * HttpServletRequest request,
88 * HttpServletResponse response)
89 * throws IOException, ServletException {
90 * // do add
91 * return mapping.findForward("success");
92 * }
93 *
94 * public ActionForward delete(ActionMapping mapping,
95 * ActionForm form,
96 * HttpServletRequest request,
97 * HttpServletResponse response)
98 * throws IOException, ServletException {
99 * // do delete
100 * return mapping.findForward("success");
101 * }
102 * </pre>
103 * <p> <strong>Notes</strong> - If duplicate values exist for the keys
104 * returned by getKeys, only the first one found will be returned. If no
105 * corresponding key is found then an exception will be thrown. You can
106 * override the method <code>unspecified</code> to provide a custom handler.
107 * If the submit was cancelled (a <code>html:cancel</code> button was
108 * pressed), the custom handler <code>cancelled</code> will be used instead.
109 */
110 public abstract class LookupDispatchAction extends DispatchAction {
111
112 /**
113 * Commons Logging instance.
114 */
115 private static final Log LOG = LogFactory.getLog(LookupDispatchAction.class);
116
117 /**
118 * Reverse lookup map from resource value to resource key.
119 */
120 protected Map localeMap = new HashMap();
121
122 /**
123 * Resource key to method name lookup.
124 */
125 protected Map keyMethodMap = null;
126
127
128
129 /**
130 * Process the specified HTTP request, and create the corresponding HTTP
131 * response (or forward to another web component that will create it).
132 * Return an <code>ActionForward</code> instance describing where and how
133 * control should be forwarded, or <code>null</code> if the response has
134 * already been completed.
135 *
136 * @param mapping The ActionMapping used to select this instance
137 * @param request The HTTP request we are processing
138 * @param response The HTTP response we are creating
139 * @param form The optional ActionForm bean for this request (if any)
140 * @return Describes where and how control should be forwarded.
141 * @throws Exception if an error occurs
142 */
143 public ActionForward execute(ActionMapping mapping, ActionForm form,
144 HttpServletRequest request, HttpServletResponse response)
145 throws Exception {
146 return super.execute(mapping, form, request, response);
147 }
148
149 /**
150 * This is the first time this Locale is used so build the reverse lookup
151 * Map. Search for message keys in all configured MessageResources for the
152 * current module.
153 *
154 * @param request The HTTP request we are processing
155 * @param userLocale The locale for this request
156 * @return The reverse lookup map for the specified locale.
157 */
158 private Map initLookupMap(HttpServletRequest request, Locale userLocale) {
159 Map lookupMap = new HashMap();
160
161 this.keyMethodMap = this.getKeyMethodMap();
162
163 ModuleConfig moduleConfig =
164 (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);
165
166 MessageResourcesConfig[] mrc =
167 moduleConfig.findMessageResourcesConfigs();
168
169
170 for (int i = 0; i < mrc.length; i++) {
171 MessageResources resources =
172 this.getResources(request, mrc[i].getKey());
173
174
175 Iterator iter = this.keyMethodMap.keySet().iterator();
176
177 while (iter.hasNext()) {
178 String key = (String) iter.next();
179 String text = resources.getMessage(userLocale, key);
180
181
182 if ((text != null) && !lookupMap.containsKey(text)) {
183 lookupMap.put(text, key);
184 }
185 }
186 }
187
188 return lookupMap;
189 }
190
191 /**
192 * Provides the mapping from resource key to method name.
193 *
194 * @return Resource key / method name map.
195 */
196 protected abstract Map getKeyMethodMap();
197
198 /**
199 * Lookup the method name corresponding to the client request's locale.
200 *
201 * @param request The HTTP request we are processing
202 * @param keyName The parameter name to use as the properties key
203 * @param mapping The ActionMapping used to select this instance
204 * @return The method's localized name.
205 * @throws ServletException if keyName cannot be resolved
206 * @since Struts 1.2.0
207 */
208 protected String getLookupMapName(HttpServletRequest request,
209 String keyName, ActionMapping mapping)
210 throws ServletException {
211
212 Map lookupMap = null;
213
214 synchronized (localeMap) {
215 Locale userLocale = this.getLocale(request);
216
217 lookupMap = (Map) this.localeMap.get(userLocale);
218
219 if (lookupMap == null) {
220 lookupMap = this.initLookupMap(request, userLocale);
221 this.localeMap.put(userLocale, lookupMap);
222 }
223 }
224
225
226 String key = (String) lookupMap.get(keyName);
227
228 if (key == null) {
229 String message =
230 messages.getMessage("dispatch.resource", mapping.getPath());
231 LOG.error(message + " '" + keyName + "'");
232 throw new ServletException(message);
233 }
234
235
236 String methodName = (String) keyMethodMap.get(key);
237
238 if (methodName == null) {
239 String message =
240 messages.getMessage("dispatch.lookup", mapping.getPath(), key);
241
242 throw new ServletException(message);
243 }
244
245 return methodName;
246 }
247
248 /**
249 * Returns the method name, given a parameter's value.
250 *
251 * @param mapping The ActionMapping used to select this instance
252 * @param form The optional ActionForm bean for this request (if
253 * any)
254 * @param request The HTTP request we are processing
255 * @param response The HTTP response we are creating
256 * @param parameter The <code>ActionMapping</code> parameter's name
257 * @return The method's name.
258 * @throws Exception if an error occurs
259 * @since Struts 1.2.0
260 */
261 protected String getMethodName(ActionMapping mapping, ActionForm form,
262 HttpServletRequest request, HttpServletResponse response,
263 String parameter) throws Exception {
264
265
266 String keyName = request.getParameter(parameter);
267
268 if ((keyName == null) || (keyName.length() == 0)) {
269 return null;
270 }
271
272 String methodName = getLookupMapName(request, keyName, mapping);
273
274 return methodName;
275 }
276 }