Skip to content

Commit 1f34de5

Browse files
committed
HV-1592 Validate that the validator is defined with the right constraint
1 parent 2d1ff8c commit 1f34de5

File tree

10 files changed

+130
-28
lines changed

10 files changed

+130
-28
lines changed

engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public ConstraintDefinitionContext<A> includeExistingValidators(boolean includeE
4747

4848
@Override
4949
public ConstraintDefinitionContext<A> validatedBy(Class<? extends ConstraintValidator<A, ?>> validator) {
50-
validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validator ) );
50+
validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validator, this.annotationType ) );
5151
return this;
5252
}
5353

engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,38 @@
2525
* Represents an implementation of {@link ConstraintValidator}.
2626
*
2727
* @author Gunnar Morling
28+
* @author Guillaume Smet
2829
*/
2930
class ClassBasedValidatorDescriptor<A extends Annotation> implements ConstraintValidatorDescriptor<A> {
3031

31-
private static final long serialVersionUID = -8207687559460098548L;
3232
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
3333

3434
private final Class<? extends ConstraintValidator<A, ?>> validatorClass;
3535
private final Type validatedType;
3636
private final EnumSet<ValidationTarget> validationTargets;
3737

38-
public ClassBasedValidatorDescriptor(Class<? extends ConstraintValidator<A, ?>> validatorClass) {
38+
private ClassBasedValidatorDescriptor(Class<? extends ConstraintValidator<A, ?>> validatorClass) {
3939
this.validatorClass = validatorClass;
40-
this.validatedType = TypeHelper.extractType( validatorClass );
40+
this.validatedType = TypeHelper.extractValidatedType( validatorClass );
4141
this.validationTargets = determineValidationTargets( validatorClass );
4242
}
4343

44+
public static <T extends Annotation> ClassBasedValidatorDescriptor<T> of(Class<? extends ConstraintValidator<T, ?>> validatorClass,
45+
Class<? extends Annotation> registeredConstraintAnnotationType) {
46+
Type definedConstraintAnnotationType = TypeHelper.extractConstraintType( validatorClass );
47+
if ( !registeredConstraintAnnotationType.equals( definedConstraintAnnotationType ) ) {
48+
throw LOG.getConstraintValidatorDefinitionConstraintMismatchException( validatorClass, registeredConstraintAnnotationType,
49+
definedConstraintAnnotationType );
50+
}
51+
52+
return new ClassBasedValidatorDescriptor<T>( validatorClass );
53+
}
54+
4455
private static EnumSet<ValidationTarget> determineValidationTargets(Class<? extends ConstraintValidator<?, ?>> validatorClass) {
4556
SupportedValidationTarget supportedTargetAnnotation = validatorClass.getAnnotation(
46-
SupportedValidationTarget.class
47-
);
57+
SupportedValidationTarget.class );
4858

49-
//by default constraints target the annotated element
59+
// by default constraints target the annotated element
5060
if ( supportedTargetAnnotation == null ) {
5161
return EnumSet.of( ValidationTarget.ANNOTATED_ELEMENT );
5262
}

engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ public interface ConstraintValidatorDescriptor<A extends Annotation> {
4444
*/
4545
ConstraintValidator<A, ?> newInstance(ConstraintValidatorFactory constraintValidatorFactory);
4646

47-
static <A extends Annotation> ConstraintValidatorDescriptor<A> forClass(Class<? extends ConstraintValidator<A, ?>> validatorClass) {
48-
return new ClassBasedValidatorDescriptor<>( validatorClass );
47+
static <A extends Annotation> ConstraintValidatorDescriptor<A> forClass(Class<? extends ConstraintValidator<A, ?>> validatorClass,
48+
Class<? extends Annotation> constraintAnnotationType) {
49+
return ClassBasedValidatorDescriptor.of( validatorClass, constraintAnnotationType );
4950
}
5051

5152
static <A extends Annotation, T> ConstraintValidatorDescriptor<A> forLambda(Class<A> annotationType, Type validatedType, ValidationCallable<T> lambda) {

engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -679,21 +679,24 @@ public ConstraintHelper() {
679679
this.builtinConstraints = Collections.unmodifiableMap( tmpConstraints );
680680
}
681681

682-
private static <A extends Annotation> void putConstraint(Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> validators, Class<A> constraintType, Class<? extends ConstraintValidator<A, ?>> validatorType) {
683-
validators.put( constraintType, Collections.singletonList( ConstraintValidatorDescriptor.forClass( validatorType ) ) );
682+
private static <A extends Annotation> void putConstraint(Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> validators,
683+
Class<A> constraintType, Class<? extends ConstraintValidator<A, ?>> validatorType) {
684+
validators.put( constraintType, Collections.singletonList( ConstraintValidatorDescriptor.forClass( validatorType, constraintType ) ) );
684685
}
685686

686-
private static <A extends Annotation> void putConstraints(Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> validators, Class<A> constraintType, Class<? extends ConstraintValidator<A, ?>> validatorType1, Class<? extends ConstraintValidator<A, ?>> validatorType2) {
687+
private static <A extends Annotation> void putConstraints(Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> validators,
688+
Class<A> constraintType, Class<? extends ConstraintValidator<A, ?>> validatorType1, Class<? extends ConstraintValidator<A, ?>> validatorType2) {
687689
List<ConstraintValidatorDescriptor<?>> descriptors = Stream.of( validatorType1, validatorType2 )
688-
.map( ConstraintValidatorDescriptor::forClass )
690+
.map( vt -> ConstraintValidatorDescriptor.forClass( vt, constraintType ) )
689691
.collect( Collectors.toList() );
690692

691693
validators.put( constraintType, CollectionHelper.toImmutableList( descriptors ) );
692694
}
693695

694-
private static <A extends Annotation> void putConstraints(Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> validators, Class<A> constraintType, List<Class<? extends ConstraintValidator<A, ?>>> validatorDescriptors) {
696+
private static <A extends Annotation> void putConstraints(Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> validators,
697+
Class<A> constraintType, List<Class<? extends ConstraintValidator<A, ?>>> validatorDescriptors) {
695698
List<ConstraintValidatorDescriptor<?>> descriptors = validatorDescriptors.stream()
696-
.map( ConstraintValidatorDescriptor::forClass )
699+
.map( vt -> ConstraintValidatorDescriptor.forClass( vt, constraintType ) )
697700
.collect( Collectors.toList() );
698701

699702
validators.put( constraintType, CollectionHelper.toImmutableList( descriptors ) );
@@ -995,8 +998,8 @@ private <A extends Annotation> List<ConstraintValidatorDescriptor<A>> getDefault
995998
.validatedBy();
996999

9971000
return Stream.of( validatedBy )
998-
.map( c -> ConstraintValidatorDescriptor.forClass( c ) )
999-
.collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) );
1001+
.map( c -> ConstraintValidatorDescriptor.forClass( c, annotationType ) )
1002+
.collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) );
10001003
}
10011004

10021005
private static boolean isClassPresent(String className) {

engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.lang.reflect.TypeVariable;
3232
import java.lang.reflect.WildcardType;
3333
import java.util.Collections;
34+
import java.util.HashMap;
3435
import java.util.LinkedHashMap;
3536
import java.util.List;
3637
import java.util.Map;
@@ -52,6 +53,7 @@
5253
public final class TypeHelper {
5354

5455
private static final Map<Class<?>, Set<Class<?>>> SUBTYPES_BY_PRIMITIVE;
56+
private static final int CONSTRAINT_TYPE_INDEX = 0;
5557
private static final int VALIDATOR_TYPE_INDEX = 1;
5658
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
5759

@@ -324,24 +326,32 @@ public static <A extends Annotation> Map<Type, ConstraintValidatorDescriptor<A>>
324326
return validatorsTypes;
325327
}
326328

327-
public static Type extractType(Class<? extends ConstraintValidator<?, ?>> validator) {
328-
Map<Type, Type> resolvedTypes = newHashMap();
329+
public static Type extractValidatedType(Class<? extends ConstraintValidator<?, ?>> validator) {
330+
return extractConstraintValidatorTypeArgumentType( validator, VALIDATOR_TYPE_INDEX );
331+
}
332+
333+
public static Type extractConstraintType(Class<? extends ConstraintValidator<?, ?>> validator) {
334+
return extractConstraintValidatorTypeArgumentType( validator, CONSTRAINT_TYPE_INDEX );
335+
}
336+
337+
public static Type extractConstraintValidatorTypeArgumentType(Class<? extends ConstraintValidator<?, ?>> validator, int typeArgumentIndex) {
338+
Map<Type, Type> resolvedTypes = new HashMap<>();
329339
Type constraintValidatorType = resolveTypes( resolvedTypes, validator );
330340

331341
//we now have all bind from a type to its resolution at one level
332-
Type validatorType = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[VALIDATOR_TYPE_INDEX];
333-
if ( validatorType == null ) {
342+
Type type = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[typeArgumentIndex];
343+
if ( type == null ) {
334344
throw LOG.getNullIsAnInvalidTypeForAConstraintValidatorException();
335345
}
336-
else if ( validatorType instanceof GenericArrayType ) {
337-
validatorType = TypeHelper.getArrayType( TypeHelper.getComponentType( validatorType ) );
346+
else if ( type instanceof GenericArrayType ) {
347+
type = TypeHelper.getArrayType( TypeHelper.getComponentType( type ) );
338348
}
339349

340-
while ( resolvedTypes.containsKey( validatorType ) ) {
341-
validatorType = resolvedTypes.get( validatorType );
350+
while ( resolvedTypes.containsKey( type ) ) {
351+
type = resolvedTypes.get( type );
342352
}
343353
//FIXME raise an exception if validatorType is not a class
344-
return validatorType;
354+
return type;
345355
}
346356

347357
public static boolean isUnboundWildcard(Type type) {

engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,4 +849,10 @@ ValidationException getUnableToAccessMethodException(Lookup lookup, @FormatWith(
849849
@LogMessage(level = WARN)
850850
@Message(id = 242, value = "Unable to load or instantiate JPA aware resolver %1$s. All properties will per default be traversable.")
851851
void logUnableToLoadOrInstantiateJPAAwareResolver(String traversableResolverClassName);
852+
853+
@Message(id = 243, value = "Constraint %2$s references constraint validator type %1$s, but this validator is defined for constraint type %3$s.")
854+
ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatchException(
855+
@FormatWith(ClassObjectFormatter.class) Class<? extends ConstraintValidator<?, ?>> constraintValidatorImplementationType,
856+
@FormatWith(ClassObjectFormatter.class) Class<? extends Annotation> registeredConstraintAnnotationType,
857+
@FormatWith(TypeFormatter.class) Type declaredConstraintAnnotationType);
852858
}

engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintDefinitionStaxBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ <A extends Annotation> List<ConstraintValidatorDescriptor<A>> build(Class<A> ann
140140
.map( value -> classLoadingHelper.loadClass( value, defaultPackage ) )
141141
.peek( this::checkValidatorAssignability )
142142
.map( clazz -> (Class<? extends ConstraintValidator<A, ?>>) clazz )
143-
.map( ConstraintValidatorDescriptor::forClass )
143+
.map( validatorClass -> ConstraintValidatorDescriptor.forClass( validatorClass, annotation ) )
144144
.collect( Collectors.toList() );
145145
}
146146

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.test.constraintvalidator;
8+
9+
import static org.hibernate.validator.testutils.ValidatorUtil.getValidator;
10+
11+
import javax.validation.ConstraintDefinitionException;
12+
import javax.validation.Validator;
13+
14+
import org.hibernate.validator.testutil.TestForIssue;
15+
import org.testng.annotations.BeforeMethod;
16+
import org.testng.annotations.Test;
17+
18+
/**
19+
* @author Guillaume Smet
20+
*/
21+
@TestForIssue(jiraKey = "HV-1592")
22+
public class ConstraintDefinitionTypeMismatchTest {
23+
24+
private Validator validator;
25+
26+
@BeforeMethod
27+
public void setUp() {
28+
validator = getValidator();
29+
}
30+
31+
@Test(expectedExceptions = ConstraintDefinitionException.class, expectedExceptionsMessageRegExp = "^HV000243:.*")
32+
public void constraint_validator_constraint_type_mismatch_causes_exception() {
33+
validator.validate( new TypeMismatchBean() );
34+
}
35+
36+
public class TypeMismatchBean {
37+
38+
@TypeMismatchConstraint
39+
private String property;
40+
}
41+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.test.constraintvalidator;
8+
9+
import static java.lang.annotation.ElementType.FIELD;
10+
import static java.lang.annotation.ElementType.METHOD;
11+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
12+
13+
import java.lang.annotation.Documented;
14+
import java.lang.annotation.Retention;
15+
import java.lang.annotation.Target;
16+
17+
import javax.validation.Constraint;
18+
import javax.validation.Payload;
19+
20+
21+
@Documented
22+
@Constraint(validatedBy = MustNotMatchValidator.class)
23+
@Target({ METHOD, FIELD })
24+
@Retention(RUNTIME)
25+
public @interface TypeMismatchConstraint {
26+
String message() default "{org.hibernate.validator.test.constraintvalidator.TypeMismatchConstraint.message}";
27+
28+
Class<?>[] groups() default { };
29+
30+
Class<? extends Payload>[] payload() default { };
31+
}

engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ public void isArrayWithParameterizedType() {
858858
@Test
859859
public void testTypeDiscovery() {
860860
List<ConstraintValidatorDescriptor<Positive>> validatorDescriptors = new ArrayList<>();
861-
validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( PositiveConstraintValidator.class ) );
861+
validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( PositiveConstraintValidator.class, Positive.class ) );
862862
Map<Type, ConstraintValidatorDescriptor<Positive>> validatorsTypes = TypeHelper
863863
.getValidatorTypes( Positive.class, validatorDescriptors );
864864

0 commit comments

Comments
 (0)