/* * Hibernate Validator, declare and validate application constraints * * License: Apache License, Version 2.0 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>. */ package org.hibernate.validator.test.internal.engine.constraintvalidation; import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotSame; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import java.util.Set; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorFactory; import javax.validation.Validator; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.validation.metadata.BeanDescriptor; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.PropertyDescriptor; import org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Hardy Ferentschik * @author Gunnar Morling */ public class ConstraintValidatorManagerTest { private ConstraintValidatorManager constraintValidatorManager; private ConstraintValidatorFactory constraintValidatorFactory; private Validator validator; @BeforeMethod public void setUp() { constraintValidatorFactory = new ConstraintValidatorFactoryImpl(); constraintValidatorManager = new ConstraintValidatorManager( constraintValidatorFactory ); validator = getValidator(); } @Test public void testGetDefaultConstraintValidatorFactory() { assertTrue( constraintValidatorManager.getDefaultConstraintValidatorFactory() == constraintValidatorFactory, "Unexpected default factory" ); } @Test public void testGetInitializedValidator() { ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" ); ConstraintValidator<?, ?> constraintValidator = constraintValidatorManager.getInitializedValidator( String.class, constraintDescriptor, constraintValidatorFactory ); assertTrue( constraintValidator instanceof NotNullValidator, "Unexpected validator type" ); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullValidatedValueThrowsIllegalArgumentException() { ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" ); constraintValidatorManager.getInitializedValidator( null, constraintDescriptor, constraintValidatorFactory ); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullDescriptorThrowsIllegalArgumentException() { constraintValidatorManager.getInitializedValidator( String.class, null, constraintValidatorFactory ); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullFactoryThrowsIllegalArgumentException() { ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" ); constraintValidatorManager.getInitializedValidator( String.class, constraintDescriptor, null ); } @Test public void testUnexpectedTypeException() { ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s2" ); ConstraintValidator<?, ?> constraintValidator = constraintValidatorManager.getInitializedValidator( Object.class, constraintDescriptor, constraintValidatorFactory ); assertNull( constraintValidator, "there should be no matching constraint validator" ); } @Test public void testConstraintValidatorInstancesAreCachedPerFactory() { ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" ); ConstraintValidator<?, ?> constraintValidator1 = constraintValidatorManager.getInitializedValidator( String.class, constraintDescriptor, constraintValidatorFactory ); assertTrue( constraintValidatorManager.numberOfCachedConstraintValidatorInstances() == 1, "There should be only one instance cached" ); ConstraintValidator<?, ?> constraintValidator2 = constraintValidatorManager.getInitializedValidator( String.class, constraintDescriptor, new MyCustomValidatorFactory() ); assertTrue( constraintValidatorManager.numberOfCachedConstraintValidatorInstances() == 2, "Constraint Validator Factory should be part of the cache key" ); assertNotSame( constraintValidator1, constraintValidator2, "The validator instances should not be the same" ); } @Test public void testOnlyTheInstancesForTheLeastRecentlyUsedCustomFactoryAreCached() { ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" ); for ( int i = 0; i < 10; i++ ) { constraintValidatorManager.getInitializedValidator( String.class, constraintDescriptor, new MyCustomValidatorFactory() ); assertEquals( constraintValidatorManager.numberOfCachedConstraintValidatorInstances(), 1, "There should be only one instance cached" ); } constraintValidatorManager.clear(); assertEquals( constraintValidatorManager.numberOfCachedConstraintValidatorInstances(), 0, "Cache should be empty" ); } @Test @TestForIssue(jiraKey = "HV-662") public void testValidatorsAreCachedPerConstraint() { Validator validator = getConfiguration() .addMapping( ConstraintValidatorManagerTest.class.getResourceAsStream( "hv-662-mapping.xml" ) ) .buildValidatorFactory() .getValidator(); ConstraintDescriptorImpl<?> notNullOnFirstNameDescriptor = getSingleConstraintDescriptorForProperty( validator, User.class, "firstName" ); ConstraintDescriptorImpl<?> notNullOnLastNameDescriptor = getSingleConstraintDescriptorForProperty( validator, User.class, "lastName" ); ConstraintValidator<?, ?> notNullValidatorForFirstName1 = constraintValidatorManager.getInitializedValidator( String.class, notNullOnFirstNameDescriptor, constraintValidatorFactory ); ConstraintValidator<?, ?> notNullValidatorForFirstName2 = constraintValidatorManager.getInitializedValidator( String.class, notNullOnFirstNameDescriptor, constraintValidatorFactory ); ConstraintValidator<?, ?> notNullValidatorForLastName = constraintValidatorManager.getInitializedValidator( String.class, notNullOnLastNameDescriptor, constraintValidatorFactory ); assertThat( notNullValidatorForFirstName1 ).isSameAs( notNullValidatorForFirstName2 ); assertThat( notNullValidatorForFirstName1 ).isSameAs( notNullValidatorForLastName ); } @Test @TestForIssue(jiraKey = "HV-662") public void testValidatorsAreCachedPerConstraintAndAnnotationMembers() { Validator validator = getConfiguration() .addMapping( ConstraintValidatorManagerTest.class.getResourceAsStream( "hv-662-mapping.xml" ) ) .buildValidatorFactory() .getValidator(); ConstraintDescriptorImpl<?> sizeOnMiddleNameDescriptor = getSingleConstraintDescriptorForProperty( validator, User.class, "middleName" ); ConstraintDescriptorImpl<?> sizeOnAddress1Descriptor = getSingleConstraintDescriptorForProperty( validator, User.class, "address1" ); ConstraintDescriptorImpl<?> sizeOnAddress2Descriptor = getSingleConstraintDescriptorForProperty( validator, User.class, "address2" ); ConstraintValidator<?, ?> sizeValidatorForMiddleName = constraintValidatorManager.getInitializedValidator( String.class, sizeOnMiddleNameDescriptor, constraintValidatorFactory ); ConstraintValidator<?, ?> sizeValidatorForAddress1 = constraintValidatorManager.getInitializedValidator( String.class, sizeOnAddress1Descriptor, constraintValidatorFactory ); ConstraintValidator<?, ?> sizeValidatorForAddress2 = constraintValidatorManager.getInitializedValidator( String.class, sizeOnAddress2Descriptor, constraintValidatorFactory ); assertThat( sizeValidatorForMiddleName ).isNotSameAs( sizeValidatorForAddress1 ); assertThat( sizeValidatorForAddress1 ).isSameAs( sizeValidatorForAddress2 ); } private ConstraintDescriptorImpl<?> getConstraintDescriptorForProperty(String propertyName) { return getSingleConstraintDescriptorForProperty( validator, Foo.class, propertyName ); } private ConstraintDescriptorImpl<?> getSingleConstraintDescriptorForProperty(Validator validator, Class<?> clazz, String propertyName) { BeanDescriptor beanDescriptor = validator.getConstraintsForClass( clazz ); PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( propertyName ); Set<ConstraintDescriptor<?>> constraintDescriptorSet = propertyDescriptor.getConstraintDescriptors(); assertEquals( constraintDescriptorSet.size(), 1, "There should be only one constraint descriptor" ); return (ConstraintDescriptorImpl<?>) constraintDescriptorSet.iterator().next(); } public class Foo { @NotNull String s1; @Size String s2; } public class MyCustomValidatorFactory implements ConstraintValidatorFactory { private final ConstraintValidatorFactory delegate; public MyCustomValidatorFactory() { delegate = new ConstraintValidatorFactoryImpl(); } @Override public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) { return delegate.getInstance( key ); } @Override public void releaseInstance(ConstraintValidator<?, ?> instance) { delegate.releaseInstance( instance ); } } }