KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > struts > actions > ActionDispatcher


1 /*
2  * $Id: ActionDispatcher.java 164746 2005-04-26 05:41:28Z hrabago $
3  *
4  * Copyright 2005 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package org.apache.struts.actions;
20
21 import java.lang.reflect.InvocationTargetException JavaDoc;
22 import java.lang.reflect.Method JavaDoc;
23 import java.util.HashMap JavaDoc;
24
25 import javax.servlet.ServletException JavaDoc;
26 import javax.servlet.http.HttpServletRequest JavaDoc;
27 import javax.servlet.http.HttpServletResponse JavaDoc;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.struts.action.Action;
32 import org.apache.struts.action.ActionForm;
33 import org.apache.struts.action.ActionForward;
34 import org.apache.struts.action.ActionMapping;
35 import org.apache.struts.util.MessageResources;
36 import org.apache.struts.Globals;
37
38 /**
39  * <p>Action <i>helper</i> class that dispatches to a public method in an Action.</p>
40  * <p/>
41  * <p>This class is provided as an alternative mechanism to using DispatchAction
42  * and its various flavours and means <i>Dispatch</i> behaviour can be
43  * easily implemented into any <code>Action</code> without having to
44  * inherit from a particular super <code>Action</code>.</p>
45  * <p/>
46  * <p>To implement <i>dispatch</i> behaviour</i> in an <code>Action</code> class,
47  * create your custom Action as follows, along with the methods you
48  * require (and optionally "cancelled" and "unspecified" methods):</p>
49  * <p/>
50  * <pre>
51  * public class MyCustomAction extends Action {
52  *
53  * protected ActionDispatcher dispatcher
54  * = new ActionDispatcher(this, ActionDispatcher.MAPPING_FLAVOR);
55  *
56  * public ActionForward execute(ActionMapping mapping,
57  * ActionForm form,
58  * HttpServletRequest request,
59  * HttpServletResponse response)
60  * throws Exception {
61  * return dispatcher.execute(mapping, form, request, response);
62  * }
63  * }
64  * </pre>
65  * <p/>
66  * <p>It provides three flavours of determing the name of the method:</p>
67  * <p/>
68  * <ul>
69  * <li><strong>{@link #DEFAULT_FLAVOR}</strong> - uses the parameter specified in
70  * the struts-config.xml to get the method name from the Request
71  * (equivalent to <code>DispatchAction</code> <b>except</b> uses "method"
72  * as a default if the <code>parameter</code> is not specified
73  * in the struts-config.xml).</li>
74  * <p/>
75  * <li><strong>{@link #DISPATCH_FLAVOR}</strong> - uses the parameter specified in
76  * the struts-config.xml to get the method name from the Request
77  * (equivalent to <code>DispatchAction</code>).</li>
78  * <p/>
79  * <li><strong>{@link #MAPPING_FLAVOR}</strong> - uses the parameter specified in
80  * the struts-config.xml as the method name
81  * (equivalent to <code>MappingDispatchAction</code>).</li>
82  * <p/>
83  * </ul>
84  *
85  * @since Struts 1.2.7
86  * @version $Revision: 164746 $ $Date: 2005-04-26 06:41:28 +0100 (Tue, 26 Apr 2005) $
87  */

