KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > mvc > BaseCommandController


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.web.servlet.mvc;
18
19 import javax.servlet.http.HttpServletRequest JavaDoc;
20
21 import org.springframework.beans.BeanUtils;
22 import org.springframework.beans.PropertyEditorRegistrar;
23 import org.springframework.validation.BindException;
24 import org.springframework.validation.BindingErrorProcessor;
25 import org.springframework.validation.MessageCodesResolver;
26 import org.springframework.validation.ValidationUtils;
27 import org.springframework.validation.Validator;
28 import org.springframework.web.bind.ServletRequestDataBinder;
29
30 /**
31  * <p>Controller implementation which creates an object (the command object) on
32  * receipt of a request and attempts to populate this object with request parameters.</p>
33  *
34  * <p>This controller is the base for all controllers wishing to populate
35  * JavaBeans based on request parameters, validate the content of such
36  * JavaBeans using {@link org.springframework.validation.Validator Validators}
37  * and use custom editors (in the form of
38  * {@link java.beans.PropertyEditor PropertyEditors}) to transform
39  * objects into strings and vice versa, for example. Three notions are mentioned here:</p>
40  *
41  * <p><b>Command class:</b><br>
42  * An instance of the command class will be created for each request and populated
43  * with request parameters. A command class can basically be any Java class; the only
44  * requirement is a no-arg constructor. The command class should preferably be a
45  * JavaBean in order to be able to populate bean properties with request parameters.</p>
46  *
47  * <p><b>Populating using request parameters and PropertyEditors:</b><br>
48  * Upon receiving a request, any BaseCommandController will attempt to fill the
49  * command object using the request parameters. This is done using the typical
50  * and well-known JavaBeans property notation. When a request parameter named
51  * <code>'firstName'</code> exists, the framework will attempt to call
52  * <code>setFirstName([value])</code> passing the value of the parameter. Nested properties
53  * are of course supported. For instance a parameter named <code>'address.city'</code>
54  * will result in a <code>getAddress().setCity([value])</code> call on the
55  * command class.</p>
56  *
57  * <p>It's important to realise that you are not limited to String arguments in
58  * your JavaBeans. Using the PropertyEditor-notion as supplied by the
59  * java.beans package, you will be able to transform Strings to Objects and
60  * the other way around. For instance <code>setLocale(Locale loc)</code> is
61  * perfectly possible for a request parameter named <code>locale</code> having
62  * a value of <code>en</code>, as long as you register the appropriate
63  * PropertyEditor in the Controller (see {@link #initBinder initBinder()}
64  * for more information on that matter.</p>
65  *
66  * <p><b>Validators:</b>
67  * After the controller has successfully populated the command object with
68  * parameters from the request, it will use any configured validators to
69  * validate the object. Validation results will be put in a
70  * {@link org.springframework.validation.Errors Errors} object which can be
71  * used in a View to render any input problems.</p>
72  *
73  * <p><b><a name="workflow">Workflow
74  * (<a HREF="AbstractController.html#workflow">and that defined by superclass</a>):</b><br>
75  * Since this class is an abstract base class for more specific implementation,
76  * it does not override the handleRequestInternal() method and also has no
77  * actual workflow. Implementing classes like
78  * {@link AbstractFormController AbstractFormController},
79  * {@link AbstractCommandController AbstractcommandController},
80  * {@link SimpleFormController SimpleFormController} and
81  * {@link AbstractWizardFormController AbstractWizardFormController}
82  * provide actual functionality and workflow.
83  * More information on workflow performed by superclasses can be found
84  * <a HREF="AbstractController.html#workflow">here</a>.</p>
85  *
86  * <p><b><a name="config">Exposed configuration properties</a>
87  * (<a HREF="AbstractController.html#config">and those defined by superclass</a>):</b><br>
88  * <table border="1">
89  * <tr>
90  * <td><b>name</b></th>
91  * <td><b>default</b></td>
92  * <td><b>description</b></td>
93  * </tr>
94  * <tr>
95  * <td>commandName</td>
96  * <td>command</td>
97  * <td>the name to use when binding the instantiated command class
98  * to the request</td>
99  * </tr>
100  * <tr>
101  * <td>commandClass</td>
102  * <td><i>null</i></td>
103  * <td>the class to use upon receiving a request and which to fill
104  * using the request parameters. What object is used and whether
105  * or not it should be created is defined by extending classes
106  * and their configuration properties and methods.</td>
107  * </tr>
108  * <tr>
109  * <td>validators</td>
110  * <td><i>null</i></td>
111  * <td>Array of Validator beans. The validator will be called at appropriate
112  * places in the workflow of subclasses (have a look at those for more info)
113  * to validate the command object.</td>
114  * </tr>
115  * <tr>
116  * <td>validator</td>
117  * <td><i>null</i></td>
118  * <td>Short-form property for setting only one Validator bean (usually passed in
119  * using a &lt;ref bean="beanId"/&gt; property.</td>
120  * </tr>
121  * <tr>
122  * <td>validateOnBinding</td>
123  * <td>true</td>
124  * <td>Indicates whether or not to validate the command object after the
125  * object has been populated with request parameters.</td>
126  * </tr>
127  * </table>
128  * </p>
129  *
130  * @author Rod Johnson
131  * @author Juergen Hoeller
132  */

