001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet.mvc;
018:
019: import javax.servlet.http.HttpServletRequest;
020:
021: import org.springframework.beans.BeanUtils;
022: import org.springframework.beans.PropertyEditorRegistrar;
023: import org.springframework.validation.BindException;
024: import org.springframework.validation.BindingErrorProcessor;
025: import org.springframework.validation.MessageCodesResolver;
026: import org.springframework.validation.ValidationUtils;
027: import org.springframework.validation.Validator;
028: import org.springframework.web.bind.ServletRequestDataBinder;
029:
030: /**
031: * <p>Controller implementation which creates an object (the command object) on
032: * receipt of a request and attempts to populate this object with request parameters.</p>
033: *
034: * <p>This controller is the base for all controllers wishing to populate
035: * JavaBeans based on request parameters, validate the content of such
036: * JavaBeans using {@link org.springframework.validation.Validator Validators}
037: * and use custom editors (in the form of
038: * {@link java.beans.PropertyEditor PropertyEditors}) to transform
039: * objects into strings and vice versa, for example. Three notions are mentioned here:</p>
040: *
041: * <p><b>Command class:</b><br>
042: * An instance of the command class will be created for each request and populated
043: * with request parameters. A command class can basically be any Java class; the only
044: * requirement is a no-arg constructor. The command class should preferably be a
045: * JavaBean in order to be able to populate bean properties with request parameters.</p>
046: *
047: * <p><b>Populating using request parameters and PropertyEditors:</b><br>
048: * Upon receiving a request, any BaseCommandController will attempt to fill the
049: * command object using the request parameters. This is done using the typical
050: * and well-known JavaBeans property notation. When a request parameter named
051: * <code>'firstName'</code> exists, the framework will attempt to call
052: * <code>setFirstName([value])</code> passing the value of the parameter. Nested properties
053: * are of course supported. For instance a parameter named <code>'address.city'</code>
054: * will result in a <code>getAddress().setCity([value])</code> call on the
055: * command class.</p>
056: *
057: * <p>It's important to realise that you are not limited to String arguments in
058: * your JavaBeans. Using the PropertyEditor-notion as supplied by the
059: * java.beans package, you will be able to transform Strings to Objects and
060: * the other way around. For instance <code>setLocale(Locale loc)</code> is
061: * perfectly possible for a request parameter named <code>locale</code> having
062: * a value of <code>en</code>, as long as you register the appropriate
063: * PropertyEditor in the Controller (see {@link #initBinder initBinder()}
064: * for more information on that matter.</p>
065: *
066: * <p><b>Validators:</b>
067: * After the controller has successfully populated the command object with
068: * parameters from the request, it will use any configured validators to
069: * validate the object. Validation results will be put in a
070: * {@link org.springframework.validation.Errors Errors} object which can be
071: * used in a View to render any input problems.</p>
072: *
073: * <p><b><a name="workflow">Workflow
074: * (<a href="AbstractController.html#workflow">and that defined by superclass</a>):</b><br>
075: * Since this class is an abstract base class for more specific implementation,
076: * it does not override the handleRequestInternal() method and also has no
077: * actual workflow. Implementing classes like
078: * {@link AbstractFormController AbstractFormController},
079: * {@link AbstractCommandController AbstractcommandController},
080: * {@link SimpleFormController SimpleFormController} and
081: * {@link AbstractWizardFormController AbstractWizardFormController}
082: * provide actual functionality and workflow.
083: * More information on workflow performed by superclasses can be found
084: * <a href="AbstractController.html#workflow">here</a>.</p>
085: *
086: * <p><b><a name="config">Exposed configuration properties</a>
087: * (<a href="AbstractController.html#config">and those defined by superclass</a>):</b><br>
088: * <table border="1">
089: * <tr>
090: * <td><b>name</b></th>
091: * <td><b>default</b></td>
092: * <td><b>description</b></td>
093: * </tr>
094: * <tr>
095: * <td>commandName</td>
096: * <td>command</td>
097: * <td>the name to use when binding the instantiated command class
098: * to the request</td>
099: * </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 <ref bean="beanId"/> 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 DEFAULT_COMMAND_NAME = "command";
137:
138: private String commandName = DEFAULT_COMMAND_NAME;
139:
140: private Class commandClass;
141:
142: private Validator[] validators;
143:
144: private boolean validateOnBinding = true;
145:
146: private MessageCodesResolver messageCodesResolver;
147:
148: private BindingErrorProcessor bindingErrorProcessor;
149:
150: private PropertyEditorRegistrar[] propertyEditorRegistrars;
151:
152: /**
153: * Set the name of the command in the model.
154: * The command object will be included in the model under this name.
155: */
156: public final void setCommandName(String commandName) {
157: this .commandName = commandName;
158: }
159:
160: /**
161: * Return the name of the command in the model.
162: */
163: public final String getCommandName() {
164: return this .commandName;
165: }
166:
167: /**
168: * Set the command class for this controller.
169: * An instance of this class gets populated and validated on each request.
170: */
171: public final void setCommandClass(Class commandClass) {
172: this .commandClass = commandClass;
173: }
174:
175: /**
176: * Return the command class for this controller.
177: */
178: public final Class getCommandClass() {
179: return this .commandClass;
180: }
181:
182: /**
183: * Set the primary Validator for this controller. The Validator
184: * must support the specified command class. If there are one
185: * or more existing validators set already when this method is
186: * called, only the specified validator will be kept. Use
187: * {@link #setValidators(Validator[])} to set multiple validators.
188: */
189: public final void setValidator(Validator validator) {
190: this .validators = new Validator[] { validator };
191: }
192:
193: /**
194: * Return the primary Validator for this controller.
195: */
196: public final Validator getValidator() {
197: return (this .validators != null && this .validators.length > 0 ? this .validators[0]
198: : null);
199: }
200:
201: /**
202: * Set the Validators for this controller.
203: * The Validator must support the specified command class.
204: */
205: public final void setValidators(Validator[] validators) {
206: this .validators = validators;
207: }
208:
209: /**
210: * Return the Validators for this controller.
211: */
212: public final Validator[] getValidators() {
213: return validators;
214: }
215:
216: /**
217: * Set if the Validator should get applied when binding.
218: */
219: public final void setValidateOnBinding(boolean validateOnBinding) {
220: this .validateOnBinding = validateOnBinding;
221: }
222:
223: /**
224: * Return if the Validator should get applied when binding.
225: */
226: public final boolean isValidateOnBinding() {
227: return validateOnBinding;
228: }
229:
230: /**
231: * Set the strategy to use for resolving errors into message codes.
232: * Applies the given strategy to all data binders used by this controller.
233: * <p>Default is <code>null</code>, i.e. using the default strategy of
234: * the data binder.
235: * @see #createBinder
236: * @see org.springframework.validation.DataBinder#setMessageCodesResolver
237: */
238: public final void setMessageCodesResolver(
239: 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(
259: BindingErrorProcessor bindingErrorProcessor) {
260: this .bindingErrorProcessor = bindingErrorProcessor;
261: }
262:
263: /**
264: * Return the strategy to use for processing binding errors.
265: */
266: public final BindingErrorProcessor getBindingErrorProcessor() {
267: return bindingErrorProcessor;
268: }
269:
270: /**
271: * Specify a single PropertyEditorRegistrar to be applied
272: * to every DataBinder that this controller uses.
273: * <p>Allows for factoring out the registration of PropertyEditors
274: * to separate objects, as an alternative to {@link #initBinder}.
275: * @see #initBinder
276: */
277: public final void setPropertyEditorRegistrar(
278: PropertyEditorRegistrar propertyEditorRegistrar) {
279: this .propertyEditorRegistrars = new PropertyEditorRegistrar[] { propertyEditorRegistrar };
280: }
281:
282: /**
283: * Specify multiple PropertyEditorRegistrars to be applied
284: * to every DataBinder that this controller uses.
285: * <p>Allows for factoring out the registration of PropertyEditors
286: * to separate objects, as an alternative to {@link #initBinder}.
287: * @see #initBinder
288: */
289: public final void setPropertyEditorRegistrars(
290: PropertyEditorRegistrar[] propertyEditorRegistrars) {
291: this .propertyEditorRegistrars = propertyEditorRegistrars;
292: }
293:
294: /**
295: * Return the PropertyEditorRegistrars to be applied
296: * to every DataBinder that this controller uses.
297: */
298: public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() {
299: return propertyEditorRegistrars;
300: }
301:
302: protected void initApplicationContext() {
303: if (this .validators != null) {
304: for (int i = 0; i < this .validators.length; i++) {
305: if (this .commandClass != null
306: && !this .validators[i]
307: .supports(this .commandClass))
308: throw new IllegalArgumentException("Validator ["
309: + this .validators[i]
310: + "] does not support command class ["
311: + this .commandClass.getName() + "]");
312: }
313: }
314: }
315:
316: /**
317: * Retrieve a command object for the given request.
318: * <p>The default implementation calls {@link #createCommand}.
319: * Subclasses can override this.
320: * @param request current HTTP request
321: * @return object command to bind onto
322: * @throws Exception if the command object could not be obtained
323: * @see #createCommand
324: */
325: protected Object getCommand(HttpServletRequest request)
326: throws Exception {
327: return createCommand();
328: }
329:
330: /**
331: * Create a new command instance for the command class of this controller.
332: * <p>This implementation uses <code>BeanUtils.instantiateClass</code>,
333: * so the command needs to have a no-arg constructor (supposed to be
334: * public, but not required to).
335: * @return the new command instance
336: * @throws Exception if the command object could not be instantiated
337: * @see org.springframework.beans.BeanUtils#instantiateClass(Class)
338: */
339: protected final Object createCommand() throws Exception {
340: if (this .commandClass == null) {
341: throw new IllegalStateException(
342: "Cannot create command without commandClass being set - "
343: + "either set commandClass or (in a form controller) override formBackingObject");
344: }
345: if (logger.isDebugEnabled()) {
346: logger.debug("Creating new command of class ["
347: + this .commandClass.getName() + "]");
348: }
349: return BeanUtils.instantiateClass(this .commandClass);
350: }
351:
352: /**
353: * Check if the given command object is a valid for this controller,
354: * i.e. its command class.
355: * @param command the command object to check
356: * @return if the command object is valid for this controller
357: */
358: protected final boolean checkCommand(Object command) {
359: return (this .commandClass == null || this .commandClass
360: .isInstance(command));
361: }
362:
363: /**
364: * Bind the parameters of the given request to the given command object.
365: * @param request current HTTP request
366: * @param command the command to bind onto
367: * @return the ServletRequestDataBinder instance for additional custom validation
368: * @throws Exception in case of invalid state or arguments
369: */
370: protected final ServletRequestDataBinder bindAndValidate(
371: HttpServletRequest request, Object command)
372: throws Exception {
373:
374: ServletRequestDataBinder binder = createBinder(request, command);
375: BindException errors = new BindException(binder
376: .getBindingResult());
377: if (!suppressBinding(request)) {
378: binder.bind(request);
379: onBind(request, command, errors);
380: if (this .validators != null && isValidateOnBinding()
381: && !suppressValidation(request, command, errors)) {
382: for (int i = 0; i < this .validators.length; i++) {
383: ValidationUtils.invokeValidator(this .validators[i],
384: command, errors);
385: }
386: }
387: onBindAndValidate(request, command, errors);
388: }
389: return binder;
390: }
391:
392: /**
393: * Return whether to suppress binding for the given request.
394: * <p>The default implementation always returns "false". Can be overridden
395: * in subclasses to suppress validation, for example, if a special
396: * request parameter is set.
397: * @param request current HTTP request
398: * @return whether to suppress binding for the given request
399: * @see #suppressValidation
400: */
401: protected boolean suppressBinding(HttpServletRequest request) {
402: return false;
403: }
404:
405: /**
406: * Create a new binder instance for the given command and request.
407: * <p>Called by {@link #bindAndValidate}. Can be overridden to plug in
408: * custom ServletRequestDataBinder instances.
409: * <p>The default implementation creates a standard ServletRequestDataBinder
410: * and invokes {@link #prepareBinder} and {@link #initBinder}.
411: * <p>Note that neither {@link #prepareBinder} nor {@link #initBinder} will
412: * be invoked automatically if you override this method! Call those methods
413: * at appropriate points of your overridden method.
414: * @param request current HTTP request
415: * @param command the command to bind onto
416: * @return the new binder instance
417: * @throws Exception in case of invalid state or arguments
418: * @see #bindAndValidate
419: * @see #prepareBinder
420: * @see #initBinder
421: */
422: protected ServletRequestDataBinder createBinder(
423: HttpServletRequest request, Object command)
424: throws Exception {
425:
426: ServletRequestDataBinder binder = new ServletRequestDataBinder(
427: command, getCommandName());
428: prepareBinder(binder);
429: initBinder(request, binder);
430: return binder;
431: }
432:
433: /**
434: * Prepare the given binder, applying the specified MessageCodesResolver,
435: * BindingErrorProcessor and PropertyEditorRegistrars (if any).
436: * Called by {@link #createBinder}.
437: * @param binder the new binder instance
438: * @see #createBinder
439: * @see #setMessageCodesResolver
440: * @see #setBindingErrorProcessor
441: */
442: protected final void prepareBinder(ServletRequestDataBinder binder) {
443: if (useDirectFieldAccess()) {
444: binder.initDirectFieldAccess();
445: }
446: if (this .messageCodesResolver != null) {
447: binder.setMessageCodesResolver(this .messageCodesResolver);
448: }
449: if (this .bindingErrorProcessor != null) {
450: binder.setBindingErrorProcessor(this .bindingErrorProcessor);
451: }
452: if (this .propertyEditorRegistrars != null) {
453: for (int i = 0; i < this .propertyEditorRegistrars.length; i++) {
454: this .propertyEditorRegistrars[i]
455: .registerCustomEditors(binder);
456: }
457: }
458: }
459:
460: /**
461: * Determine whether to use direct field access instead of bean property access.
462: * Applied by {@link #prepareBinder}.
463: * <p>Default is "false". Can be overridden in subclasses.
464: * @return whether to use direct field access (<code>true</code>)
465: * or bean property access (<code>false</code>)
466: * @see #prepareBinder
467: * @see org.springframework.validation.DataBinder#initDirectFieldAccess()
468: */
469: protected boolean useDirectFieldAccess() {
470: return false;
471: }
472:
473: /**
474: * Initialize the given binder instance, for example with custom editors.
475: * Called by {@link #createBinder}.
476: * <p>This method allows you to register custom editors for certain fields of your
477: * command class. For instance, you will be able to transform Date objects into a
478: * String pattern and back, in order to allow your JavaBeans to have Date properties
479: * and still be able to set and display them in an HTML interface.
480: * <p>The default implementation is empty.
481: * @param request current HTTP request
482: * @param binder the new binder instance
483: * @throws Exception in case of invalid state or arguments
484: * @see #createBinder
485: * @see org.springframework.validation.DataBinder#registerCustomEditor
486: * @see org.springframework.beans.propertyeditors.CustomDateEditor
487: */
488: protected void initBinder(HttpServletRequest request,
489: ServletRequestDataBinder binder) throws Exception {
490: }
491:
492: /**
493: * Callback for custom post-processing in terms of binding.
494: * Called on each submit, after standard binding but before validation.
495: * <p>The default implementation delegates to {@link #onBind(HttpServletRequest, Object)}.
496: * @param request current HTTP request
497: * @param command the command object to perform further binding on
498: * @param errors validation errors holder, allowing for additional
499: * custom registration of binding errors
500: * @throws Exception in case of invalid state or arguments
501: * @see #bindAndValidate
502: * @see #onBind(HttpServletRequest, Object)
503: */
504: protected void onBind(HttpServletRequest request, Object command,
505: BindException errors) throws Exception {
506:
507: onBind(request, command);
508: }
509:
510: /**
511: * Callback for custom post-processing in terms of binding.
512: * <p>Called by the default implementation of the
513: * {@link #onBind(HttpServletRequest, Object, BindException)} variant
514: * with all parameters, after standard binding but before validation.
515: * <p>The default implementation is empty.
516: * @param request current HTTP request
517: * @param command the command object to perform further binding on
518: * @throws Exception in case of invalid state or arguments
519: * @see #onBind(HttpServletRequest, Object, BindException)
520: */
521: protected void onBind(HttpServletRequest request, Object command)
522: throws Exception {
523: }
524:
525: /**
526: * Return whether to suppress validation for the given request.
527: * <p>The default implementation delegates to {@link #suppressValidation(HttpServletRequest, Object)}.
528: * @param request current HTTP request
529: * @param command the command object to validate
530: * @param errors validation errors holder, allowing for additional
531: * custom registration of binding errors
532: * @return whether to suppress validation for the given request
533: */
534: protected boolean suppressValidation(HttpServletRequest request,
535: Object command, BindException errors) {
536: return suppressValidation(request, command);
537: }
538:
539: /**
540: * Return whether to suppress validation for the given request.
541: * <p>Called by the default implementation of the
542: * {@link #suppressValidation(HttpServletRequest, Object, BindException)} variant
543: * with all parameters.
544: * <p>The default implementation delegates to {@link #suppressValidation(HttpServletRequest)}.
545: * @param request current HTTP request
546: * @param command the command object to validate
547: * @return whether to suppress validation for the given request
548: */
549: protected boolean suppressValidation(HttpServletRequest request,
550: Object command) {
551: return suppressValidation(request);
552: }
553:
554: /**
555: * Return whether to suppress validation for the given request.
556: * <p>Called by the default implementation of the
557: * {@link #suppressValidation(HttpServletRequest, Object)} variant
558: * with all parameters.
559: * <p>The default implementation is empty.
560: * @param request current HTTP request
561: * @return whether to suppress validation for the given request
562: * @deprecated as of Spring 2.0.4, in favor of the
563: * {@link #suppressValidation(HttpServletRequest, Object)} variant
564: */
565: protected boolean suppressValidation(HttpServletRequest request) {
566: return false;
567: }
568:
569: /**
570: * Callback for custom post-processing in terms of binding and validation.
571: * Called on each submit, after standard binding and validation,
572: * but before error evaluation.
573: * <p>The default implementation is empty.
574: * @param request current HTTP request
575: * @param command the command object, still allowing for further binding
576: * @param errors validation errors holder, allowing for additional
577: * custom validation
578: * @throws Exception in case of invalid state or arguments
579: * @see #bindAndValidate
580: * @see org.springframework.validation.Errors
581: */
582: protected void onBindAndValidate(HttpServletRequest request,
583: Object command, BindException errors) throws Exception {
584: }
585:
586: }
|