88 public class ActionDispatcher {
89
90
91     // ----------------------------------------------------- Instance Variables
92

93     /**
94      * Indicates "default" dispatch flavor
95      */

96     public static final int DEFAULT_FLAVOR = 0;
97
98     /**
99      * Indicates "mapping" dispatch flavor
100      */

101     public static final int MAPPING_FLAVOR = 1;
102
103     /**
104      * Indicates flavor compatible with DispatchAction
105      */

106     public static final int DISPATCH_FLAVOR = 2;
107
108
109     /**
110      * The associated Action to dispatch to.
111      */

112     protected Action actionInstance;
113
114     /**
115      * Indicates dispatch <i>flavor</i>
116      */

117     protected int flavor;
118
119     /**
120      * The Class instance of this <code>DispatchAction</code> class.
121      */

122     protected Class JavaDoc clazz;
123
124     /**
125      * Commons Logging instance.
126      */

127     protected static Log log = LogFactory.getLog(ActionDispatcher.class);
128
129
130     /**
131      * The message resources for this package.
132      */

133     protected static MessageResources messages =
134             MessageResources.getMessageResources
135             ("org.apache.struts.actions.LocalStrings");
136
137
138     /**
139      * The set of Method objects we have introspected for this class,
140      * keyed by method name. This collection is populated as different
141      * methods are called, so that introspection needs to occur only
142      * once per method name.
143      */

144     protected HashMap JavaDoc methods = new HashMap JavaDoc();
145
146     /**
147      * The set of argument type classes for the reflected method call. These
148      * are the same for all calls, so calculate them only once.
149      */

150     protected Class JavaDoc[] types = {
151         ActionMapping.class,
152         ActionForm.class,
153         HttpServletRequest JavaDoc.class,
154         HttpServletResponse JavaDoc.class};
155
156     // ----------------------------------------------------- Constructors
157

158     public ActionDispatcher(Action actionInstance) {
159         this(actionInstance, DEFAULT_FLAVOR);
160     }
161
162
163     public ActionDispatcher(Action actionInstance, int flavor) {
164
165         this.actionInstance = actionInstance;
166         this.flavor = flavor;
167
168         clazz = actionInstance.getClass();
169
170     }
171
172
173     // --------------------------------------------------------- Public Methods
174

175
176     /**
177      * Process the specified HTTP request, and create the corresponding HTTP
178      * response (or forward to another web component that will create it).
179      * Return an <code>ActionForward</code> instance describing where and how
180      * control should be forwarded, or <code>null</code> if the response has
181      * already been completed.
182      *
183      * @param mapping The ActionMapping used to select this instance
184      * @param form The optional ActionForm bean for this request (if any)
185      * @param request The HTTP request we are processing
186      * @param response The HTTP response we are creating
187      * @throws Exception if an exception occurs
188      */

189     public ActionForward execute(ActionMapping mapping,
190                                  ActionForm form,
191                                  HttpServletRequest JavaDoc request,
192                                  HttpServletResponse JavaDoc response)
193             throws Exception JavaDoc {
194
195         // Process "cancelled"
196
if (isCancelled(request)) {
197             ActionForward af = cancelled(mapping, form, request, response);
198             if (af != null) {
199                 return af;
200             }
201         }
202         // Identify the request parameter containing the method name
203
String JavaDoc parameter = getParameter(mapping, form, request, response);
204
205         // Get the method's name. This could be overridden in subclasses.
206
String JavaDoc name = getMethodName(mapping, form, request, response, parameter);
207
208
209         // Prevent recursive calls
210
if ("execute".equals(name) || "perform".equals(name)) {
211             String JavaDoc message =
212                     messages.getMessage("dispatch.recursive", mapping.getPath());
213
214             log.error(message);
215             throw new ServletException JavaDoc(message);
216         }
217
218
219         // Invoke the named method, and return the result
220
return dispatchMethod(mapping, form, request, response, name);
221
222     }
223
224
225     /**
226      * <p>Dispatches to the target class' <code>unspecified</code> method,
227      * if present, otherwise throws a ServletException. Classes utilizing
228      * <code>ActionDispatcher</code> should provide an <code>unspecified</code>
229      * method if they wish to provide behavior different than
230      * throwing a ServletException..</p>
231      */

232     protected ActionForward unspecified(ActionMapping mapping,
233                                         ActionForm form,
234                                         HttpServletRequest JavaDoc request,
235                                         HttpServletResponse JavaDoc response)
236             throws Exception JavaDoc {
237
238
239         // Identify if there is an "unspecified" method to be dispatched to
240
String JavaDoc name = "unspecified";
241         Method JavaDoc method = null;
242         try {
243             method = getMethod(name);
244
245         } catch (NoSuchMethodException JavaDoc e) {
246             String JavaDoc message = messages.getMessage("dispatch.parameter",
247                     mapping.getPath(),
248                     mapping.getParameter());
249
250             log.error(message);
251
252             throw new ServletException JavaDoc(message);
253         }
254
255         return dispatchMethod(mapping, form, request, response, name, method);
256
257     }
258
259     /**
260      * <p>Dispatches to the target class' cancelled method, if present,
261      * otherwise returns null. Classes utilizing <code>ActionDispatcher</code>
262      * should provide a <code>cancelled</code> method if they wish to provide
263      * behavior different than returning null.</p>
264      */

265     protected ActionForward cancelled(ActionMapping mapping,
266                                       ActionForm form,
267                                       HttpServletRequest JavaDoc request,
268                                       HttpServletResponse JavaDoc response)
269             throws Exception JavaDoc {
270
271         // Identify if there is an "cancelled" method to be dispatched to
272
String JavaDoc name = "cancelled";
273         Method JavaDoc method = null;
274         try {
275             method = getMethod(name);
276
277         } catch (NoSuchMethodException JavaDoc e) {
278             return null;
279         }
280
281         return dispatchMethod(mapping, form, request, response, name, method);
282
283     }
284
285     // ----------------------------------------------------- Protected Methods
286

287
288     /**
289      * Dispatch to the specified method.
290      */

291     protected ActionForward dispatchMethod(ActionMapping mapping,
292                                            ActionForm form,
293                                            HttpServletRequest JavaDoc request,
294                                            HttpServletResponse JavaDoc response,
295                                            String JavaDoc name) throws Exception JavaDoc {
296
297         // Make sure we have a valid method name to call.
298
// This may be null if the user hacks the query string.
299
if (name == null) {
300             return this.unspecified(mapping, form, request, response);
301         }
302
303         // Identify the method object to be dispatched to
304
Method JavaDoc method = null;
305         try {
306             method = getMethod(name);
307
308         } catch (NoSuchMethodException JavaDoc e) {
309             String JavaDoc message =
310                     messages.getMessage("dispatch.method", mapping.getPath(), name);
311             log.error(message, e);
312             throw e;
313         }
314
315         return dispatchMethod(mapping, form, request, response, name, method);
316
317     }
318
319     /**
320      * Dispatch to the specified method.
321      */

322     protected ActionForward dispatchMethod(ActionMapping mapping,
323                                            ActionForm form,
324                                            HttpServletRequest JavaDoc request,
325                                            HttpServletResponse JavaDoc response,
326                                            String JavaDoc name,
327                                            Method JavaDoc method) throws Exception JavaDoc {
328
329         ActionForward forward = null;
330         try {
331             Object JavaDoc args[] = {mapping, form, request, response};
332             forward = (ActionForward) method.invoke(actionInstance, args);
333
334         } catch (ClassCastException JavaDoc e) {
335             String JavaDoc message =
336                     messages.getMessage("dispatch.return", mapping.getPath(), name);
337             log.error(message, e);
338             throw e;
339
340         } catch (IllegalAccessException JavaDoc e) {
341             String JavaDoc message =
342                     messages.getMessage("dispatch.error", mapping.getPath(), name);
343             log.error(message, e);
344             throw e;
345
346         } catch (InvocationTargetException JavaDoc e) {
347             // Rethrow the target exception if possible so that the
348
// exception handling machinery can deal with it
349
Throwable JavaDoc t = e.getTargetException();
350             if (t instanceof Exception JavaDoc) {
351                 throw ((Exception JavaDoc) t);
352             } else {
353                 String JavaDoc message =
354                         messages.getMessage("dispatch.error", mapping.getPath(), name);
355                 log.error(message, e);
356                 throw new ServletException JavaDoc(t);
357             }
358         }
359
360         // Return the returned ActionForward instance
361
return (forward);
362     }
363
364
365     /**
366      * Introspect the current class to identify a method of the specified
367      * name that accepts the same parameter types as the <code>execute</code>
368      * method does.
369      *
370      * @param name Name of the method to be introspected
371      * @throws NoSuchMethodException if no such method can be found
372      */

373     protected Method JavaDoc getMethod(String JavaDoc name)
374             throws NoSuchMethodException JavaDoc {
375
376         synchronized (methods) {
377             Method JavaDoc method = (Method JavaDoc) methods.get(name);
378             if (method == null) {
379                 method = clazz.getMethod(name, types);
380                 methods.put(name, method);
381             }
382             return (method);
383         }
384
385     }
386
387     /**
388      * <p>Returns the parameter value as influenced by the selected
389      * {@link #flavor} specified for this <code>ActionDispatcher</code>.</p>
390      *
391      * @param mapping The ActionMapping used to select this instance
392      * @param form The optional ActionForm bean for this request (if any)
393      * @param request The HTTP request we are processing
394      * @param response The HTTP response we are creating
395      * @return The <code>ActionMapping</code> parameter's value
396      */

397     protected String JavaDoc getParameter(ActionMapping mapping,
398                                   ActionForm form,
399                                   HttpServletRequest JavaDoc request,
400                                   HttpServletResponse JavaDoc response)
401             throws Exception JavaDoc {
402
403         String JavaDoc parameter = mapping.getParameter();
404         if ("".equals(parameter)) {
405             parameter = null;
406         }
407         
408         if ((parameter == null) && (flavor == DEFAULT_FLAVOR)) {
409             // use "method" for DEFAULT_FLAVOR if no parameter was provided
410
return "method";
411         }
412
413         if ((parameter == null) &&
414                 ((flavor == MAPPING_FLAVOR || flavor == DISPATCH_FLAVOR))) {
415             String JavaDoc message =
416                     messages.getMessage("dispatch.handler", mapping.getPath());
417
418             log.error(message);
419
420             throw new ServletException JavaDoc(message);
421         }
422
423         return parameter;
424
425     }
426
427     /**
428      * Returns the method name, given a parameter's value.
429      *
430      * @param mapping The ActionMapping used to select this instance
431      * @param form The optional ActionForm bean for this request (if any)
432      * @param request The HTTP request we are processing
433      * @param response The HTTP response we are creating
434      * @param parameter The <code>ActionMapping</code> parameter's name
435      * @return The method's name.
436      */

437     protected String JavaDoc getMethodName(ActionMapping mapping,
438                                    ActionForm form,
439                                    HttpServletRequest JavaDoc request,
440                                    HttpServletResponse JavaDoc response,
441                                    String JavaDoc parameter)
442             throws Exception JavaDoc {
443
444
445         // "Mapping" flavor, defaults to "method"
446
if (flavor == MAPPING_FLAVOR) {
447             return parameter;
448         }
449
450         // default behaviour
451
return request.getParameter(parameter);
452     }
453
454     /**
455      * <p>Returns <code>true</code> if the current form's cancel button was
456      * pressed. This method will check if the <code>Globals.CANCEL_KEY</code>
457      * request attribute has been set, which normally occurs if the cancel
458      * button generated by <strong>CancelTag</strong> was pressed by the user
459      * in the current request. If <code>true</code>, validation performed
460      * by an <strong>ActionForm</strong>'s <code>validate()</code> method
461      * will have been skipped by the controller servlet.</p>
462      *
463      * @param request The servlet request we are processing
464      * @see org.apache.struts.taglib.html.CancelTag
465      */

466     protected boolean isCancelled(HttpServletRequest JavaDoc request) {
467
468         return (request.getAttribute(Globals.CANCEL_KEY) != null);
469
470     }
471
472 }
473
474
Popular Tags