133 public abstract class BaseCommandController extends AbstractController {
134
135     /** Default command name used for binding command objects: "command" */
136     public static final String JavaDoc DEFAULT_COMMAND_NAME = "command";
137
138
139     private String JavaDoc commandName = DEFAULT_COMMAND_NAME;
140
141     private Class JavaDoc commandClass;
142
143     private Validator[] validators;
144
145     private boolean validateOnBinding = true;
146
147     private MessageCodesResolver messageCodesResolver;
148
149     private BindingErrorProcessor bindingErrorProcessor;
150
151     private PropertyEditorRegistrar[] propertyEditorRegistrars;
152
153
154     /**
155      * Set the name of the command in the model.
156      * The command object will be included in the model under this name.
157      */

158     public final void setCommandName(String JavaDoc commandName) {
159         this.commandName = commandName;
160     }
161
162     /**
163      * Return the name of the command in the model.
164      */

165     public final String JavaDoc getCommandName() {
166         return this.commandName;
167     }
168
169     /**
170      * Set the command class for this controller.
171      * An instance of this class gets populated and validated on each request.
172      */

173     public final void setCommandClass(Class JavaDoc commandClass) {
174         this.commandClass = commandClass;
175     }
176
177     /**
178      * Return the command class for this controller.
179      */

180     public final Class JavaDoc getCommandClass() {
181         return this.commandClass;
182     }
183
184     /**
185      * Set the primary Validator for this controller. The Validator
186      * must support the specified command class. If there are one
187      * or more existing validators set already when this method is
188      * called, only the specified validator will be kept. Use
189      * {@link #setValidators(Validator[])} to set multiple validators.
190      */

191     public final void setValidator(Validator validator) {
192         this.validators = new Validator[] {validator};
193     }
194
195     /**
196      * Return the primary Validator for this controller.
197      */

198     public final Validator getValidator() {
199         return (this.validators != null && this.validators.length > 0 ? this.validators[0] : null);
200     }
201
202     /**
203      * Set the Validators for this controller.
204      * The Validator must support the specified command class.
205      */

206     public final void setValidators(Validator[] validators) {
207         this.validators = validators;
208     }
209
210     /**
211      * Return the Validators for this controller.
212      */

213     public final Validator[] getValidators() {
214         return validators;
215     }
216
217     /**
218      * Set if the Validator should get applied when binding.
219      */

220     public final void setValidateOnBinding(boolean validateOnBinding) {
221         this.validateOnBinding = validateOnBinding;
222     }
223
224     /**
225      * Return if the Validator should get applied when binding.
226      */

227     public final boolean isValidateOnBinding() {
228         return validateOnBinding;
229     }
230
231     /**
232      * Set the strategy to use for resolving errors into message codes.
233      * Applies the given strategy to all data binders used by this controller.
234      * <p>Default is <code>null</code>, i.e. using the default strategy of
235      * the data binder.
236      * @see #createBinder
237      * @see org.springframework.validation.DataBinder#setMessageCodesResolver
238      */

239     public final void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
240         this.messageCodesResolver = messageCodesResolver;
241     }
242
243     /**
244      * Return the strategy to use for resolving errors into message codes.
245      */

