001 /******************************************************************************* 002 * Portions created by Sebastian Thomschke are copyright (c) 2005-2012 Sebastian 003 * Thomschke. 004 * 005 * All Rights Reserved. This program and the accompanying materials 006 * are made available under the terms of the Eclipse Public License v1.0 007 * which accompanies this distribution, and is available at 008 * http://www.eclipse.org/legal/epl-v10.html 009 * 010 * Contributors: 011 * Sebastian Thomschke - initial implementation. 012 *******************************************************************************/ 013 package net.sf.oval; 014 015 import static java.lang.Boolean.*; 016 017 import java.lang.reflect.Constructor; 018 import java.lang.reflect.Field; 019 import java.lang.reflect.Method; 020 import java.util.Collection; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.Set; 024 import java.util.WeakHashMap; 025 026 import net.sf.oval.collection.CollectionFactory; 027 import net.sf.oval.collection.CollectionFactoryJDKImpl; 028 import net.sf.oval.collection.CollectionFactoryJavalutionImpl; 029 import net.sf.oval.collection.CollectionFactoryTroveImpl; 030 import net.sf.oval.configuration.Configurer; 031 import net.sf.oval.configuration.annotation.AnnotationsConfigurer; 032 import net.sf.oval.configuration.annotation.JPAAnnotationsConfigurer; 033 import net.sf.oval.configuration.pojo.POJOConfigurer; 034 import net.sf.oval.configuration.pojo.elements.ClassConfiguration; 035 import net.sf.oval.configuration.pojo.elements.ConstraintSetConfiguration; 036 import net.sf.oval.configuration.pojo.elements.ConstructorConfiguration; 037 import net.sf.oval.configuration.pojo.elements.FieldConfiguration; 038 import net.sf.oval.configuration.pojo.elements.MethodConfiguration; 039 import net.sf.oval.configuration.pojo.elements.ObjectConfiguration; 040 import net.sf.oval.configuration.pojo.elements.ParameterConfiguration; 041 import net.sf.oval.configuration.xml.XMLConfigurer; 042 import net.sf.oval.constraint.AssertConstraintSetCheck; 043 import net.sf.oval.constraint.AssertFieldConstraintsCheck; 044 import net.sf.oval.constraint.AssertValidCheck; 045 import net.sf.oval.constraint.NotNullCheck; 046 import net.sf.oval.context.ConstructorParameterContext; 047 import net.sf.oval.context.FieldContext; 048 import net.sf.oval.context.MethodParameterContext; 049 import net.sf.oval.context.MethodReturnValueContext; 050 import net.sf.oval.context.OValContext; 051 import net.sf.oval.exception.ConstraintSetAlreadyDefinedException; 052 import net.sf.oval.exception.ConstraintsViolatedException; 053 import net.sf.oval.exception.ExceptionTranslator; 054 import net.sf.oval.exception.FieldNotFoundException; 055 import net.sf.oval.exception.InvalidConfigurationException; 056 import net.sf.oval.exception.MethodNotFoundException; 057 import net.sf.oval.exception.OValException; 058 import net.sf.oval.exception.ReflectionException; 059 import net.sf.oval.exception.UndefinedConstraintSetException; 060 import net.sf.oval.exception.ValidationFailedException; 061 import net.sf.oval.expression.ExpressionLanguageRegistry; 062 import net.sf.oval.guard.ParameterNameResolver; 063 import net.sf.oval.guard.ParameterNameResolverEnumerationImpl; 064 import net.sf.oval.internal.ClassChecks; 065 import net.sf.oval.internal.ContextCache; 066 import net.sf.oval.internal.Log; 067 import net.sf.oval.internal.MessageRenderer; 068 import net.sf.oval.internal.util.ArrayUtils; 069 import net.sf.oval.internal.util.Assert; 070 import net.sf.oval.internal.util.IdentitySet; 071 import net.sf.oval.internal.util.LinkedSet; 072 import net.sf.oval.internal.util.ReflectionUtils; 073 import net.sf.oval.internal.util.StringUtils; 074 import net.sf.oval.internal.util.ThreadLocalLinkedList; 075 import net.sf.oval.localization.context.OValContextRenderer; 076 import net.sf.oval.localization.context.ToStringValidationContextRenderer; 077 import net.sf.oval.localization.message.MessageResolver; 078 import net.sf.oval.localization.message.ResourceBundleMessageResolver; 079 import net.sf.oval.localization.value.MessageValueFormatter; 080 import net.sf.oval.localization.value.ToStringMessageValueFormatter; 081 import net.sf.oval.logging.LoggerFactory; 082 import net.sf.oval.ogn.ObjectGraphNavigationResult; 083 import net.sf.oval.ogn.ObjectGraphNavigatorRegistry; 084 085 /** 086 * <p>Instances of this class can validate objects based on declared constraints. 087 * Constraints can either be declared using OVal's constraint annotations, XML configuration 088 * files or EJB3 JPA annotations.</p> 089 * 090 * <p>This class is thread-safe.</p> 091 * 092 * @author Sebastian Thomschke 093 * 094 * @see AnnotationsConfigurer 095 * @see JPAAnnotationsConfigurer 096 * @see POJOConfigurer 097 * @see XMLConfigurer 098 */ 099 public class Validator implements IValidator 100 { 101 protected static final class DelegatingParameterNameResolver implements ParameterNameResolver 102 { 103 private ParameterNameResolver delegate; 104 105 public DelegatingParameterNameResolver(final ParameterNameResolver delegate) 106 { 107 this.delegate = delegate; 108 } 109 110 public ParameterNameResolver getDelegate() 111 { 112 return delegate; 113 } 114 115 /** 116 * {@inheritDoc} 117 */ 118 public String[] getParameterNames(final Constructor< ? > constructor) throws ReflectionException 119 { 120 return delegate.getParameterNames(constructor); 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 public String[] getParameterNames(final Method method) throws ReflectionException 127 { 128 return delegate.getParameterNames(method); 129 } 130 131 public void setDelegate(final ParameterNameResolver delegate) 132 { 133 this.delegate = delegate; 134 } 135 } 136 137 private static final Log LOG = Log.getLog(Validator.class); 138 139 private static CollectionFactory collectionFactory = _createDefaultCollectionFactory(); 140 private static OValContextRenderer contextRenderer = ToStringValidationContextRenderer.INSTANCE; 141 142 private static MessageResolver messageResolver; 143 private static MessageValueFormatter messageValueFormatter = ToStringMessageValueFormatter.INSTANCE; 144 145 private static CollectionFactory _createDefaultCollectionFactory() 146 { 147 // if Javolution collection classes are found use them by default 148 if (ReflectionUtils.isClassPresent("javolution.util.FastMap") 149 && ReflectionUtils.isClassPresent("javolution.util.FastSet") 150 && ReflectionUtils.isClassPresent("javolution.util.FastTable")) 151 { 152 LOG.info("javolution.util collection classes are available."); 153 154 return new CollectionFactoryJavalutionImpl(); 155 } 156 // else if Trove collection classes are found use them by default 157 else if (ReflectionUtils.isClassPresent("gnu.trove.THashMap") 158 && ReflectionUtils.isClassPresent("gnu.trove.THashSet")) 159 { 160 LOG.info("gnu.trove collection classes are available."); 161 162 return new CollectionFactoryTroveImpl(); 163 } 164 // else use JDK collection classes by default 165 else 166 return new CollectionFactoryJDKImpl(); 167 } 168 169 /** 170 * Returns a shared instance of the CollectionFactory 171 */ 172 public static CollectionFactory getCollectionFactory() 173 { 174 return collectionFactory; 175 } 176 177 /** 178 * @return the contextRenderer 179 */ 180 public static OValContextRenderer getContextRenderer() 181 { 182 return contextRenderer; 183 } 184 185 /** 186 * @return the loggerFactory 187 */ 188 public static LoggerFactory getLoggerFactory() 189 { 190 return Log.getLoggerFactory(); 191 } 192 193 /** 194 * @return the messageResolver 195 */ 196 public static MessageResolver getMessageResolver() 197 { 198 /* 199 * since ResourceBundleMessageResolver references getCollectionFactory() of this class 200 * we are lazy referencing the resolvers shared instance. 201 */ 202 if (messageResolver == null) messageResolver = ResourceBundleMessageResolver.INSTANCE; 203 return messageResolver; 204 } 205 206 /** 207 * @return the messageValueFormatter 208 */ 209 public static MessageValueFormatter getMessageValueFormatter() 210 { 211 return messageValueFormatter; 212 } 213 214 /** 215 * 216 * @param factory the new collection factory to be used by all validator instances 217 */ 218 public static void setCollectionFactory(final CollectionFactory factory) throws IllegalArgumentException 219 { 220 Assert.argumentNotNull("factory", factory); 221 Validator.collectionFactory = factory; 222 } 223 224 /** 225 * @param contextRenderer the contextRenderer to set 226 */ 227 public static void setContextRenderer(final OValContextRenderer contextRenderer) 228 { 229 Assert.argumentNotNull("contextRenderer", contextRenderer); 230 Validator.contextRenderer = contextRenderer; 231 } 232 233 /** 234 * @param loggerFactory the loggerFactory to set 235 */ 236 public static void setLoggerFactory(final LoggerFactory loggerFactory) 237 { 238 Assert.argumentNotNull("loggerFactory", loggerFactory); 239 Log.setLoggerFactory(loggerFactory); 240 } 241 242 /** 243 * @param messageResolver the messageResolver to set 244 * @throws IllegalArgumentException if <code>messageResolver == null</code> 245 */ 246 public static void setMessageResolver(final MessageResolver messageResolver) throws IllegalArgumentException 247 { 248 Assert.argumentNotNull("messageResolver", messageResolver); 249 Validator.messageResolver = messageResolver; 250 } 251 252 /** 253 * @param formatter the messageValueFormatter to set 254 */ 255 public static void setMessageValueFormatter(final MessageValueFormatter formatter) 256 { 257 Assert.argumentNotNull("formatter", formatter); 258 Validator.messageValueFormatter = formatter; 259 } 260 261 private final Map<Class< ? >, ClassChecks> checksByClass = new WeakHashMap<Class< ? >, ClassChecks>(); 262 263 private final List<Configurer> configurers = new LinkedSet<Configurer>(4); 264 265 private final Map<String, ConstraintSet> constraintSetsById = collectionFactory.createMap(4); 266 267 protected final ThreadLocalLinkedList<Set<Object>> currentlyValidatedObjects = new ThreadLocalLinkedList<Set<Object>>(); 268 269 protected final ThreadLocalLinkedList<List<ConstraintViolation>> currentViolations = new ThreadLocalLinkedList<List<ConstraintViolation>>(); 270 271 private final Set<String> disabledProfiles = collectionFactory.createSet(); 272 273 private final Set<String> enabledProfiles = collectionFactory.createSet(); 274 275 protected final ExpressionLanguageRegistry expressionLanguageRegistry = new ExpressionLanguageRegistry(); 276 277 private ExceptionTranslator exceptionTranslator; 278 279 private boolean isAllProfilesEnabledByDefault = true; 280 281 /** 282 * Flag that indicates any configuration method related to profiles was called. 283 * Used for performance improvements. 284 */ 285 private boolean isProfilesFeatureUsed = false; 286 287 protected final ObjectGraphNavigatorRegistry ognRegistry = new ObjectGraphNavigatorRegistry(); 288 289 protected final DelegatingParameterNameResolver parameterNameResolver = new DelegatingParameterNameResolver( 290 new ParameterNameResolverEnumerationImpl()); 291 292 /** 293 * Constructs a new validator instance and uses a new instance of AnnotationsConfigurer 294 */ 295 public Validator() 296 { 297 ReflectionUtils.assertPrivateAccessAllowed(); 298 configurers.add(new AnnotationsConfigurer()); 299 } 300 301 /** 302 * Constructs a new validator instance and configures it using the given configurers 303 * 304 * @param configurers 305 */ 306 public Validator(final Collection<Configurer> configurers) 307 { 308 ReflectionUtils.assertPrivateAccessAllowed(); 309 if (configurers != null) this.configurers.addAll(configurers); 310 } 311 312 /** 313 * Constructs a new validator instance and configures it using the given configurers 314 * 315 * @param configurers 316 */ 317 public Validator(final Configurer... configurers) 318 { 319 ReflectionUtils.assertPrivateAccessAllowed(); 320 if (configurers != null) for (final Configurer configurer : configurers) 321 this.configurers.add(configurer); 322 } 323 324 private void _addChecks(final ClassChecks cc, final ClassConfiguration classCfg) 325 throws InvalidConfigurationException, ReflectionException 326 { 327 if (TRUE.equals(classCfg.overwrite)) cc.clear(); 328 329 if (classCfg.checkInvariants != null) cc.isCheckInvariants = classCfg.checkInvariants; 330 331 // cache the result for better performance 332 final boolean applyFieldConstraintsToConstructors = TRUE.equals(classCfg.applyFieldConstraintsToConstructors); 333 final boolean applyFieldConstraintsToSetters = TRUE.equals(classCfg.applyFieldConstraintsToSetters); 334 final boolean assertParametersNotNull = TRUE.equals(classCfg.assertParametersNotNull); 335 final NotNullCheck sharedNotNullCheck = assertParametersNotNull ? new NotNullCheck() : null; 336 337 try 338 { 339 /* ****************************** 340 * apply object level checks 341 * ******************************/ 342 if (classCfg.objectConfiguration != null) 343 { 344 final ObjectConfiguration objectCfg = classCfg.objectConfiguration; 345 346 if (TRUE.equals(objectCfg.overwrite)) cc.clearObjectChecks(); 347 cc.addObjectChecks(objectCfg.checks); 348 } 349 350 /* ****************************** 351 * apply field checks 352 * ******************************/ 353 if (classCfg.fieldConfigurations != null) 354 for (final FieldConfiguration fieldCfg : classCfg.fieldConfigurations) 355 { 356 final Field field = classCfg.type.getDeclaredField(fieldCfg.name); 357 358 if (TRUE.equals(fieldCfg.overwrite)) cc.clearFieldChecks(field); 359 360 if (fieldCfg.checks != null && fieldCfg.checks.size() > 0) 361 cc.addFieldChecks(field, fieldCfg.checks); 362 } 363 364 /* ****************************** 365 * apply constructor parameter checks 366 * ******************************/ 367 if (classCfg.constructorConfigurations != null) 368 for (final ConstructorConfiguration ctorCfg : classCfg.constructorConfigurations) 369 { 370 // ignore constructors without parameters 371 if (ctorCfg.parameterConfigurations == null) continue; 372 373 final Class< ? >[] paramTypes = new Class[ctorCfg.parameterConfigurations.size()]; 374 375 for (int i = 0, l = ctorCfg.parameterConfigurations.size(); i < l; i++) 376 paramTypes[i] = ctorCfg.parameterConfigurations.get(i).type; 377 378 final Constructor< ? > ctor = classCfg.type.getDeclaredConstructor(paramTypes); 379 380 if (TRUE.equals(ctorCfg.overwrite)) cc.clearConstructorChecks(ctor); 381 382 if (TRUE.equals(ctorCfg.postCheckInvariants)) cc.methodsWithCheckInvariantsPost.add(ctor); 383 384 final String[] paramNames = parameterNameResolver.getParameterNames(ctor); 385 386 for (int i = 0, l = ctorCfg.parameterConfigurations.size(); i < l; i++) 387 { 388 final ParameterConfiguration paramCfg = ctorCfg.parameterConfigurations.get(i); 389 390 if (TRUE.equals(paramCfg.overwrite)) cc.clearConstructorParameterChecks(ctor, i); 391 392 if (paramCfg.hasChecks()) cc.addConstructorParameterChecks(ctor, i, paramCfg.checks); 393 394 if (paramCfg.hasCheckExclusions()) 395 cc.addConstructorParameterCheckExclusions(ctor, i, paramCfg.checkExclusions); 396 397 if (assertParametersNotNull) cc.addConstructorParameterChecks(ctor, i, sharedNotNullCheck); 398 399 /* ******************* 400 * applying field constraints to the single parameter of setter methods 401 * *******************/ 402 if (applyFieldConstraintsToConstructors) 403 { 404 final Field field = ReflectionUtils.getField(cc.clazz, paramNames[i]); 405 406 // check if a corresponding field has been found 407 if (field != null && paramTypes[i].isAssignableFrom(field.getType())) 408 { 409 final AssertFieldConstraintsCheck check = new AssertFieldConstraintsCheck(); 410 check.setFieldName(field.getName()); 411 cc.addConstructorParameterChecks(ctor, i, check); 412 } 413 } 414 } 415 } 416 417 /* ****************************** 418 * apply method parameter and return value checks and pre/post conditions 419 * ******************************/ 420 if (classCfg.methodConfigurations != null) 421 for (final MethodConfiguration methodCfg : classCfg.methodConfigurations) 422 { 423 /* ****************************** 424 * determine the method 425 * ******************************/ 426 final Method method; 427 428 if (methodCfg.parameterConfigurations == null || methodCfg.parameterConfigurations.size() == 0) 429 method = classCfg.type.getDeclaredMethod(methodCfg.name); 430 else 431 { 432 final Class< ? >[] paramTypes = new Class[methodCfg.parameterConfigurations.size()]; 433 434 for (int i = 0, l = methodCfg.parameterConfigurations.size(); i < l; i++) 435 paramTypes[i] = methodCfg.parameterConfigurations.get(i).type; 436 437 method = classCfg.type.getDeclaredMethod(methodCfg.name, paramTypes); 438 } 439 440 if (TRUE.equals(methodCfg.overwrite)) cc.clearMethodChecks(method); 441 442 /* ****************************** 443 * applying field constraints to the single parameter of setter methods 444 * ******************************/ 445 if (applyFieldConstraintsToSetters) 446 { 447 final Field field = ReflectionUtils.getFieldForSetter(method); 448 449 // check if a corresponding field has been found 450 if (field != null) 451 { 452 final AssertFieldConstraintsCheck check = new AssertFieldConstraintsCheck(); 453 check.setFieldName(field.getName()); 454 cc.addMethodParameterChecks(method, 0, check); 455 } 456 } 457 458 /* ****************************** 459 * configure parameter constraints 460 * ******************************/ 461 if (methodCfg.parameterConfigurations != null && methodCfg.parameterConfigurations.size() > 0) 462 for (int i = 0, l = methodCfg.parameterConfigurations.size(); i < l; i++) 463 { 464 final ParameterConfiguration paramCfg = methodCfg.parameterConfigurations.get(i); 465 466 if (TRUE.equals(paramCfg.overwrite)) cc.clearMethodParameterChecks(method, i); 467 468 if (paramCfg.hasChecks()) cc.addMethodParameterChecks(method, i, paramCfg.checks); 469 470 if (paramCfg.hasCheckExclusions()) 471 cc.addMethodParameterCheckExclusions(method, i, paramCfg.checkExclusions); 472 473 if (assertParametersNotNull) cc.addMethodParameterChecks(method, i, sharedNotNullCheck); 474 } 475 476 /* ****************************** 477 * configure return value constraints 478 * ******************************/ 479 if (methodCfg.returnValueConfiguration != null) 480 { 481 if (TRUE.equals(methodCfg.returnValueConfiguration.overwrite)) 482 cc.clearMethodReturnValueChecks(method); 483 484 if (methodCfg.returnValueConfiguration.checks != null 485 && methodCfg.returnValueConfiguration.checks.size() > 0) 486 cc.addMethodReturnValueChecks(method, methodCfg.isInvariant, 487 methodCfg.returnValueConfiguration.checks); 488 } 489 490 if (TRUE.equals(methodCfg.preCheckInvariants)) cc.methodsWithCheckInvariantsPre.add(method); 491 492 /* 493 * configure pre conditions 494 */ 495 if (methodCfg.preExecutionConfiguration != null) 496 { 497 if (TRUE.equals(methodCfg.preExecutionConfiguration.overwrite)) 498 cc.clearMethodPreChecks(method); 499 500 if (methodCfg.preExecutionConfiguration.checks != null 501 && methodCfg.preExecutionConfiguration.checks.size() > 0) 502 cc.addMethodPreChecks(method, methodCfg.preExecutionConfiguration.checks); 503 } 504 505 if (TRUE.equals(methodCfg.postCheckInvariants)) cc.methodsWithCheckInvariantsPost.add(method); 506 507 /* 508 * configure post conditions 509 */ 510 if (methodCfg.postExecutionConfiguration != null) 511 { 512 if (TRUE.equals(methodCfg.postExecutionConfiguration.overwrite)) 513 cc.clearMethodPostChecks(method); 514 515 if (methodCfg.postExecutionConfiguration.checks != null 516 && methodCfg.postExecutionConfiguration.checks.size() > 0) 517 cc.addMethodPostChecks(method, methodCfg.postExecutionConfiguration.checks); 518 } 519 } 520 } 521 catch (final NoSuchMethodException ex) 522 { 523 throw new MethodNotFoundException(ex); 524 } 525 catch (final NoSuchFieldException ex) 526 { 527 throw new FieldNotFoundException(ex); 528 } 529 } 530 531 private void _checkConstraint(final List<ConstraintViolation> violations, final Check check, 532 final Object validatedObject, final Object valueToValidate, final OValContext context, 533 final String[] profiles) 534 { 535 /* 536 * special handling of the AssertValid constraint 537 */ 538 if (check instanceof AssertValidCheck) 539 { 540 checkConstraintAssertValid(violations, (AssertValidCheck) check, validatedObject, valueToValidate, context, 541 profiles); 542 return; 543 } 544 545 /* 546 * special handling of the FieldConstraints constraint 547 */ 548 if (check instanceof AssertConstraintSetCheck) 549 { 550 checkConstraintAssertConstraintSet(violations, (AssertConstraintSetCheck) check, validatedObject, 551 valueToValidate, context, profiles); 552 return; 553 } 554 555 /* 556 * special handling of the FieldConstraints constraint 557 */ 558 if (check instanceof AssertFieldConstraintsCheck) 559 { 560 checkConstraintAssertFieldConstraints(violations, (AssertFieldConstraintsCheck) check, validatedObject, 561 valueToValidate, context, profiles); 562 return; 563 } 564 565 /* 566 * standard constraints handling 567 */ 568 if (!check.isSatisfied(validatedObject, valueToValidate, context, this)) 569 { 570 final String errorMessage = renderMessage(context, valueToValidate, check.getMessage(), 571 check.getMessageVariables()); 572 violations.add(new ConstraintViolation(check, errorMessage, validatedObject, valueToValidate, context)); 573 } 574 } 575 576 /** 577 * validate validatedObject based on the constraints of the given class 578 */ 579 private void _validateObjectInvariants(final Object validatedObject, final Class< ? > clazz, 580 final List<ConstraintViolation> violations, final String[] profiles) throws ValidationFailedException 581 { 582 assert validatedObject != null; 583 assert clazz != null; 584 assert violations != null; 585 586 // abort if the root class has been reached 587 if (clazz == Object.class) return; 588 589 try 590 { 591 final ClassChecks cc = getClassChecks(clazz); 592 593 // validate field constraints 594 for (final Field field : cc.constrainedFields) 595 { 596 final Collection<Check> checks = cc.checksForFields.get(field); 597 598 if (checks != null && checks.size() > 0) 599 { 600 final Object valueToValidate = ReflectionUtils.getFieldValue(field, validatedObject); 601 final OValContext ctx = ContextCache.getFieldContext(field); 602 603 for (final Check check : checks) 604 checkConstraint(violations, check, validatedObject, valueToValidate, ctx, profiles, false); 605 } 606 } 607 608 // validate constraints on getter methods 609 for (final Method getter : cc.constrainedMethods) 610 { 611 final Collection<Check> checks = cc.checksForMethodReturnValues.get(getter); 612 613 if (checks != null && checks.size() > 0) 614 { 615 final Object valueToValidate = ReflectionUtils.invokeMethod(getter, validatedObject); 616 final OValContext ctx = ContextCache.getMethodReturnValueContext(getter); 617 618 for (final Check check : checks) 619 checkConstraint(violations, check, validatedObject, valueToValidate, ctx, profiles, false); 620 } 621 } 622 623 // validate object constraints 624 if (cc.checksForObject.size() > 0) 625 { 626 final OValContext ctx = ContextCache.getClassContext(clazz); 627 for (final Check check : cc.checksForObject) 628 checkConstraint(violations, check, validatedObject, validatedObject, ctx, profiles, false); 629 } 630 631 // if the super class is annotated to be validatable also validate it against the object 632 _validateObjectInvariants(validatedObject, clazz.getSuperclass(), violations, profiles); 633 } 634 catch (final OValException ex) 635 { 636 throw new ValidationFailedException("Object validation failed. Class: " + clazz + " Validated object: " 637 + validatedObject, ex); 638 } 639 } 640 641 /** 642 * Validates the static field and static getter constrains of the given class. 643 * Constraints specified for super classes are not taken in account. 644 */ 645 private void _validateStaticInvariants(final Class< ? > validatedClass, final List<ConstraintViolation> violations, 646 final String[] profiles) throws ValidationFailedException 647 { 648 assert validatedClass != null; 649 assert violations != null; 650 651 final ClassChecks cc = getClassChecks(validatedClass); 652 653 // validate static field constraints 654 for (final Field field : cc.constrainedStaticFields) 655 { 656 final Collection<Check> checks = cc.checksForFields.get(field); 657 658 if (checks != null && checks.size() > 0) 659 { 660 final Object valueToValidate = ReflectionUtils.getFieldValue(field, null); 661 final OValContext context = ContextCache.getFieldContext(field); 662 663 for (final Check check : checks) 664 checkConstraint(violations, check, validatedClass, valueToValidate, context, profiles, false); 665 } 666 } 667 668 // validate constraints on getter methods 669 for (final Method getter : cc.constrainedStaticMethods) 670 { 671 final Collection<Check> checks = cc.checksForMethodReturnValues.get(getter); 672 673 if (checks != null && checks.size() > 0) 674 { 675 final Object valueToValidate = ReflectionUtils.invokeMethod(getter, null); 676 final OValContext context = ContextCache.getMethodReturnValueContext(getter); 677 678 for (final Check check : checks) 679 checkConstraint(violations, check, validatedClass, valueToValidate, context, profiles, false); 680 } 681 } 682 } 683 684 /** 685 * Registers object-level constraint checks 686 * 687 * @param clazz the class to register the checks for 688 * @param checks the checks to add 689 * @throws IllegalArgumentException if <code>clazz == null</code> or <code>checks == null</code> or checks is empty 690 */ 691 public void addChecks(final Class< ? > clazz, final Check... checks) throws IllegalArgumentException 692 { 693 Assert.argumentNotNull("clazz", clazz); 694 Assert.argumentNotEmpty("checks", checks); 695 696 getClassChecks(clazz).addObjectChecks(checks); 697 } 698 699 /** 700 * Registers constraint checks for the given field 701 * 702 * @param field the field to declare the checks for 703 * @param checks the checks to add 704 * @throws IllegalArgumentException if <code>field == null</code> or <code>checks == null</code> or checks is empty 705 */ 706 public void addChecks(final Field field, final Check... checks) throws IllegalArgumentException 707 { 708 Assert.argumentNotNull("field", field); 709 Assert.argumentNotEmpty("checks", checks); 710 711 getClassChecks(field.getDeclaringClass()).addFieldChecks(field, checks); 712 } 713 714 /** 715 * Registers constraint checks for the given getter's return value 716 * 717 * @param invariantMethod a non-void, non-parameterized method (usually a JavaBean Getter style method) 718 * @param checks the checks to add 719 * @throws IllegalArgumentException if <code>getter == null</code> or <code>checks == null</code> 720 * @throws InvalidConfigurationException if getter is not a getter method 721 */ 722 public void addChecks(final Method invariantMethod, final Check... checks) throws IllegalArgumentException, 723 InvalidConfigurationException 724 { 725 Assert.argumentNotNull("invariantMethod", invariantMethod); 726 Assert.argumentNotEmpty("checks", checks); 727 728 getClassChecks(invariantMethod.getDeclaringClass()).addMethodReturnValueChecks(invariantMethod, TRUE, checks); 729 } 730 731 /** 732 * Registers a new constraint set. 733 * 734 * @param constraintSet cannot be null 735 * @param overwrite 736 * @throws ConstraintSetAlreadyDefinedException if <code>overwrite == false</code> and 737 * a constraint set with the given id exists already 738 * @throws IllegalArgumentException if <code>constraintSet == null</code> 739 * or <code>constraintSet.id == null</code> 740 * or <code>constraintSet.id.length == 0</code> 741 * @throws IllegalArgumentException if <code>constraintSet.id == null</code> 742 */ 743 public void addConstraintSet(final ConstraintSet constraintSet, final boolean overwrite) 744 throws ConstraintSetAlreadyDefinedException, IllegalArgumentException 745 { 746 Assert.argumentNotNull("constraintSet", constraintSet); 747 Assert.argumentNotEmpty("constraintSet.id", constraintSet.getId()); 748 749 synchronized (constraintSetsById) 750 { 751 if (!overwrite && constraintSetsById.containsKey(constraintSet.getId())) 752 throw new ConstraintSetAlreadyDefinedException(constraintSet.getId()); 753 754 constraintSetsById.put(constraintSet.getId(), constraintSet); 755 } 756 } 757 758 /** 759 * {@inheritDoc} 760 */ 761 public void assertValid(final Object validatedObject) throws IllegalArgumentException, ValidationFailedException, 762 ConstraintsViolatedException 763 { 764 final List<ConstraintViolation> violations = validate(validatedObject); 765 766 if (violations.size() > 0) throw translateException(new ConstraintsViolatedException(violations)); 767 } 768 769 /** 770 * {@inheritDoc} 771 */ 772 public void assertValidFieldValue(final Object validatedObject, final Field validatedField, 773 final Object fieldValueToValidate) throws IllegalArgumentException, ValidationFailedException, 774 ConstraintsViolatedException 775 { 776 final List<ConstraintViolation> violations = validateFieldValue(validatedObject, validatedField, 777 fieldValueToValidate); 778 779 if (violations.size() > 0) throw translateException(new ConstraintsViolatedException(violations)); 780 } 781 782 protected void checkConstraint(final List<ConstraintViolation> violations, final Check check, 783 Object validatedObject, Object valueToValidate, OValContext context, final String[] profiles, 784 final boolean isContainerValue) throws OValException 785 { 786 if (!isAnyProfileEnabled(check.getProfiles(), profiles)) return; 787 788 if (!check.isActive(validatedObject, valueToValidate, this)) return; 789 790 final ConstraintTarget[] targets = check.getAppliesTo(); 791 792 // only process the target expression if we are not already on a value inside the container object (collection, array, map) 793 if (!isContainerValue) 794 { 795 String target = check.getTarget(); 796 if (target != null) 797 { 798 target = target.trim(); 799 if (target.length() > 0) 800 { 801 if (valueToValidate == null) return; 802 final String[] chunks = target.split(":", 2); 803 final String ognId, path; 804 if (chunks.length == 1) 805 { 806 ognId = ""; 807 path = chunks[0]; 808 } 809 else 810 { 811 ognId = chunks[0]; 812 path = chunks[1]; 813 } 814 final ObjectGraphNavigationResult result = ognRegistry.getObjectGraphNavigator(ognId) // 815 .navigateTo(valueToValidate, path); 816 if (result == null) return; 817 validatedObject = result.targetParent; 818 valueToValidate = result.target; 819 if (result.targetAccessor instanceof Field) 820 context = ContextCache.getFieldContext((Field) result.targetAccessor); 821 else 822 context = ContextCache.getMethodReturnValueContext((Method) result.targetAccessor); 823 } 824 } 825 } 826 827 final Class< ? > compileTimeType = context.getCompileTimeType(); 828 829 final boolean isCollection = valueToValidate != null ? // 830 valueToValidate instanceof Collection< ? > : // 831 Collection.class.isAssignableFrom(compileTimeType); 832 final boolean isMap = !isCollection && // 833 (valueToValidate != null ? // 834 valueToValidate instanceof Map< ? , ? > : // 835 Map.class.isAssignableFrom(compileTimeType)); 836 final boolean isArray = !isCollection && !isMap && // 837 (valueToValidate != null ? // 838 valueToValidate.getClass().isArray() : // 839 compileTimeType.isArray()); 840 final boolean isContainer = isCollection || isMap || isArray; 841 842 if (isContainer && valueToValidate != null) 843 if (isCollection) 844 { 845 if (ArrayUtils.containsSame(targets, ConstraintTarget.VALUES)) 846 for (final Object item : (Collection< ? >) valueToValidate) 847 checkConstraint(violations, check, validatedObject, item, context, profiles, true); 848 } 849 else if (isMap) 850 { 851 if (ArrayUtils.containsSame(targets, ConstraintTarget.KEYS)) 852 for (final Object item : ((Map< ? , ? >) valueToValidate).keySet()) 853 checkConstraint(violations, check, validatedObject, item, context, profiles, true); 854 855 if (ArrayUtils.containsSame(targets, ConstraintTarget.VALUES)) 856 for (final Object item : ((Map< ? , ? >) valueToValidate).values()) 857 checkConstraint(violations, check, validatedObject, item, context, profiles, true); 858 } 859 else if (ArrayUtils.containsSame(targets, ConstraintTarget.VALUES)) 860 for (final Object item : ArrayUtils.asList(valueToValidate)) 861 checkConstraint(violations, check, validatedObject, item, context, profiles, true); 862 if (isContainerValue || !isContainer || isContainer 863 && ArrayUtils.containsSame(targets, ConstraintTarget.CONTAINER)) 864 _checkConstraint(violations, check, validatedObject, valueToValidate, context, profiles); 865 } 866 867 protected void checkConstraintAssertConstraintSet(final List<ConstraintViolation> violations, 868 final AssertConstraintSetCheck check, final Object validatedObject, final Object valueToValidate, 869 final OValContext context, final String[] profiles) throws OValException 870 { 871 final ConstraintSet cs = getConstraintSet(check.getId()); 872 873 if (cs == null) throw new UndefinedConstraintSetException(check.getId()); 874 875 final Collection<Check> referencedChecks = cs.getChecks(); 876 877 if (referencedChecks != null && referencedChecks.size() > 0) 878 for (final Check referencedCheck : referencedChecks) 879 checkConstraint(violations, referencedCheck, validatedObject, valueToValidate, context, profiles, false); 880 } 881 882 protected void checkConstraintAssertFieldConstraints(final List<ConstraintViolation> violations, 883 final AssertFieldConstraintsCheck check, final Object validatedObject, final Object valueToValidate, 884 final OValContext context, final String[] profiles) throws OValException 885 { 886 final Class< ? > targetClass; 887 888 /* 889 * set the targetClass based on the validation context 890 */ 891 if (check.getDeclaringClass() != null && check.getDeclaringClass() != Void.class) 892 targetClass = check.getDeclaringClass(); 893 else if (context instanceof ConstructorParameterContext) 894 // the class declaring the field must either be the class declaring the constructor or one of its super 895 // classes 896 targetClass = ((ConstructorParameterContext) context).getConstructor().getDeclaringClass(); 897 else if (context instanceof MethodParameterContext) 898 // the class declaring the field must either be the class declaring the method or one of its super classes 899 targetClass = ((MethodParameterContext) context).getMethod().getDeclaringClass(); 900 else if (context instanceof MethodReturnValueContext) 901 // the class declaring the field must either be the class declaring the getter or one of its super classes 902 targetClass = ((MethodReturnValueContext) context).getMethod().getDeclaringClass(); 903 else 904 // the lowest class that is expected to declare the field (or one of its super classes) 905 targetClass = validatedObject.getClass(); 906 907 // the name of the field whose constraints shall be used 908 String fieldName = check.getFieldName(); 909 910 /* 911 * calculate the field name based on the validation context if the @AssertFieldConstraints constraint didn't specify the field name 912 */ 913 if (fieldName == null || fieldName.length() == 0) 914 if (context instanceof ConstructorParameterContext) 915 fieldName = ((ConstructorParameterContext) context).getParameterName(); 916 else if (context instanceof MethodParameterContext) 917 fieldName = ((MethodParameterContext) context).getParameterName(); 918 else if (context instanceof MethodReturnValueContext) 919 fieldName = ReflectionUtils.guessFieldName(((MethodReturnValueContext) context).getMethod()); 920 921 /* 922 * find the field based on fieldName and targetClass 923 */ 924 final Field field = ReflectionUtils.getFieldRecursive(targetClass, fieldName); 925 926 if (field == null) 927 throw new FieldNotFoundException("Field <" + fieldName + "> not found in class <" + targetClass 928 + "> or its super classes."); 929 930 final ClassChecks cc = getClassChecks(field.getDeclaringClass()); 931 final Collection<Check> referencedChecks = cc.checksForFields.get(field); 932 if (referencedChecks != null && referencedChecks.size() > 0) 933 for (final Check referencedCheck : referencedChecks) 934 checkConstraint(violations, referencedCheck, validatedObject, valueToValidate, context, profiles, false); 935 } 936 937 protected void checkConstraintAssertValid(final List<ConstraintViolation> violations, final AssertValidCheck check, 938 final Object validatedObject, final Object valueToValidate, final OValContext context, 939 final String[] profiles) throws OValException 940 { 941 if (valueToValidate == null) return; 942 943 // ignore circular dependencies 944 if (isCurrentlyValidated(valueToValidate)) return; 945 946 final List<ConstraintViolation> additionalViolations = collectionFactory.createList(); 947 validateInvariants(valueToValidate, additionalViolations, profiles); 948 949 if (additionalViolations.size() != 0) 950 { 951 final String errorMessage = renderMessage(context, valueToValidate, check.getMessage(), 952 check.getMessageVariables()); 953 954 violations.add(new ConstraintViolation(check, errorMessage, validatedObject, valueToValidate, context, 955 additionalViolations)); 956 } 957 } 958 959 /** 960 * Disables all constraints profiles globally, i.e. no configured constraint will be validated. 961 */ 962 public synchronized void disableAllProfiles() 963 { 964 isProfilesFeatureUsed = true; 965 isAllProfilesEnabledByDefault = false; 966 967 enabledProfiles.clear(); 968 disabledProfiles.clear(); 969 } 970 971 /** 972 * Disables a constraints profile globally. 973 * @param profile the id of the profile 974 */ 975 public void disableProfile(final String profile) 976 { 977 isProfilesFeatureUsed = true; 978 979 if (isAllProfilesEnabledByDefault) 980 disabledProfiles.add(profile); 981 else 982 enabledProfiles.remove(profile); 983 } 984 985 /** 986 * Enables all constraints profiles globally, i.e. all configured constraint will be validated. 987 */ 988 public synchronized void enableAllProfiles() 989 { 990 isProfilesFeatureUsed = true; 991 isAllProfilesEnabledByDefault = true; 992 993 enabledProfiles.clear(); 994 disabledProfiles.clear(); 995 } 996 997 /** 998 * Enables a constraints profile globally. 999 * @param profile the id of the profile 1000 */ 1001 public void enableProfile(final String profile) 1002 { 1003 isProfilesFeatureUsed = true; 1004 1005 if (isAllProfilesEnabledByDefault) 1006 disabledProfiles.remove(profile); 1007 else 1008 enabledProfiles.add(profile); 1009 } 1010 1011 /** 1012 * Gets the object-level constraint checks for the given class 1013 * 1014 * @param clazz the class to get the checks for 1015 * @throws IllegalArgumentException if <code>clazz == null</code> 1016 */ 1017 public Check[] getChecks(final Class< ? > clazz) throws IllegalArgumentException 1018 { 1019 Assert.argumentNotNull("clazz", clazz); 1020 1021 final ClassChecks cc = getClassChecks(clazz); 1022 1023 final Set<Check> checks = cc.checksForObject; 1024 return checks == null ? null : checks.toArray(new Check[checks.size()]); 1025 } 1026 1027 /** 1028 * Gets the constraint checks for the given field 1029 * 1030 * @param field the field to get the checks for 1031 * @throws IllegalArgumentException if <code>field == null</code> 1032 */ 1033 public Check[] getChecks(final Field field) throws IllegalArgumentException 1034 { 1035 Assert.argumentNotNull("field", field); 1036 1037 final ClassChecks cc = getClassChecks(field.getDeclaringClass()); 1038 1039 final Set<Check> checks = cc.checksForFields.get(field); 1040 return checks == null ? null : checks.toArray(new Check[checks.size()]); 1041 } 1042 1043 /** 1044 * Gets the constraint checks for the given method's return value 1045 * 1046 * @param method the method to get the checks for 1047 * @throws IllegalArgumentException if <code>getter == null</code> 1048 */ 1049 public Check[] getChecks(final Method method) throws IllegalArgumentException 1050 { 1051 Assert.argumentNotNull("method", method); 1052 1053 final ClassChecks cc = getClassChecks(method.getDeclaringClass()); 1054 1055 final Set<Check> checks = cc.checksForMethodReturnValues.get(method); 1056 return checks == null ? null : checks.toArray(new Check[checks.size()]); 1057 } 1058 1059 /** 1060 * Returns the ClassChecks object for the particular class, 1061 * allowing you to modify the checks 1062 * 1063 * @param clazz cannot be null 1064 * @return returns the ClassChecks for the given class 1065 * @throws IllegalArgumentException if <code>clazz == null</code> 1066 */ 1067 protected ClassChecks getClassChecks(final Class< ? > clazz) throws IllegalArgumentException, 1068 InvalidConfigurationException, ReflectionException 1069 { 1070 Assert.argumentNotNull("clazz", clazz); 1071 1072 synchronized (checksByClass) 1073 { 1074 ClassChecks cc = checksByClass.get(clazz); 1075 1076 if (cc == null) 1077 { 1078 cc = new ClassChecks(clazz, parameterNameResolver); 1079 1080 for (final Configurer configurer : configurers) 1081 { 1082 final ClassConfiguration classConfig = configurer.getClassConfiguration(clazz); 1083 if (classConfig != null) _addChecks(cc, classConfig); 1084 } 1085 1086 checksByClass.put(clazz, cc); 1087 } 1088 1089 return cc; 1090 } 1091 } 1092 1093 /** 1094 * @return the internal linked set with the registered configurers 1095 */ 1096 public List<Configurer> getConfigurers() 1097 { 1098 return configurers; 1099 } 1100 1101 /** 1102 * Returns the given constraint set. 1103 * 1104 * @param constraintSetId the id of the constraint set to retrieve 1105 * @return the constraint set or null if not found 1106 * @throws InvalidConfigurationException 1107 * @throws IllegalArgumentException if <code>constraintSetId</code> is null 1108 */ 1109 public ConstraintSet getConstraintSet(final String constraintSetId) throws InvalidConfigurationException, 1110 IllegalArgumentException 1111 { 1112 Assert.argumentNotNull("constraintSetsById", constraintSetsById); 1113 synchronized (constraintSetsById) 1114 { 1115 ConstraintSet cs = constraintSetsById.get(constraintSetId); 1116 1117 if (cs == null) for (final Configurer configurer : configurers) 1118 { 1119 final ConstraintSetConfiguration csc = configurer.getConstraintSetConfiguration(constraintSetId); 1120 if (csc != null) 1121 { 1122 cs = new ConstraintSet(csc.id); 1123 cs.setChecks(csc.checks); 1124 1125 addConstraintSet(cs, csc.overwrite != null && csc.overwrite); 1126 } 1127 } 1128 return cs; 1129 } 1130 } 1131 1132 /** 1133 * @return the exceptionProcessor 1134 */ 1135 public ExceptionTranslator getExceptionTranslator() 1136 { 1137 return exceptionTranslator; 1138 } 1139 1140 /** 1141 * @return the expressionLanguageRegistry 1142 */ 1143 public ExpressionLanguageRegistry getExpressionLanguageRegistry() 1144 { 1145 return expressionLanguageRegistry; 1146 } 1147 1148 /** 1149 * @return the objectGraphNavigatorRegistry 1150 */ 1151 public ObjectGraphNavigatorRegistry getObjectGraphNavigatorRegistry() 1152 { 1153 return ognRegistry; 1154 } 1155 1156 /** 1157 * Determines if at least one of the given profiles is enabled 1158 * 1159 * @param profilesOfCheck 1160 * @param enabledProfiles optional array of profiles (can be null) 1161 * @return Returns true if at least one of the given profiles is enabled. 1162 */ 1163 protected boolean isAnyProfileEnabled(final String[] profilesOfCheck, final String[] enabledProfiles) 1164 { 1165 if (enabledProfiles == null) 1166 { 1167 // use the global profile configuration 1168 if (profilesOfCheck == null || profilesOfCheck.length == 0) return isProfileEnabled("default"); 1169 1170 for (final String profile : profilesOfCheck) 1171 if (isProfileEnabled(profile)) return true; 1172 } 1173 else 1174 { 1175 // use the local profile configuration 1176 if (profilesOfCheck == null || profilesOfCheck.length == 0) 1177 return ArrayUtils.containsEqual(enabledProfiles, "default"); 1178 1179 for (final String profile : profilesOfCheck) 1180 if (ArrayUtils.containsEqual(enabledProfiles, profile)) return true; 1181 } 1182 return false; 1183 } 1184 1185 /** 1186 * Determines if the given object is currently validated in the current thread 1187 * 1188 * @param object 1189 * @return Returns true if the given object is currently validated in the current thread. 1190 */ 1191 protected boolean isCurrentlyValidated(final Object object) 1192 { 1193 Assert.argumentNotNull("object", object); 1194 return currentlyValidatedObjects.get().getLast().contains(object); 1195 } 1196 1197 /** 1198 * Determines if the given profile is enabled. 1199 * 1200 * @param profileId 1201 * @return Returns true if the given profile is enabled. 1202 */ 1203 public boolean isProfileEnabled(final String profileId) 1204 { 1205 Assert.argumentNotNull("profileId", profileId); 1206 if (isProfilesFeatureUsed) 1207 { 1208 if (isAllProfilesEnabledByDefault) return !disabledProfiles.contains(profileId); 1209 1210 return enabledProfiles.contains(profileId); 1211 } 1212 return true; 1213 } 1214 1215 /** 1216 * clears the checks and constraint sets => a reconfiguration using the 1217 * currently registered configurers will automatically happen 1218 */ 1219 public void reconfigureChecks() 1220 { 1221 synchronized (checksByClass) 1222 { 1223 checksByClass.clear(); 1224 } 1225 synchronized (constraintSetsById) 1226 { 1227 constraintSetsById.clear(); 1228 } 1229 } 1230 1231 /** 1232 * Removes object-level constraint checks 1233 * 1234 * @param clazz 1235 * @param checks 1236 * @throws IllegalArgumentException if <code>clazz == null</code> or <code>checks == null</code> or checks is empty 1237 */ 1238 public void removeChecks(final Class< ? > clazz, final Check... checks) throws IllegalArgumentException 1239 { 1240 Assert.argumentNotNull("clazz", clazz); 1241 Assert.argumentNotEmpty("checks", checks); 1242 1243 getClassChecks(clazz).removeObjectChecks(checks); 1244 } 1245 1246 /** 1247 * Removes constraint checks for the given field 1248 * 1249 * @param field 1250 * @param checks 1251 * @throws IllegalArgumentException if <code>field == null</code> or <code>checks == null</code> or checks is empty 1252 */ 1253 public void removeChecks(final Field field, final Check... checks) throws IllegalArgumentException 1254 { 1255 Assert.argumentNotNull("field", field); 1256 Assert.argumentNotEmpty("checks", checks); 1257 1258 getClassChecks(field.getDeclaringClass()).removeFieldChecks(field, checks); 1259 } 1260 1261 /** 1262 * Removes constraint checks for the given getter's return value 1263 * 1264 * @param getter a JavaBean Getter style method 1265 * @param checks 1266 * @throws IllegalArgumentException if <code>getter == null</code> or <code>checks == null</code> 1267 */ 1268 public void removeChecks(final Method getter, final Check... checks) throws IllegalArgumentException 1269 { 1270 Assert.argumentNotNull("getter", getter); 1271 Assert.argumentNotEmpty("checks", checks); 1272 1273 getClassChecks(getter.getDeclaringClass()).removeMethodReturnValueChecks(getter, checks); 1274 } 1275 1276 /** 1277 * Removes the constraint set with the given id 1278 * @param id the id of the constraint set to remove, cannot be null 1279 * @return the removed constraint set 1280 * @throws IllegalArgumentException if <code>id == null</code> 1281 */ 1282 public ConstraintSet removeConstraintSet(final String id) throws IllegalArgumentException 1283 { 1284 Assert.argumentNotNull("id", id); 1285 1286 synchronized (constraintSetsById) 1287 { 1288 return constraintSetsById.remove(id); 1289 } 1290 } 1291 1292 protected String renderMessage(final OValContext context, final Object value, final String messageKey, 1293 final Map<String, ? > messageValues) 1294 { 1295 String message = MessageRenderer.renderMessage(messageKey, messageValues); 1296 1297 // if there are no place holders in the message simply return it 1298 if (message.indexOf('{') == -1) return message; 1299 1300 message = StringUtils.replaceAll(message, "{context}", contextRenderer.render(context)); 1301 message = StringUtils.replaceAll(message, "{invalidValue}", messageValueFormatter.format(value)); 1302 1303 return message; 1304 } 1305 1306 /** 1307 * Reports an additional constraint violation for the current validation cycle. 1308 * This method is intended to be executed by check implementations only. 1309 * @param constraintViolation the constraint violation 1310 */ 1311 public void reportConstraintViolation(final ConstraintViolation constraintViolation) 1312 { 1313 Assert.argumentNotNull("constraintViolation", constraintViolation); 1314 if (currentViolations.get().size() == 0) 1315 throw new IllegalStateException("No active validation cycle found for the current thread."); 1316 currentViolations.get().getLast().add(constraintViolation); 1317 } 1318 1319 /** 1320 * @param exceptionTranslator the exceptionTranslator to set 1321 */ 1322 public void setExceptionTranslator(final ExceptionTranslator exceptionTranslator) 1323 { 1324 this.exceptionTranslator = exceptionTranslator; 1325 } 1326 1327 protected RuntimeException translateException(final OValException ex) 1328 { 1329 if (exceptionTranslator != null) 1330 { 1331 final RuntimeException rex = exceptionTranslator.translateException(ex); 1332 if (rex != null) return rex; 1333 } 1334 return ex; 1335 } 1336 1337 /** 1338 * {@inheritDoc} 1339 */ 1340 public List<ConstraintViolation> validate(final Object validatedObject) throws IllegalArgumentException, 1341 ValidationFailedException 1342 { 1343 Assert.argumentNotNull("validatedObject", validatedObject); 1344 1345 // create required objects for this validation cycle 1346 final List<ConstraintViolation> violations = collectionFactory.createList(); 1347 currentViolations.get().add(violations); 1348 currentlyValidatedObjects.get().add(new IdentitySet<Object>(4)); 1349 1350 try 1351 { 1352 validateInvariants(validatedObject, violations, (String[]) null); 1353 return violations; 1354 } 1355 finally 1356 { 1357 // remove the validation cycle related objects 1358 currentViolations.get().removeLast(); 1359 currentlyValidatedObjects.get().removeLast(); 1360 } 1361 } 1362 1363 /** 1364 * {@inheritDoc} 1365 */ 1366 public List<ConstraintViolation> validate(final Object validatedObject, final String... profiles) 1367 throws IllegalArgumentException, ValidationFailedException 1368 { 1369 Assert.argumentNotNull("validatedObject", validatedObject); 1370 1371 // create required objects for this validation cycle 1372 final List<ConstraintViolation> violations = collectionFactory.createList(); 1373 currentViolations.get().add(violations); 1374 currentlyValidatedObjects.get().add(new IdentitySet<Object>(4)); 1375 1376 try 1377 { 1378 validateInvariants(validatedObject, violations, profiles); 1379 return violations; 1380 } 1381 finally 1382 { 1383 // remove the validation cycle related objects 1384 currentViolations.get().removeLast(); 1385 currentlyValidatedObjects.get().removeLast(); 1386 } 1387 } 1388 1389 /** 1390 * {@inheritDoc} 1391 */ 1392 public List<ConstraintViolation> validateFieldValue(final Object validatedObject, final Field validatedField, 1393 final Object fieldValueToValidate) throws IllegalArgumentException, ValidationFailedException 1394 { 1395 Assert.argumentNotNull("validatedObject", validatedObject); 1396 Assert.argumentNotNull("validatedField", validatedField); 1397 1398 // create required objects for this validation cycle 1399 final List<ConstraintViolation> violations = collectionFactory.createList(); 1400 currentViolations.get().add(violations); 1401 currentlyValidatedObjects.get().add(new IdentitySet<Object>(4)); 1402 1403 try 1404 { 1405 final ClassChecks cc = getClassChecks(validatedField.getDeclaringClass()); 1406 final Collection<Check> checks = cc.checksForFields.get(validatedField); 1407 1408 if (checks == null || checks.size() == 0) return violations; 1409 1410 final FieldContext context = ContextCache.getFieldContext(validatedField); 1411 1412 for (final Check check : checks) 1413 checkConstraint(violations, check, validatedObject, fieldValueToValidate, context, null, false); 1414 return violations; 1415 } 1416 catch (final OValException ex) 1417 { 1418 throw new ValidationFailedException("Field validation failed. Field: " + validatedField 1419 + " Validated object: " + validatedObject, ex); 1420 } 1421 finally 1422 { 1423 // remove the validation cycle related objects 1424 currentViolations.get().removeLast(); 1425 currentlyValidatedObjects.get().removeLast(); 1426 } 1427 } 1428 1429 /** 1430 * validates the field and getter constrains of the given object. 1431 * if the given object is a class the static fields and getters 1432 * are validated. 1433 * 1434 * @param validatedObject the object to validate, cannot be null 1435 * @throws ValidationFailedException 1436 * @throws IllegalArgumentException if <code>validatedObject == null</code> 1437 */ 1438 protected void validateInvariants(final Object validatedObject, final List<ConstraintViolation> violations, 1439 final String[] profiles) throws IllegalArgumentException, ValidationFailedException 1440 { 1441 Assert.argumentNotNull("validatedObject", validatedObject); 1442 1443 currentlyValidatedObjects.get().getLast().add(validatedObject); 1444 if (validatedObject instanceof Class< ? >) 1445 _validateStaticInvariants((Class< ? >) validatedObject, violations, profiles); 1446 else 1447 _validateObjectInvariants(validatedObject, validatedObject.getClass(), violations, profiles); 1448 } 1449 }