1
2
3
4
5
6
7
8
9
10
11
12
13
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
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
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
117 Assert.notNull(domainObject,
118 "Cannot validate a null domain object, as unable to getClass()");
119
120
121 List<Object> allObjects = new Vector<Object>();
122 allObjects.add(domainObject);
123
124
125
126
127 obtainAllChildren(domainObject, allObjects);
128
129 Assert.notEmpty(allObjects,
130 "The list of objects to be validated was empty");
131
132
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
142 BindBeforeValidationUtils.bindIfRequired(currentDomainObject);
143
144 Errors errors = new BindException(currentDomainObject,
145 clazz.getName());
146 Validator v = findValidator(clazz);
147
148
149 v.validate(currentDomainObject, errors);
150
151
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
224 List<Object> currentChildren = new Vector<Object>();
225 introspectionManager.obtainImmediateChildren(parentObject,
226 currentChildren);
227
228
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 }