246     public final MessageCodesResolver getMessageCodesResolver() {
247         return messageCodesResolver;
248     }
249
250     /**
251      * Set the strategy to use for processing binding errors, that is,
252      * required field errors and <code>PropertyAccessException</code>s.
253      * <p>Default is <code>null</code>, that is, using the default strategy
254      * of the data binder.
255      * @see #createBinder
256      * @see org.springframework.validation.DataBinder#setBindingErrorProcessor
257      */

258     public final void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) {
259         this.bindingErrorProcessor = bindingErrorProcessor;
260     }
261
262     /**
263      * Return the strategy to use for processing binding errors.
264      */

265     public final BindingErrorProcessor getBindingErrorProcessor() {
266         return bindingErrorProcessor;
267     }
268
269     /**
270      * Specify a single PropertyEditorRegistrar to be applied
271      * to every DataBinder that this controller uses.
272      * <p>Allows for factoring out the registration of PropertyEditors
273      * to separate objects, as an alternative to {@link #initBinder}.
274      * @see #initBinder
275      */

276     public final void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) {
277         this.propertyEditorRegistrars = new PropertyEditorRegistrar[] {propertyEditorRegistrar};
278     }
279
280     /**
281      * Specify multiple PropertyEditorRegistrars to be applied
282      * to every DataBinder that this controller uses.
283      * <p>Allows for factoring out the registration of PropertyEditors
284      * to separate objects, as an alternative to {@link #initBinder}.
285      * @see #initBinder
286      */

287     public final void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
288         this.propertyEditorRegistrars = propertyEditorRegistrars;
289     }
290
291     /**
292      * Return the PropertyEditorRegistrars to be applied
293      * to every DataBinder that this controller uses.
294      */

295     public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() {
296         return propertyEditorRegistrars;
297     }
298
299
300     protected void initApplicationContext() {
301         if (this.validators != null) {
302             for (int i = 0; i < this.validators.length; i++) {
303                 if (this.commandClass != null && !this.validators[i].supports(this.commandClass))
304                     throw new IllegalArgumentException JavaDoc("Validator [" + this.validators[i] +
305                             "] does not support command class [" +
306                             this.commandClass.getName() + "]");
307             }
308         }
309     }
310
311
312     /**
313      * Retrieve a command object for the given request.
314      * <p>The default implementation calls {@link #createCommand}.
315      * Subclasses can override this.
316      * @param request current HTTP request
317      * @return object command to bind onto
318      * @throws Exception if the command object could not be obtained
319      * @see #createCommand
320      */

321     protected Object JavaDoc getCommand(HttpServletRequest JavaDoc request) throws Exception JavaDoc {
322         return createCommand();
323     }
324
325     /**
326      * Create a new command instance for the command class of this controller.
327      * <p>This implementation uses <code>BeanUtils.instantiateClass</code>,
328      * so the command needs to have a no-arg constructor (supposed to be
329      * public, but not required to).
330      * @return the new command instance
331      * @throws Exception if the command object could not be instantiated
332      * @see org.springframework.beans.BeanUtils#instantiateClass(Class)
333      */

334     protected final Object JavaDoc createCommand() throws Exception JavaDoc {
335         if (this.commandClass == null) {
336             throw new IllegalStateException JavaDoc("Cannot create command without commandClass being set - " +
337                     "either set commandClass or (in a form controller) override formBackingObject");
338         }
339         if (logger.isDebugEnabled()) {
340             logger.debug("Creating new command of class [" + this.commandClass.getName() + "]");
341         }
342         return BeanUtils.instantiateClass(this.commandClass);
343     }
344
345     /**
346      * Check if the given command object is a valid for this controller,
347      * i.e. its command class.
348      * @param command the command object to check
349      * @return if the command object is valid for this controller
350      */

