View Javadoc

    1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
    2    *
    3    * Licensed under the Apache License, Version 2.0 (the "License");
    4    * you may not use this file except in compliance with the License.
    5    * You may obtain a copy of the License at
    6    *
    7    *     http://www.apache.org/licenses/LICENSE-2.0
    8    *
    9    * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15
    16  package org.acegisecurity.domain.validation;
    17
    18  import org.acegisecurity.domain.dao.DetachmentContextHolder;
    19
    20  import org.apache.commons.logging.Log;
    21  import org.apache.commons.logging.LogFactory;
    22
    23  import org.springframework.beans.factory.InitializingBean;
    24
    25  import org.springframework.util.Assert;
    26
    27  import org.springframework.validation.BindException;
    28  import org.springframework.validation.Errors;
    29  import org.springframework.validation.Validator;
    30
    31  import java.util.Iterator;
    32  import java.util.List;
    33  import java.util.Vector;
    34
    35
    36  /***
    37   * Default implementation of {@link ValidationManager}.
    38   *
    39   * @author Ben Alex
    40   * @author Matthew E. Porter
    41   * @version $Id: ValidationManagerImpl.java,v 1.9 2005/11/17 00:55:50 benalex Exp $
    42   */
    43  public class ValidationManagerImpl implements InitializingBean,
    44      ValidationManager {
    45      //~ Instance fields ========================================================
    46
    47      protected final Log logger = LogFactory.getLog(getClass());
    48      private IntrospectionManager introspectionManager;
    49      private ValidationRegistryManager validationRegistryManager = new ValidationRegistryManagerImpl();
    50      private boolean strictValidation = true;
    51
    52      //~ Methods ================================================================
    53
    54      public void setIntrospectionManager(
    55          IntrospectionManager introspectionManager) {
    56          this.introspectionManager = introspectionManager;
    57      }
    58
    59      public IntrospectionManager getIntrospectionManager() {
    60          return introspectionManager;
    61      }
    62
    63      /***
    64       * Indicates whether a {@link ValidatorNotFoundException} should be thrown
    65       * if any domain object does not have a corresponding
    66       * <code>Validator</code>.
    67       *
    68       * <p>
    69       * Defaults to <code>true</code>. This is a reasonable default, as callers
    70       * of <code>ValidationManager</code> should expect the object to support
    71       * validation.
    72       * </p>
    73       *
    74       * @param strictValidation set to <code>false</code> if you wish to
    75       *        silently ignore any domain object that is missing a
    76       *        <code>Validator</code>
    77       */
    78      public void setStrictValidation(boolean strictValidation) {
    79          this.strictValidation = strictValidation;
    80      }
    81
    82      public boolean isStrictValidation() {
    83          return strictValidation;
    84      }
    85
    86      public void setValidationRegistryManager(
    87          ValidationRegistryManager validationRegistryManager) {
    88          this.validationRegistryManager = validationRegistryManager;
    89      }
    90
    91      public ValidationRegistryManager getValidationRegistryManager() {
    92          return validationRegistryManager;
    93      }
    94
    95      public void afterPropertiesSet() throws Exception {
    96          Assert.notNull(validationRegistryManager,
    97              "A ValidationRegistryManager is required");
    98          Assert.notNull(introspectionManager,
    99              "An IntrospectionManager is required");
    100     }
    101
    102     /***
    103      * Validates the passed domain object, along with any children,
    104      * grandchildren, great-grandchildren etc.
    105      *
    106      * @param domainObject to validate (cannot be <code>null</code>)
    107      *
    108      * @throws BindException if a validation problem occurs
    109      * @throws ValidatorNotFoundException if no matching <code>Validator</code>
    110      *         could be found for the object or its children (only ever thrown
    111      *         if the {@link #strictValidation}) was set to
    112      *         <code>true</code>).
    113      */
    114     public void validate(Object domainObject)
    115         throws BindException, ValidatorNotFoundException {
    116         // Abort if null
    117         Assert.notNull(domainObject,
    118             "Cannot validate a null domain object, as unable to getClass()");
    119
    120         // Construct a list of objects to be validated and adds self
    121         List<Object> allObjects = new Vector<Object>();
    122         allObjects.add(domainObject);
    123
    124         // Add all children (and grandchildren, great-grandchildren etc)
    125         // of domain object to the list of objects to be validated
    126         // (list never contains null)
    127         obtainAllChildren(domainObject, allObjects);
    128
    129         Assert.notEmpty(allObjects,
    130             "The list of objects to be validated was empty");
    131
    132         // Process list of objects to be validated by validating each
    133         Iterator<Object> iter = allObjects.iterator();
    134
    135         while (iter.hasNext()) {
    136             Object currentDomainObject = iter.next();
    137             Class clazz = currentDomainObject.getClass();
    138
    139             DetachmentContextHolder.setForceReturnOfDetachedInstances(true);
    140             try {
    141                 // Call bindSupport() if this class wishes
    142                 BindBeforeValidationUtils.bindIfRequired(currentDomainObject);
    143
    144                 Errors errors = new BindException(currentDomainObject,
    145                         clazz.getName());
    146                 Validator v = findValidator(clazz);
    147
    148                 // Perform validation
    149                 v.validate(currentDomainObject, errors);
    150
    151                 // Handle validation outcome
    152                 if (errors.getErrorCount() == 0) {
    153                     if (logger.isDebugEnabled()) {
    154                         logger.debug("Validated '" + clazz + "' successfully using '"
    155 							+ v.getClass() + "'");
    156                     }
    157                 } else {
    158                     if (logger.isDebugEnabled()) {
    159                         logger.debug("Validated '" + clazz + "' using '" + v.getClass()
    160                             + "' but errors detected");
    161                     }
    162
    163                     throw (BindException) errors;
    164                 }
    165             } catch (ValidatorNotFoundException validatorNotFoundException) {
    166                 if (strictValidation) {
    167                     if (logger.isErrorEnabled()) {
    168                         logger.error(validatorNotFoundException);
    169                     }
    170
    171                     throw validatorNotFoundException;
    172                 }
    173
    174                 if (logger.isDebugEnabled()) {
    175                     logger.debug("Could not locate validator for class '"
    176                         + clazz + "'; skipping without error");
    177                 }
    178             } finally {
    179             	DetachmentContextHolder.setForceReturnOfDetachedInstances(false);
    180             }
    181         }
    182     }
    183
    184     private Validator findValidator(Class clazz)
    185         throws ValidatorNotFoundException {
    186         Assert.notNull(clazz, "Class cannot be null");
    187
    188         Validator validator = this.validationRegistryManager.findValidator(clazz);
    189
    190         if (validator == null) {
    191             throw new ValidatorNotFoundException(
    192                 "No Validator found for class '" + clazz + "'");
    193         }
    194
    195         return validator;
    196     }
    197
    198     /***
    199      * Locates all immediate children of the passed <code>parentObject</code>,
    200      * adding each of those immediate children to the <code>allObjects</code>
    201      * list and then calling this same method for each of those immediate
    202      * children.
    203      *
    204      * <p>
    205      * Does <b>not</b> add the passed <code>parentObject</code> to the
    206      * <code>allObjects</code> list. The caller of this method should ensure
    207      * the <code>parentObject</code> is added to the list instead.
    208      * </p>
    209      *
    210      * @param parentObject the object we wish to locate all children for
    211      * @param allObjects the list to add the located children to
    212      */
    213     private void obtainAllChildren(Object parentObject, List<Object> allObjects) {
    214         Assert.notNull(parentObject, "Violation of parentObject method contract");
    215         Assert.notNull(allObjects, "Violation of allObjects method contract");
    216         Assert.isTrue(allObjects.contains(parentObject),
    217             "List of objects missing the requested parentObject");
    218
    219 		if (logger.isDebugEnabled()) {
    220 			logger.debug("Searching for children of " + parentObject);
    221 		}
    222
    223         // Add immediate children of this domain object
    224         List<Object> currentChildren = new Vector<Object>();
    225         introspectionManager.obtainImmediateChildren(parentObject,
    226             currentChildren);
    227
    228         // Now iterate the children, adding their children to the object list
    229         Iterator<Object> childrenIter = currentChildren.iterator();
    230
    231         while (childrenIter.hasNext()) {
    232             Object childObject = childrenIter.next();
    233
    234             if (childObject != null) {
    235 				if (allObjects.contains(childObject)) {
    236 					if (logger.isDebugEnabled()) {
    237 						logger.debug("Already processed this object (will not re-add): " + childObject);
    238 					}
    239 				} else {
    240 					if (logger.isDebugEnabled()) {
    241 						logger.debug("New child object found; adding child object to list of objects, and searching for its children: " + childObject);
    242 					}
    243 					allObjects.add(childObject);
    244 	                obtainAllChildren(childObject, allObjects);
    245 				}
    246             }
    247         }
    248     }
    249 }