001: /*
002: * $Id: LookupDispatchAction.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: package org.apache.struts.actions;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.apache.struts.Globals;
026: import org.apache.struts.action.ActionForm;
027: import org.apache.struts.action.ActionForward;
028: import org.apache.struts.action.ActionMapping;
029: import org.apache.struts.config.MessageResourcesConfig;
030: import org.apache.struts.config.ModuleConfig;
031: import org.apache.struts.util.MessageResources;
032:
033: import javax.servlet.ServletException;
034: import javax.servlet.http.HttpServletRequest;
035: import javax.servlet.http.HttpServletResponse;
036:
037: import java.util.HashMap;
038: import java.util.Iterator;
039: import java.util.Locale;
040: import java.util.Map;
041:
042: /**
043: * <p> An abstract <strong>Action</strong> that dispatches to the subclass
044: * mapped <code>execute</code> method. This is useful in cases where an HTML
045: * form has multiple submit buttons with the same name. The button name is
046: * specified by the <code>parameter</code> property of the corresponding
047: * ActionMapping. To configure the use of this action in your
048: * <code>struts-config.xml</code> file, create an entry like this:</p> <pre>
049: * <action path="/test"
050: * type="org.example.MyAction"
051: * name="MyForm"
052: * scope="request"
053: * input="/test.jsp"
054: * parameter="method"/>
055: * </pre> <p>
056: *
057: * which will use the value of the request parameter named "method" to locate
058: * the corresponding key in ApplicationResources. For example, you might have
059: * the following ApplicationResources.properties:</p> <pre>
060: * button.add=Add Record
061: * button.delete=Delete Record
062: * </pre><p>
063: *
064: * And your JSP would have the following format for submit buttons:</p> <pre>
065: * <html:form action="/test">
066: * <html:submit property="method">
067: * <bean:message key="button.add"/>
068: * </html:submit>
069: * <html:submit property="method">
070: * <bean:message key="button.delete"/>
071: * </html:submit>
072: * </html:form>
073: * </pre> <p>
074: *
075: * Your subclass must implement both getKeyMethodMap and the methods defined
076: * in the map. An example of such implementations are:</p>
077: * <pre>
078: * protected Map getKeyMethodMap() {
079: * Map map = new HashMap();
080: * map.put("button.add", "add");
081: * map.put("button.delete", "delete");
082: * return map;
083: * }
084: *
085: * public ActionForward add(ActionMapping mapping,
086: * ActionForm form,
087: * HttpServletRequest request,
088: * HttpServletResponse response)
089: * throws IOException, ServletException {
090: * // do add
091: * return mapping.findForward("success");
092: * }
093: *
094: * public ActionForward delete(ActionMapping mapping,
095: * ActionForm form,
096: * HttpServletRequest request,
097: * HttpServletResponse response)
098: * throws IOException, ServletException {
099: * // 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
116: .getLog(LookupDispatchAction.class);
117:
118: /**
119: * Reverse lookup map from resource value to resource key.
120: */
121: protected Map localeMap = new HashMap();
122:
123: /**
124: * Resource key to method name lookup.
125: */
126: protected Map keyMethodMap = null;
127:
128: // ---------------------------------------------------------- Public Methods
129:
130: /**
131: * Process the specified HTTP request, and create the corresponding HTTP
132: * response (or forward to another web component that will create it).
133: * Return an <code>ActionForward</code> instance describing where and how
134: * control should be forwarded, or <code>null</code> if the response has
135: * already been completed.
136: *
137: * @param mapping The ActionMapping used to select this instance
138: * @param request The HTTP request we are processing
139: * @param response The HTTP response we are creating
140: * @param form The optional ActionForm bean for this request (if any)
141: * @return Describes where and how control should be forwarded.
142: * @throws Exception if an error occurs
143: */
144: public ActionForward execute(ActionMapping mapping,
145: ActionForm form, HttpServletRequest request,
146: HttpServletResponse response) throws Exception {
147: return super .execute(mapping, form, request, response);
148: }
149:
150: /**
151: * This is the first time this Locale is used so build the reverse lookup
152: * Map. Search for message keys in all configured MessageResources for the
153: * current module.
154: *
155: * @param request The HTTP request we are processing
156: * @param userLocale The locale for this request
157: * @return The reverse lookup map for the specified locale.
158: */
159: private Map initLookupMap(HttpServletRequest request,
160: Locale userLocale) {
161: Map lookupMap = new HashMap();
162:
163: this .keyMethodMap = this .getKeyMethodMap();
164:
165: ModuleConfig moduleConfig = (ModuleConfig) request
166: .getAttribute(Globals.MODULE_KEY);
167:
168: MessageResourcesConfig[] mrc = moduleConfig
169: .findMessageResourcesConfigs();
170:
171: // Look through all module's MessageResources
172: for (int i = 0; i < mrc.length; i++) {
173: MessageResources resources = this .getResources(request,
174: mrc[i].getKey());
175:
176: // Look for key in MessageResources
177: Iterator iter = this .keyMethodMap.keySet().iterator();
178:
179: while (iter.hasNext()) {
180: String key = (String) iter.next();
181: String text = resources.getMessage(userLocale, key);
182:
183: // Found key and haven't added to Map yet, so add the text
184: if ((text != null) && !lookupMap.containsKey(text)) {
185: lookupMap.put(text, key);
186: }
187: }
188: }
189:
190: return lookupMap;
191: }
192:
193: /**
194: * Provides the mapping from resource key to method name.
195: *
196: * @return Resource key / method name map.
197: */
198: protected abstract Map getKeyMethodMap();
199:
200: /**
201: * Lookup the method name corresponding to the client request's locale.
202: *
203: * @param request The HTTP request we are processing
204: * @param keyName The parameter name to use as the properties key
205: * @param mapping The ActionMapping used to select this instance
206: * @return The method's localized name.
207: * @throws ServletException if keyName cannot be resolved
208: * @since Struts 1.2.0
209: */
210: protected String getLookupMapName(HttpServletRequest request,
211: String keyName, ActionMapping mapping)
212: throws ServletException {
213: // Based on this request's Locale get the lookupMap
214: Map lookupMap = null;
215:
216: synchronized (localeMap) {
217: Locale userLocale = this .getLocale(request);
218:
219: lookupMap = (Map) this .localeMap.get(userLocale);
220:
221: if (lookupMap == null) {
222: lookupMap = this .initLookupMap(request, userLocale);
223: this .localeMap.put(userLocale, lookupMap);
224: }
225: }
226:
227: // Find the key for the resource
228: String key = (String) lookupMap.get(keyName);
229:
230: if (key == null) {
231: String message = messages.getMessage("dispatch.resource",
232: mapping.getPath());
233: LOG.error(message + " '" + keyName + "'");
234: throw new ServletException(message);
235: }
236:
237: // Find the method name
238: String methodName = (String) keyMethodMap.get(key);
239:
240: if (methodName == null) {
241: String message = messages.getMessage("dispatch.lookup",
242: mapping.getPath(), key);
243:
244: throw new ServletException(message);
245: }
246:
247: return methodName;
248: }
249:
250: /**
251: * Returns the method name, given a parameter's value.
252: *
253: * @param mapping The ActionMapping used to select this instance
254: * @param form The optional ActionForm bean for this request (if
255: * any)
256: * @param request The HTTP request we are processing
257: * @param response The HTTP response we are creating
258: * @param parameter The <code>ActionMapping</code> parameter's name
259: * @return The method's name.
260: * @throws Exception if an error occurs
261: * @since Struts 1.2.0
262: */
263: protected String getMethodName(ActionMapping mapping,
264: ActionForm form, HttpServletRequest request,
265: HttpServletResponse response, String parameter)
266: throws Exception {
267: // Identify the method name to be dispatched to.
268: // dispatchMethod() will call unspecified() if name is null
269: String keyName = request.getParameter(parameter);
270:
271: if ((keyName == null) || (keyName.length() == 0)) {
272: return null;
273: }
274:
275: String methodName = getLookupMapName(request, keyName, mapping);
276:
277: return methodName;
278: }
279: }
|