351     protected final boolean checkCommand(Object JavaDoc command) {
352         return (this.commandClass == null || this.commandClass.isInstance(command));
353     }
354
355
356     /**
357      * Bind the parameters of the given request to the given command object.
358      * @param request current HTTP request
359      * @param command the command to bind onto
360      * @return the ServletRequestDataBinder instance for additional custom validation
361      * @throws Exception in case of invalid state or arguments
362      */

363     protected final ServletRequestDataBinder bindAndValidate(HttpServletRequest JavaDoc request, Object JavaDoc command)
364             throws Exception JavaDoc {
365
366         ServletRequestDataBinder binder = createBinder(request, command);
367         BindException errors = new BindException(binder.getBindingResult());
368         if (!suppressBinding(request)) {
369             binder.bind(request);
370             onBind(request, command, errors);
371             if (this.validators != null && isValidateOnBinding() && !suppressValidation(request, command)) {
372                 for (int i = 0; i < this.validators.length; i++) {
373                     ValidationUtils.invokeValidator(this.validators[i], command, errors);
374                 }
375             }
376             onBindAndValidate(request, command, errors);
377         }
378         return binder;
379     }
380
381     /**
382      * Return whether to suppress binding for the given request.
383      * <p>The default implementation always returns "false". Can be overridden
384      * in subclasses to suppress validation, for example, if a special
385      * request parameter is set.
386      * @param request current HTTP request
387      * @return whether to suppress binding for the given request
388      * @see #suppressValidation
389      */

390     protected boolean suppressBinding(HttpServletRequest JavaDoc request) {
391         return false;
392     }
393
394     /**
395      * Create a new binder instance for the given command and request.
396      * <p>Called by {@link #bindAndValidate}. Can be overridden to plug in
397      * custom ServletRequestDataBinder instances.
398      * <p>The default implementation creates a standard ServletRequestDataBinder
399      * and invokes {@link #prepareBinder} and {@link #initBinder}.
400      * <p>Note that neither {@link #prepareBinder} nor {@link #initBinder} will
401      * be invoked automatically if you override this method! Call those methods
402      * at appropriate points of your overridden method.
403      * @param request current HTTP request
404      * @param command the command to bind onto
405      * @return the new binder instance
406      * @throws Exception in case of invalid state or arguments
407      * @see #bindAndValidate
408      * @see #prepareBinder
409      * @see #initBinder
410      */

411     protected ServletRequestDataBinder createBinder(HttpServletRequest JavaDoc request, Object JavaDoc command)
412         throws Exception JavaDoc {
413
414         ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName());
415         prepareBinder(binder);
416         initBinder(request, binder);
417         return binder;
418     }
419
420     /**
421      * Prepare the given binder, applying the specified MessageCodesResolver,
422      * BindingErrorProcessor and PropertyEditorRegistrars (if any).
423      * Called by {@link #createBinder}.
424      * @param binder the new binder instance
425      * @see #createBinder
426      * @see #setMessageCodesResolver
427      * @see #setBindingErrorProcessor
428      */

429     protected final void prepareBinder(ServletRequestDataBinder binder) {
430         if (useDirectFieldAccess()) {
431             binder.initDirectFieldAccess();
432         }
433         if (this.messageCodesResolver != null) {
434             binder.setMessageCodesResolver(this.messageCodesResolver);
435         }
436         if (this.bindingErrorProcessor != null) {
437             binder.setBindingErrorProcessor(this.bindingErrorProcessor);
438         }
439         if (this.propertyEditorRegistrars != null) {
440             for (int i = 0; i < this.propertyEditorRegistrars.length; i++) {
441                 this.propertyEditorRegistrars[i].registerCustomEditors(binder);
442             }
443         }
444     }
445
446     /**
447      * Determine whether to use direct field access instead of bean property access.
448      * Applied by {@link #prepareBinder}.
449      * <p>Default is "false". Can be overridden in subclasses.
450      * @see #prepareBinder
451      * @see org.springframework.validation.DataBinder#initDirectFieldAccess()
452      */

