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.validation;
018:
019: import java.beans.PropertyEditor;
020: import java.util.HashMap;
021: import java.util.Map;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025:
026: import org.springframework.beans.ConfigurablePropertyAccessor;
027: import org.springframework.beans.MutablePropertyValues;
028: import org.springframework.beans.PropertyAccessException;
029: import org.springframework.beans.PropertyAccessorUtils;
030: import org.springframework.beans.PropertyBatchUpdateException;
031: import org.springframework.beans.PropertyEditorRegistry;
032: import org.springframework.beans.PropertyValue;
033: import org.springframework.beans.PropertyValues;
034: import org.springframework.util.Assert;
035: import org.springframework.util.ObjectUtils;
036: import org.springframework.util.PatternMatchUtils;
037: import org.springframework.util.StringUtils;
038:
039: /**
040: * Binder that allows for setting property values onto a target object,
041: * including support for validation and binding result analysis.
042: * The binding process can be customized through specifying allowed fields,
043: * required fields, custom editors, etc.
044: *
045: * <p>Note that there are potential security implications in failing to set an array
046: * of allowed fields. In the case of HTTP form POST data for example, malicious clients
047: * can attempt to subvert an application by supplying values for fields or properties
048: * that do not exist on the form. In some cases this could lead to illegal data being
049: * set on command objects <i>or their nested objects</i>. For this reason, it is
050: * <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b>
051: * on the DataBinder.
052: *
053: * <p>The binding results can be examined via the {@link BindingResult} interface,
054: * extending the {@link Errors} interface: see the {@link #getBindingResult()} method.
055: * Missing fields and property access exceptions will be converted to {@link FieldError FieldErrors},
056: * collected in the Errors instance, using the following error codes:
057: *
058: * <ul>
059: * <li>Missing field error: "required"
060: * <li>Type mismatch error: "typeMismatch"
061: * <li>Method invocation error: "methodInvocation"
062: * </ul>
063: *
064: * <p>By default, binding errors get resolved through the {@link BindingErrorProcessor}
065: * strategy, processing for missing fields and property access exceptions: see the
066: * {@link #setBindingErrorProcessor} method. You can override the default strategy
067: * if needed, for example to generate different error codes.
068: *
069: * <p>Custom validation errors can be added afterwards. You will typically want to resolve
070: * such error codes into proper user-visible error messages; this can be achieved through
071: * resolving each error via a {@link org.springframework.context.MessageSource}, which is
072: * able to resolve an {@link ObjectError}/{@link FieldError} through its
073: * {@link org.springframework.context.MessageSource#getMessage(org.springframework.context.MessageSourceResolvable, java.util.Locale)}
074: * method. The list of message codes can be customized through the {@link MessageCodesResolver}
075: * strategy: see the {@link #setMessageCodesResolver} method. {@link DefaultMessageCodesResolver}'s
076: * javadoc states details on the default resolution rules.
077: *
078: * <p>This generic data binder can be used in any kind of environment.
079: * It is typically used by Spring web MVC controllers, via the web-specific
080: * subclasses {@link org.springframework.web.bind.ServletRequestDataBinder}
081: * and {@link org.springframework.web.portlet.bind.PortletRequestDataBinder}.
082: *
083: * @author Rod Johnson
084: * @author Juergen Hoeller
085: * @author Rob Harrop
086: * @see #setAllowedFields
087: * @see #setRequiredFields
088: * @see #registerCustomEditor
089: * @see #setMessageCodesResolver
090: * @see #setBindingErrorProcessor
091: * @see #bind
092: * @see #getBindingResult
093: * @see DefaultMessageCodesResolver
094: * @see DefaultBindingErrorProcessor
095: * @see org.springframework.context.MessageSource
096: * @see org.springframework.web.bind.ServletRequestDataBinder
097: */
098: public class DataBinder implements PropertyEditorRegistry {
099:
100: /** Default object name used for binding: "target" */
101: public static final String DEFAULT_OBJECT_NAME = "target";
102:
103: /**
104: * We'll create a lot of DataBinder instances: Let's use a static logger.
105: */
106: protected static final Log logger = LogFactory
107: .getLog(DataBinder.class);
108:
109: private final Object target;
110:
111: private final String objectName;
112:
113: private AbstractPropertyBindingResult bindingResult;
114:
115: private BindException bindException;
116:
117: private boolean ignoreUnknownFields = true;
118:
119: private boolean ignoreInvalidFields = false;
120:
121: private String[] allowedFields;
122:
123: private String[] disallowedFields;
124:
125: private String[] requiredFields;
126:
127: private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
128:
129: /**
130: * Create a new DataBinder instance, with default object name.
131: * @param target target object to bind onto
132: * @see #DEFAULT_OBJECT_NAME
133: */
134: public DataBinder(Object target) {
135: this (target, DEFAULT_OBJECT_NAME);
136: }
137:
138: /**
139: * Create a new DataBinder instance.
140: * @param target target object to bind onto
141: * @param objectName name of the target object
142: */
143: public DataBinder(Object target, String objectName) {
144: Assert.notNull(target, "Target must not be null");
145: this .target = target;
146: this .objectName = objectName;
147: }
148:
149: /**
150: * Return the wrapped target object.
151: */
152: public Object getTarget() {
153: return this .target;
154: }
155:
156: /**
157: * Return the name of the bound object.
158: */
159: public String getObjectName() {
160: return this .objectName;
161: }
162:
163: /**
164: * Initialize standard JavaBean property access for this DataBinder.
165: * <p>This is the default; an explicit call just leads to eager initialization.
166: * @see #initDirectFieldAccess()
167: */
168: public void initBeanPropertyAccess() {
169: Assert
170: .isNull(
171: this .bindingResult,
172: "DataBinder is already initialized - call initBeanPropertyAccess before any other configuration methods");
173: this .bindingResult = new BeanPropertyBindingResult(getTarget(),
174: getObjectName());
175: }
176:
177: /**
178: * Initialize direct field access for this DataBinder,
179: * as alternative to the default bean property access.
180: * @see #initBeanPropertyAccess()
181: */
182: public void initDirectFieldAccess() {
183: Assert
184: .isNull(
185: this .bindingResult,
186: "DataBinder is already initialized - call initDirectFieldAccess before any other configuration methods");
187: this .bindingResult = new DirectFieldBindingResult(getTarget(),
188: getObjectName());
189: }
190:
191: /**
192: * Return the internal BindingResult held by this DataBinder,
193: * as AbstractPropertyBindingResult.
194: */
195: protected AbstractPropertyBindingResult getInternalBindingResult() {
196: if (this .bindingResult == null) {
197: initBeanPropertyAccess();
198: }
199: return this .bindingResult;
200: }
201:
202: /**
203: * Return the underlying PropertyAccessor of this binder's BindingResult.
204: * To be used by binder subclasses that need property checks.
205: */
206: protected ConfigurablePropertyAccessor getPropertyAccessor() {
207: return getInternalBindingResult().getPropertyAccessor();
208: }
209:
210: /**
211: * Return the BindingResult instance created by this DataBinder.
212: * This allows for convenient access to the binding results after
213: * a bind operation.
214: * @return the BindingResult instance, to be treated as BindingResult
215: * or as Errors instance (Errors is a super-interface of BindingResult)
216: * @see Errors
217: * @see #bind
218: */
219: public BindingResult getBindingResult() {
220: return getInternalBindingResult();
221: }
222:
223: /**
224: * Return the Errors instance for this data binder.
225: * @return the Errors instance, to be treated as Errors or as BindException
226: * @deprecated in favor of {@link #getBindingResult()}.
227: * Use the {@link BindException#BindException(BindingResult)} constructor
228: * to create a BindException instance if still needed.
229: * @see #getBindingResult()
230: */
231: public BindException getErrors() {
232: if (this .bindException == null) {
233: this .bindException = new BindException(getBindingResult());
234: }
235: return this .bindException;
236: }
237:
238: /**
239: * Set whether to ignore unknown fields, that is, whether to ignore bind
240: * parameters that do not have corresponding fields in the target object.
241: * <p>Default is "true". Turn this off to enforce that all bind parameters
242: * must have a matching field in the target object.
243: * <p>Note that this setting only applies to <i>binding</i> operations
244: * on this DataBinder, not to <i>retrieving</i> values via its
245: * {@link #getBindingResult() BindingResult}.
246: * @see #bind
247: */
248: public void setIgnoreUnknownFields(boolean ignoreUnknownFields) {
249: this .ignoreUnknownFields = ignoreUnknownFields;
250: }
251:
252: /**
253: * Return whether to ignore unknown fields when binding.
254: */
255: public boolean isIgnoreUnknownFields() {
256: return this .ignoreUnknownFields;
257: }
258:
259: /**
260: * Set whether to ignore invalid fields, that is, whether to ignore bind
261: * parameters that have corresponding fields in the target object which are
262: * not accessible (for example because of null values in the nested path).
263: * <p>Default is "false". Turn this on to ignore bind parameters for
264: * nested objects in non-existing parts of the target object graph.
265: * <p>Note that this setting only applies to <i>binding</i> operations
266: * on this DataBinder, not to <i>retrieving</i> values via its
267: * {@link #getBindingResult() BindingResult}.
268: * @see #bind
269: */
270: public void setIgnoreInvalidFields(boolean ignoreInvalidFields) {
271: this .ignoreInvalidFields = ignoreInvalidFields;
272: }
273:
274: /**
275: * Return whether to ignore invalid fields when binding.
276: */
277: public boolean isIgnoreInvalidFields() {
278: return this .ignoreInvalidFields;
279: }
280:
281: /**
282: * Register fields that should be allowed for binding. Default is all
283: * fields. Restrict this for example to avoid unwanted modifications
284: * by malicious users when binding HTTP request parameters.
285: * <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching
286: * can be implemented by overriding the <code>isAllowed</code> method.
287: * <p>Alternatively, specify a list of <i>disallowed</i> fields.
288: * @param allowedFields array of field names
289: * @see #setDisallowedFields
290: * @see #isAllowed(String)
291: * @see org.springframework.web.bind.ServletRequestDataBinder
292: */
293: public void setAllowedFields(String[] allowedFields) {
294: this .allowedFields = PropertyAccessorUtils
295: .canonicalPropertyNames(allowedFields);
296: }
297:
298: /**
299: * Return the fields that should be allowed for binding.
300: * @return array of field names
301: */
302: public String[] getAllowedFields() {
303: return this .allowedFields;
304: }
305:
306: /**
307: * Register fields that should <i>not</i> be allowed for binding. Default is none.
308: * Mark fields as disallowed for example to avoid unwanted modifications
309: * by malicious users when binding HTTP request parameters.
310: * <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching
311: * can be implemented by overriding the <code>isAllowed</code> method.
312: * <p>Alternatively, specify a list of <i>allowed</i> fields.
313: * @param disallowedFields array of field names
314: * @see #setAllowedFields
315: * @see #isAllowed(String)
316: * @see org.springframework.web.bind.ServletRequestDataBinder
317: */
318: public void setDisallowedFields(String[] disallowedFields) {
319: this .disallowedFields = PropertyAccessorUtils
320: .canonicalPropertyNames(disallowedFields);
321: }
322:
323: /**
324: * Return the fields that should <i>not</i> be allowed for binding.
325: * @return array of field names
326: */
327: public String[] getDisallowedFields() {
328: return this .disallowedFields;
329: }
330:
331: /**
332: * Register fields that are required for each binding process.
333: * <p>If one of the specified fields is not contained in the list of
334: * incoming property values, a corresponding "missing field" error
335: * will be created, with error code "required" (by the default
336: * binding error processor).
337: * @param requiredFields array of field names
338: * @see #setBindingErrorProcessor
339: * @see DefaultBindingErrorProcessor#MISSING_FIELD_ERROR_CODE
340: */
341: public void setRequiredFields(String[] requiredFields) {
342: this .requiredFields = PropertyAccessorUtils
343: .canonicalPropertyNames(requiredFields);
344: if (logger.isDebugEnabled()) {
345: logger
346: .debug("DataBinder requires binding of required fields ["
347: + StringUtils
348: .arrayToCommaDelimitedString(requiredFields)
349: + "]");
350: }
351: }
352:
353: /**
354: * Return the fields that are required for each binding process.
355: * @return array of field names
356: */
357: public String[] getRequiredFields() {
358: return this .requiredFields;
359: }
360:
361: /**
362: * Set whether to extract the old field value when applying a
363: * property editor to a new value for a field.
364: * <p>Default is "true", exposing previous field values to custom editors.
365: * Turn this to "false" to avoid side effects caused by getters.
366: */
367: public void setExtractOldValueForEditor(
368: boolean extractOldValueForEditor) {
369: getPropertyAccessor().setExtractOldValueForEditor(
370: extractOldValueForEditor);
371: }
372:
373: public void registerCustomEditor(Class requiredType,
374: PropertyEditor propertyEditor) {
375: getPropertyAccessor().registerCustomEditor(requiredType,
376: propertyEditor);
377: }
378:
379: public void registerCustomEditor(Class requiredType, String field,
380: PropertyEditor propertyEditor) {
381: getPropertyAccessor().registerCustomEditor(requiredType, field,
382: propertyEditor);
383: }
384:
385: public PropertyEditor findCustomEditor(Class requiredType,
386: String propertyPath) {
387: return getPropertyAccessor().findCustomEditor(requiredType,
388: propertyPath);
389: }
390:
391: /**
392: * Set the strategy to use for resolving errors into message codes.
393: * Applies the given strategy to the underlying errors holder.
394: * <p>Default is a DefaultMessageCodesResolver.
395: * @see BeanPropertyBindingResult#setMessageCodesResolver
396: * @see DefaultMessageCodesResolver
397: */
398: public void setMessageCodesResolver(
399: MessageCodesResolver messageCodesResolver) {
400: getInternalBindingResult().setMessageCodesResolver(
401: messageCodesResolver);
402: }
403:
404: /**
405: * Set the strategy to use for processing binding errors, that is,
406: * required field errors and <code>PropertyAccessException</code>s.
407: * <p>Default is a DefaultBindingErrorProcessor.
408: * @see DefaultBindingErrorProcessor
409: */
410: public void setBindingErrorProcessor(
411: BindingErrorProcessor bindingErrorProcessor) {
412: this .bindingErrorProcessor = bindingErrorProcessor;
413: }
414:
415: /**
416: * Return the strategy for processing binding errors.
417: */
418: public BindingErrorProcessor getBindingErrorProcessor() {
419: return bindingErrorProcessor;
420: }
421:
422: /**
423: * Bind the given property values to this binder's target.
424: * <p>This call can create field errors, representing basic binding
425: * errors like a required field (code "required"), or type mismatch
426: * between value and bean property (code "typeMismatch").
427: * <p>Note that the given PropertyValues should be a throwaway instance:
428: * For efficiency, it will be modified to just contain allowed fields if it
429: * implements the MutablePropertyValues interface; else, an internal mutable
430: * copy will be created for this purpose. Pass in a copy of the PropertyValues
431: * if you want your original instance to stay unmodified in any case.
432: * @param pvs property values to bind
433: * @see #doBind(org.springframework.beans.MutablePropertyValues)
434: */
435: public void bind(PropertyValues pvs) {
436: MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues) ? (MutablePropertyValues) pvs
437: : new MutablePropertyValues(pvs);
438: doBind(mpvs);
439: }
440:
441: /**
442: * Actual implementation of the binding process, working with the
443: * passed-in MutablePropertyValues instance.
444: * @param mpvs the property values to bind,
445: * as MutablePropertyValues instance
446: * @see #checkAllowedFields
447: * @see #checkRequiredFields
448: * @see #applyPropertyValues
449: */
450: protected void doBind(MutablePropertyValues mpvs) {
451: checkAllowedFields(mpvs);
452: checkRequiredFields(mpvs);
453: applyPropertyValues(mpvs);
454: }
455:
456: /**
457: * Check the given property values against the allowed fields,
458: * removing values for fields that are not allowed.
459: * @param mpvs the property values to be bound (can be modified)
460: * @see #getAllowedFields
461: * @see #isAllowed(String)
462: */
463: protected void checkAllowedFields(MutablePropertyValues mpvs) {
464: PropertyValue[] pvs = mpvs.getPropertyValues();
465: for (int i = 0; i < pvs.length; i++) {
466: PropertyValue pv = pvs[i];
467: String field = PropertyAccessorUtils
468: .canonicalPropertyName(pv.getName());
469: if (!isAllowed(field)) {
470: mpvs.removePropertyValue(pv);
471: getBindingResult().recordSuppressedField(field);
472: if (logger.isDebugEnabled()) {
473: logger
474: .debug("Field ["
475: + field
476: + "] has been removed from PropertyValues "
477: + "and will not be bound, because it has not been found in the list of allowed fields");
478: }
479: }
480: }
481: }
482:
483: /**
484: * Return if the given field is allowed for binding.
485: * Invoked for each passed-in property value.
486: * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
487: * as well as direct equality, in the specified lists of allowed fields and
488: * disallowed fields. A field matching a disallowed pattern will not be accepted
489: * even if it also happens to match a pattern in the allowed list.
490: * <p>Can be overridden in subclasses.
491: * @param field the field to check
492: * @return if the field is allowed
493: * @see #setAllowedFields
494: * @see #setDisallowedFields
495: * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
496: */
497: protected boolean isAllowed(String field) {
498: String[] allowed = getAllowedFields();
499: String[] disallowed = getDisallowedFields();
500: return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils
501: .simpleMatch(allowed, field)) && (ObjectUtils
502: .isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(
503: disallowed, field)));
504: }
505:
506: /**
507: * Check the given property values against the required fields,
508: * generating missing field errors where appropriate.
509: * @param mpvs the property values to be bound (can be modified)
510: * @see #getRequiredFields
511: * @see #getBindingErrorProcessor
512: * @see BindingErrorProcessor#processMissingFieldError
513: */
514: protected void checkRequiredFields(MutablePropertyValues mpvs) {
515: String[] requiredFields = getRequiredFields();
516: if (!ObjectUtils.isEmpty(requiredFields)) {
517: Map propertyValues = new HashMap();
518: PropertyValue[] pvs = mpvs.getPropertyValues();
519: for (int i = 0; i < pvs.length; i++) {
520: PropertyValue pv = pvs[i];
521: String canonicalName = PropertyAccessorUtils
522: .canonicalPropertyName(pv.getName());
523: propertyValues.put(canonicalName, pv);
524: }
525: for (int i = 0; i < requiredFields.length; i++) {
526: String field = requiredFields[i];
527: PropertyValue pv = (PropertyValue) propertyValues
528: .get(field);
529: if (pv == null
530: || pv.getValue() == null
531: || (pv.getValue() instanceof String && !StringUtils
532: .hasText((String) pv.getValue()))) {
533: // Use bind error processor to create FieldError.
534: getBindingErrorProcessor()
535: .processMissingFieldError(field,
536: getInternalBindingResult());
537: // Remove property from property values to bind:
538: // It has already caused a field error with a rejected value.
539: if (pv != null) {
540: mpvs.removePropertyValue(pv);
541: propertyValues.remove(field);
542: }
543: }
544: }
545: }
546: }
547:
548: /**
549: * Apply given property values to the target object.
550: * <p>Default implementation applies all of the supplied property
551: * values as bean property values. By default, unknown fields will
552: * be ignored.
553: * @param mpvs the property values to be bound (can be modified)
554: * @see #getTarget
555: * @see #getPropertyAccessor
556: * @see #isIgnoreUnknownFields
557: * @see #getBindingErrorProcessor
558: * @see BindingErrorProcessor#processPropertyAccessException
559: */
560: protected void applyPropertyValues(MutablePropertyValues mpvs) {
561: try {
562: // Bind request parameters onto target object.
563: getPropertyAccessor().setPropertyValues(mpvs,
564: isIgnoreUnknownFields(), isIgnoreInvalidFields());
565: } catch (PropertyBatchUpdateException ex) {
566: // Use bind error processor to create FieldErrors.
567: PropertyAccessException[] exs = ex
568: .getPropertyAccessExceptions();
569: for (int i = 0; i < exs.length; i++) {
570: getBindingErrorProcessor()
571: .processPropertyAccessException(exs[i],
572: getInternalBindingResult());
573: }
574: }
575: }
576:
577: /**
578: * Close this DataBinder, which may result in throwing
579: * a BindException if it encountered any errors.
580: * @return the model Map, containing target object and Errors instance
581: * @throws BindException if there were any errors in the bind operation
582: * @see BindingResult#getModel()
583: */
584: public Map close() throws BindException {
585: if (getBindingResult().hasErrors()) {
586: throw new BindException(getBindingResult());
587: }
588: return getBindingResult().getModel();
589: }
590:
591: }
|