Skip to content

Commit

Permalink
HV-782 Constraint definition overrides for the same constraint in mul…
Browse files Browse the repository at this point in the history
…tiple files needs to throw an exception
  • Loading branch information
hferentschik authored and gunnarmorling committed Mar 27, 2013
1 parent 2d97e87 commit 887426d
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 5 deletions.
Expand Up @@ -324,9 +324,16 @@ private boolean supportsValidationTarget(Class<? extends ConstraintValidator<?,
* @param definitionClasses The validators to register
* @param keepDefaultClasses Whether any default validators should be kept or not
*/
public <A extends Annotation> void putValidatorClasses(Class<A> annotationType, List<Class<? extends ConstraintValidator<A, ?>>> definitionClasses, boolean keepDefaultClasses) {
public <A extends Annotation> void putValidatorClasses(Class<A> annotationType,
List<Class<? extends ConstraintValidator<A, ?>>> definitionClasses,
boolean keepDefaultClasses) {
if ( keepDefaultClasses ) {
definitionClasses.addAll( getDefaultValidatorClasses( annotationType ) );
List<Class<? extends ConstraintValidator<A, ?>>> defaultValidators = getDefaultValidatorClasses(
annotationType
);
for ( Class<? extends ConstraintValidator<A, ?>> defaultValidator : defaultValidators ) {
definitionClasses.add( 0, defaultValidator );
}
}

validatorClasses.put( annotationType, definitionClasses );
Expand Down
Expand Up @@ -600,4 +600,8 @@ public interface Log extends BasicLogger {
@Message(id = 166,
value = "@ValidateOnExecution is not allowed on methods overriding a superclass method or implementing an interface. Check configuration for %1$s")
ValidationException getValidateOnExecutionOnOverriddenOrInterfaceMethodException(Method m);

@Message(id = 167,
value = "A given constraint definition can only be overridden in one mapping files. %1$s is overridden in multiple files")
ValidationException getOverridingConstraintDefinitionsInMultipleMappingFilesException(String constraintClass);
}
Expand Up @@ -99,6 +99,7 @@ public final void parse(Set<InputStream> mappingStreams) {
try {
JAXBContext jc = JAXBContext.newInstance( ConstraintMappingsType.class );

Set<String> alreadyProcessedConstraintDefinitions = newHashSet();
for ( InputStream in : mappingStreams ) {
String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", in );
String schemaResourceName = getSchemaResourceName( schemaVersion );
Expand All @@ -110,7 +111,11 @@ public final void parse(Set<InputStream> mappingStreams) {
ConstraintMappingsType mapping = getValidationConfig( in, unmarshaller );
String defaultPackage = mapping.getDefaultPackage();

parseConstraintDefinitions( mapping.getConstraintDefinition(), defaultPackage );
parseConstraintDefinitions(
mapping.getConstraintDefinition(),
defaultPackage,
alreadyProcessedConstraintDefinitions
);

for ( BeanType bean : mapping.getBean() ) {
Class<?> beanClass = ReflectionHelper.loadClass( bean.getClazz(), defaultPackage );
Expand Down Expand Up @@ -203,9 +208,17 @@ public final List<Class<?>> getDefaultSequenceForClass(Class<?> beanClass) {
}

@SuppressWarnings("unchecked")
private void parseConstraintDefinitions(List<ConstraintDefinitionType> constraintDefinitionList, String defaultPackage) {
private void parseConstraintDefinitions(List<ConstraintDefinitionType> constraintDefinitionList,
String defaultPackage,
Set<String> alreadyProcessedConstraintDefinitions) {
for ( ConstraintDefinitionType constraintDefinition : constraintDefinitionList ) {
String annotationClassName = constraintDefinition.getAnnotation();
if ( alreadyProcessedConstraintDefinitions.contains( annotationClassName ) ) {
throw log.getOverridingConstraintDefinitionsInMultipleMappingFilesException( annotationClassName );
}
else {
alreadyProcessedConstraintDefinitions.add( annotationClassName );
}

Class<?> clazz = ReflectionHelper.loadClass( annotationClassName, defaultPackage );
if ( !clazz.isAnnotation() ) {
Expand Down Expand Up @@ -265,7 +278,7 @@ private void addConstrainedElements(Class<?> beanClass, Set<? extends Constraine
for ( ConstrainedElement constrainedElement : newConstrainedElements ) {
if ( existingConstrainedElements.contains( constrainedElement ) ) {
ConstraintLocation location = constrainedElement.getLocation();
throw log.getConstrainedElementConfiguredMultipleTimesException(location.getMember().toString() );
throw log.getConstrainedElementConfiguredMultipleTimesException( location.getMember().toString() );
}
else {
existingConstrainedElements.add( constrainedElement );
Expand Down
@@ -0,0 +1,125 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.validator.test.internal.xml;

import java.io.InputStream;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ValidationException;
import javax.validation.constraints.DecimalMin;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import org.hibernate.validator.internal.constraintvalidators.DecimalMinValidatorForCharSequence;
import org.hibernate.validator.internal.constraintvalidators.DecimalMinValidatorForNumber;
import org.hibernate.validator.internal.engine.DefaultParameterNameProvider;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.xml.XmlMappingParser;
import org.hibernate.validator.testutil.TestForIssue;

import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

/**
* @author Hardy Ferentschik
*/
public class XmlMappingParserTest {

private XmlMappingParser xmlMappingParser;
private ConstraintHelper constraintHelper;

@BeforeMethod
public void setupParserHelper() {
constraintHelper = new ConstraintHelper();
xmlMappingParser = new XmlMappingParser( constraintHelper, new DefaultParameterNameProvider() );
}

@Test
@TestForIssue(jiraKey = "HV-782")
public void testAdditionalConstraintValidatorsGetAddedAndAreLastInList() {
List<Class<? extends ConstraintValidator<DecimalMin, ?>>> validators = constraintHelper.getAllValidatorClasses(
DecimalMin.class
);

assertEquals( validators.size(), 2, "Wrong number of default validators" );
assertTrue( validators.contains( DecimalMinValidatorForCharSequence.class ), "Missing default validator" );
assertTrue( validators.contains( DecimalMinValidatorForNumber.class ), "Missing default validator" );

Set<InputStream> mappingStreams = newHashSet();
mappingStreams.add( XmlMappingParserTest.class.getResourceAsStream( "decimal-min-mapping-1.xml" ) );

xmlMappingParser.parse( mappingStreams );

validators = constraintHelper.getAllValidatorClasses( DecimalMin.class );
assertEquals( validators.size(), 3, "Wrong number of default validators" );
assertTrue( validators.contains( DecimalMinValidatorForCharSequence.class ), "Missing default validator" );
assertTrue( validators.contains( DecimalMinValidatorForNumber.class ), "Missing default validator" );
assertTrue( validators.contains( DecimalMinValidatorForFoo.class ), "Missing xml configured validator" );
assertTrue( validators.indexOf( DecimalMinValidatorForFoo.class ) == 2, "The custom validator must be last" );
}

@Test
@TestForIssue(jiraKey = "HV-782")
public void testOverridingOfConstraintValidatorsFromMultipleMappingFilesThrowsException() {
Set<InputStream> mappingStreams = newHashSet();
mappingStreams.add( XmlMappingParserTest.class.getResourceAsStream( "decimal-min-mapping-1.xml" ) );
mappingStreams.add( XmlMappingParserTest.class.getResourceAsStream( "decimal-min-mapping-2.xml" ) );

try {
xmlMappingParser.parse( mappingStreams );
fail( "Constraint definitions for a given constraint can only be overridden once" );
}
catch ( ValidationException e ) {
assertTrue( e.getMessage().startsWith( "HV000167" ) );
}
}

public static class DecimalMinValidatorForFoo implements ConstraintValidator<DecimalMin, Foo> {

@Override
public void initialize(DecimalMin constraintAnnotation) {
}

@Override
public boolean isValid(Foo value, ConstraintValidatorContext context) {
return false;
}
}

public static class DecimalMinValidatorForBar implements ConstraintValidator<DecimalMin, Bar> {

@Override
public void initialize(DecimalMin constraintAnnotation) {
}

@Override
public boolean isValid(Bar value, ConstraintValidatorContext context) {
return false;
}
}

public static class Foo {
}

public static class Bar {
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<constraint-mappings
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping">

<constraint-definition annotation="javax.validation.constraints.DecimalMin">
<validated-by include-existing-validators="true">
<value>org.hibernate.validator.test.internal.xml.XmlMappingParserTest$DecimalMinValidatorForFoo</value>
</validated-by>
</constraint-definition>
</constraint-mappings>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<constraint-mappings
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping">

<constraint-definition annotation="javax.validation.constraints.DecimalMin">
<validated-by include-existing-validators="true">
<value>org.hibernate.validator.test.internal.xml.XmlMappingParserTest$DecimalMinValidatorForBar</value>
</validated-by>
</constraint-definition>
</constraint-mappings>

0 comments on commit 887426d

Please sign in to comment.