453     protected boolean useDirectFieldAccess() {
454         return false;
455     }
456
457     /**
458      * Initialize the given binder instance, for example with custom editors.
459      * Called by {@link #createBinder}.
460      * <p>This method allows you to register custom editors for certain fields of your
461      * command class. For instance, you will be able to transform Date objects into a
462      * String pattern and back, in order to allow your JavaBeans to have Date properties
463      * and still be able to set and display them in an HTML interface.
464      * <p>The default implementation is empty.
465      * @param request current HTTP request
466      * @param binder the new binder instance
467      * @throws Exception in case of invalid state or arguments
468      * @see #createBinder
469      * @see org.springframework.validation.DataBinder#registerCustomEditor
470      * @see org.springframework.beans.propertyeditors.CustomDateEditor
471      */

472     protected void initBinder(HttpServletRequest JavaDoc request, ServletRequestDataBinder binder)
473         throws Exception JavaDoc {
474     }
475
476     /**
477      * Callback for custom post-processing in terms of binding.
478      * Called on each submit, after standard binding but before validation.
479      * <p>The default implementation delegates to {@link #onBind(HttpServletRequest, Object)}.
480      * @param request current HTTP request
481      * @param command the command object to perform further binding on
482      * @param errors validation errors holder, allowing for additional
483      * custom registration of binding errors
484      * @throws Exception in case of invalid state or arguments
485      * @see #bindAndValidate
486      * @see #onBind(HttpServletRequest, Object)
487      */

488     protected void onBind(HttpServletRequest JavaDoc request, Object JavaDoc command, BindException errors)
489             throws Exception JavaDoc {
490
491         onBind(request, command);
492     }
493
494     /**
495      * Callback for custom post-processing in terms of binding.
496      * <p>Called by the default implementation of the
497      * {@link #onBind(HttpServletRequest, Object, BindException)} variant
498      * with all parameters, after standard binding but before validation.
499      * <p>The default implementation is empty.
500      * @param request current HTTP request
501      * @param command the command object to perform further binding on
502      * @throws Exception in case of invalid state or arguments
503      * @see #onBind(HttpServletRequest, Object, BindException)
504      */

505     protected void onBind(HttpServletRequest JavaDoc request, Object JavaDoc command) throws Exception JavaDoc {
506     }
507
508     /**
509      * Return whether to suppress validation for the given request.
510      * <p>The default implementation delegates to {@link #suppressValidation(HttpServletRequest)}.
511      * @param request current HTTP request
512      * @param command the command object to validate
513      * @return whether to suppress validation for the given request
514      */

515     protected boolean suppressValidation(HttpServletRequest JavaDoc request, Object JavaDoc command) {
516         return suppressValidation(request);
517     }
518
519     /**
520      * Return whether to suppress validation for the given request.
521      * <p>Called by the default implementation of the
522      * {@link #suppressValidation(HttpServletRequest, Object)} variant
523      * with all parameters.
524      * <p>The default implementation is empty.
525      * @param request current HTTP request
526      * @return whether to suppress validation for the given request
527      */

528     protected boolean suppressValidation(HttpServletRequest JavaDoc request) {
529         return false;
530     }
531
532     /**
533      * Callback for custom post-processing in terms of binding and validation.
534      * Called on each submit, after standard binding and validation,
535      * but before error evaluation.
536      * <p>The default implementation is empty.
537      * @param request current HTTP request
538      * @param command the command object, still allowing for further binding
539      * @param errors validation errors holder, allowing for additional
540      * custom validation
541      * @throws Exception in case of invalid state or arguments
542      * @see #bindAndValidate
543      * @see org.springframework.validation.Errors
544      */

545     protected void onBindAndValidate(HttpServletRequest JavaDoc request, Object JavaDoc command, BindException errors)
546             throws Exception JavaDoc {
547     }
548
549 }
550
Popular Tags