From 89b7683437a3ea4960bc50aa1d91fdaaa3c1b9cc Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Tue, 29 Sep 2015 15:14:25 +0200 Subject: [PATCH 001/189] [maven-release-plugin] prepare for next development iteration --- annotation-processor/pom.xml | 2 +- cdi/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- engine-jdk8-tests/pom.xml | 2 +- engine/pom.xml | 2 +- integration/pom.xml | 2 +- osgi/integrationtest/pom.xml | 2 +- osgi/karaf-features/pom.xml | 2 +- osgi/pom.xml | 2 +- performance/pom.xml | 2 +- pom.xml | 4 ++-- tck-runner/pom.xml | 2 +- test-utils/pom.xml | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml index 68aad13b8a..b57f4b76cf 100644 --- a/annotation-processor/pom.xml +++ b/annotation-processor/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/cdi/pom.xml b/cdi/pom.xml index 6306440cf5..7f3cf0bfd9 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 0aef7da807..16e2e2ec25 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index 4afa6578b1..dec85332fc 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/engine-jdk8-tests/pom.xml b/engine-jdk8-tests/pom.xml index ef08354406..a04dd88a2d 100644 --- a/engine-jdk8-tests/pom.xml +++ b/engine-jdk8-tests/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.2.2.Final + 5.3.0-SNAPSHOT hibernate-validator-engine-jdk8-tests diff --git a/engine/pom.xml b/engine/pom.xml index f556d5248e..d1e24ca127 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/integration/pom.xml b/integration/pom.xml index 2c2044f79a..23eed0ee65 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/osgi/integrationtest/pom.xml b/osgi/integrationtest/pom.xml index 70b5ad77b8..a4056ff0e7 100644 --- a/osgi/integrationtest/pom.xml +++ b/osgi/integrationtest/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-osgi org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/osgi/karaf-features/pom.xml b/osgi/karaf-features/pom.xml index 44f352b057..3fe2274b66 100644 --- a/osgi/karaf-features/pom.xml +++ b/osgi/karaf-features/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-osgi org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/osgi/pom.xml b/osgi/pom.xml index a3b2655d82..8f903a6d56 100644 --- a/osgi/pom.xml +++ b/osgi/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/performance/pom.xml b/performance/pom.xml index 94e084e0aa..dd143f3b5d 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index f28c505395..3363fad7ab 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.2.2.Final + 5.3.0-SNAPSHOT pom Hibernate Validator Aggregator @@ -736,7 +736,7 @@ scm:git:git://github.com/hibernate/hibernate-validator.git scm:git:git@github.com:hibernate/hibernate-validator.git http://github.com/hibernate/hibernate-validator - 5.2.2.Final + HEAD diff --git a/tck-runner/pom.xml b/tck-runner/pom.xml index d3e8923884..408a9606c3 100644 --- a/tck-runner/pom.xml +++ b/tck-runner/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.2.2.Final + 5.3.0-SNAPSHOT ../pom.xml diff --git a/test-utils/pom.xml b/test-utils/pom.xml index 9505417f01..5953422ae9 100644 --- a/test-utils/pom.xml +++ b/test-utils/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.2.2.Final + 5.3.0-SNAPSHOT hibernate-validator-test-utils From 9afd5ee846fca7838c5f9e7ae5336b7237a5c855 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Wed, 14 Oct 2015 13:01:15 -0300 Subject: [PATCH 002/189] HV-1022: Missing check for type argument constraints on validateValue Type argument constraints should be checked before returning failing constraints --- ...ationConstraintUsingValidateValueTest.java | 92 +++++++++++++++++++ .../internal/engine/ValidatorImpl.java | 2 +- 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidateValueTest.java diff --git a/engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidateValueTest.java b/engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidateValueTest.java new file mode 100644 index 0000000000..274a26b33d --- /dev/null +++ b/engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidateValueTest.java @@ -0,0 +1,92 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.valuehandling; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Optional; +import java.util.Set; +import javax.validation.Constraint; +import javax.validation.ConstraintViolation; +import javax.validation.Payload; +import javax.validation.ReportAsSingleViolation; +import javax.validation.Validator; +import javax.validation.constraints.Null; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import org.hibernate.validator.constraints.CompositionType; +import org.hibernate.validator.constraints.ConstraintComposition; +import org.hibernate.validator.constraints.NotBlank; +import org.hibernate.validator.testutil.TestForIssue; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintTypes; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectPropertyPaths; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations; +import static org.hibernate.validator.testutil.ValidatorUtil.getValidator; + +/** + * Test usage of {@link Optional} on fields using validate value. + * + * @author George Gastaldi + */ +public class OptionalTypeAnnotationConstraintUsingValidateValueTest { + private Validator validator; + + @BeforeClass + public void setup() { + validator = getValidator(); + } + + @Test + @TestForIssue(jiraKey = "HV-1022") + public void type_annotation_constraint_violation_is_propagated_even_if_there_are_no_other_constraints() { + Model model = new Model(); + model.valueWithNullOrNotBlank = Optional.of( "" ); + + Set> constraintViolations = validator.validateValue( + Model.class, + "valueWithNullOrNotBlank", + model.valueWithNullOrNotBlank + ); + assertNumberOfViolations( constraintViolations, 1 ); + assertCorrectPropertyPaths( constraintViolations, "valueWithNullOrNotBlank" ); + assertCorrectConstraintViolationMessages( constraintViolations, "type" ); + assertCorrectConstraintTypes( constraintViolations, NullOrNotBlank.class ); + } + + @ConstraintComposition(CompositionType.OR) + @Null + @NotBlank + @ReportAsSingleViolation + @Constraint(validatedBy = {}) + @Target({ TYPE_USE, METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) + @Retention(RUNTIME) + @Documented + public @interface NullOrNotBlank { + + String message() default "NullOrNotBlank"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + private static class Model { + Optional<@NullOrNotBlank(message = "type") String> valueWithNullOrNotBlank; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index c891a8d369..8042fa4dd1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -858,7 +858,7 @@ private Set> validateValueInContext(ValidationContext ); valueContext.setCurrentValidatedValue( value ); - if ( metaConstraints.size() == 0 ) { + if ( metaConstraints.size() == 0 && typeArgumentConstraints.size() == 0 ) { return context.getFailingConstraints(); } From 61166d5dc45298c10e0c2be21ba249974bda2112 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Thu, 15 Oct 2015 17:11:05 +0200 Subject: [PATCH 003/189] HV-1022 Also making sure that validateProperty works for the case that there are type annotation constraints only --- ...TypeAnnotationConstraintUsingValidatePropertyTest.java | 8 ++++++-- .../validator/internal/engine/ValidatorImpl.java | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java b/engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java index 7a118678d9..7f780a524d 100644 --- a/engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java +++ b/engine-jdk8-tests/src/test/java/org/hibernate/validator/test/internal/engine/valuehandling/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java @@ -154,10 +154,10 @@ public void reference_is_validated_for_null_value_and_unwrapped() { @Test public void null_or_not_blank_type_on_optional_is_validated_for_blank_string() { - Model model = new Model(); + ModelWithSingleTypeAnnotationConstraint model = new ModelWithSingleTypeAnnotationConstraint(); model.valueWithNullOrNotBlank = Optional.of( "" ); - Set> constraintViolations = validator.validateProperty( + Set> constraintViolations = validator.validateProperty( model, "valueWithNullOrNotBlank" ); @@ -235,4 +235,8 @@ private static class Model { @NullOrNotBlank(message = "reference") String valueReference; } + + private static class ModelWithSingleTypeAnnotationConstraint { + Optional<@NullOrNotBlank(message = "type") String> valueWithNullOrNotBlank; + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 8042fa4dd1..8b55b30401 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -805,7 +805,7 @@ private Set> validatePropertyInContext(ValidationCont throw log.getInvalidPropertyPathException(); } - if ( metaConstraints.size() == 0 ) { + if ( metaConstraints.size() == 0 && typeUseConstraints.size() == 0) { return context.getFailingConstraints(); } From d7b39800eab18a09358d626231be07fdb15c9f91 Mon Sep 17 00:00:00 2001 From: Lucas POUZAC Date: Wed, 14 Oct 2015 10:13:54 +0200 Subject: [PATCH 004/189] HV-1021 Cache result of loadClass for el dependencies checking --- .../internal/engine/ValidatorFactoryImpl.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 367f07b0d5..2cb7205db1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -64,6 +64,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private static final Log log = LoggerFactory.make(); + private static Boolean missingElDependencies; + /** * The default message interpolator for this factory. */ @@ -189,7 +191,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { private static ClassLoader getExternalClassLoader(ConfigurationState configurationState) { return ( configurationState instanceof ConfigurationImpl ) ? ( (ConfigurationImpl) configurationState ).getExternalClassLoader() : null; } - + private static Set getConstraintMappings(ConfigurationState configurationState, ClassLoader externalClassLoader) { Set constraintMappings; @@ -332,10 +334,16 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, // HV-793 - To fail eagerly in case we have no EL dependencies on the classpath we try to load the expression // factory if( messageInterpolator instanceof ResourceBundleMessageInterpolator ) { - try { - ResourceBundleMessageInterpolator.class.getClassLoader().loadClass( "javax.el.ExpressionFactory" ); + if ( missingElDependencies == null ) { + try { + ResourceBundleMessageInterpolator.class.getClassLoader().loadClass( "javax.el.ExpressionFactory" ); + missingElDependencies = false; + } + catch ( ClassNotFoundException e ) { + missingElDependencies = true; + } } - catch ( ClassNotFoundException e ) { + if ( missingElDependencies ) { throw log.getMissingELDependenciesException(); } } From e9dcc799d4ecb653a8559a67b09c3cbcaaad65c4 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Fri, 16 Oct 2015 13:47:43 +0200 Subject: [PATCH 005/189] HV-1021 Making sure class loading is done via a LoadClass PrivilegedAction --- .../internal/engine/ValidatorFactoryImpl.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 2cb7205db1..ff735c1cf8 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -14,11 +14,11 @@ import java.util.List; import java.util.Map; import java.util.Set; - import javax.validation.ConstraintValidatorFactory; import javax.validation.MessageInterpolator; import javax.validation.ParameterNameProvider; import javax.validation.TraversableResolver; +import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.spi.ConfigurationState; @@ -28,7 +28,9 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionBuilderImpl; +import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.time.DefaultTimeProvider; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; @@ -41,9 +43,7 @@ import org.hibernate.validator.internal.util.privilegedactions.LoadClass; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; -import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; -import org.hibernate.validator.internal.engine.time.DefaultTimeProvider; import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; import org.hibernate.validator.spi.time.TimeProvider; import org.hibernate.validator.spi.valuehandling.ValidatedValueUnwrapper; @@ -336,10 +336,16 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, if( messageInterpolator instanceof ResourceBundleMessageInterpolator ) { if ( missingElDependencies == null ) { try { - ResourceBundleMessageInterpolator.class.getClassLoader().loadClass( "javax.el.ExpressionFactory" ); + run( + LoadClass.action( + "javax.el.ExpressionFactory", + messageInterpolator.getClass().getClassLoader(), + false + ) + ); missingElDependencies = false; } - catch ( ClassNotFoundException e ) { + catch ( ValidationException e ) { missingElDependencies = true; } } From 0b90fffaacc8bb4724c47484b35b134a1208de1f Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Fri, 16 Oct 2015 13:48:44 +0200 Subject: [PATCH 006/189] Updating copyright.txt --- copyright.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/copyright.txt b/copyright.txt index 6e79dd3d76..3d59c790d6 100644 --- a/copyright.txt +++ b/copyright.txt @@ -23,6 +23,7 @@ Justin Nauman Kevin Pollet Khalid Alqinyah Leonardo Loch Zanivan +Lucas Pouzac Mark Hobson Paolo Perrotta Pete Muir From 07f38d1adc09387f5131e2149d49d3a421153c12 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Tue, 27 Oct 2015 12:52:23 +0100 Subject: [PATCH 007/189] HV-1025 Re-introducing CloseIgnoringInputStream to wrap input stream of provided mapping streams to prevent some StAX implementations to close the input stream prematurely --- .../internal/xml/XmlMappingParser.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java index 468bebb167..072a2cdaf3 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.internal.xml; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; @@ -129,13 +130,14 @@ public final void parse(Set mappingStreams) { Set alreadyProcessedConstraintDefinitions = newHashSet(); for ( InputStream in : mappingStreams ) { + // check whether mark is supported, if so we can reset the stream in order to allow reuse of Configuration boolean markSupported = in.markSupported(); if ( markSupported ) { in.mark( Integer.MAX_VALUE ); } - XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", in ); + XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader ); String schemaResourceName = getSchemaResourceName( schemaVersion ); Schema schema = xmlParserHelper.getSchema( schemaResourceName ); @@ -384,4 +386,18 @@ private T run(PrivilegedExceptionAction action) throws JAXBException { throw log.getErrorParsingMappingFileException( e ); } } + + // HV-1025 - On some JVMs (eg the IBM JVM) the JAXB implementation closes the underlying input stream. + // To prevent this we wrap the input stream to be able to ignore the close event. It is the responsibility + // of the client API to close the stream (as per Bean Validation spec, see javax.validation.Configuration). + private static class CloseIgnoringInputStream extends FilterInputStream { + public CloseIgnoringInputStream(InputStream in) { + super( in ); + } + + @Override + public void close() { + // do nothing + } + } } From 1f85aa250eb787923c982f2654cf2a630b1a55b1 Mon Sep 17 00:00:00 2001 From: Jiri Bilek Date: Sat, 31 Oct 2015 17:31:02 +0100 Subject: [PATCH 008/189] HV-1019 PathImpl copy constructor fix The copy constructor sets the hashCode to -1, in order to be computed on first attempt to use it. --- .../org/hibernate/validator/internal/engine/path/PathImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java index ba16b17d12..818aa51d06 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java @@ -302,6 +302,7 @@ private PathImpl() { private PathImpl(List nodeList) { this.nodeList = new ArrayList( nodeList ); + this.hashCode = -1; } private static PathImpl parseProperty(String propertyName) { From fbfa96f3481bdd03759bbf3ba51afd189f3649bc Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Mon, 2 Nov 2015 13:41:40 +0100 Subject: [PATCH 009/189] HV-1019 Initializing hash code in one more constructor --- .../org/hibernate/validator/internal/engine/path/PathImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java index 818aa51d06..14ebf09921 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java @@ -298,6 +298,7 @@ private PathImpl(PathImpl path) { private PathImpl() { nodeList = new ArrayList(); + this.hashCode = -1; } private PathImpl(List nodeList) { From 1b1f1e0ee8477c454d9a40945b5b3f1d2d0f1f07 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Tue, 27 Oct 2015 14:15:14 +0100 Subject: [PATCH 010/189] HV-1023 Prevent resource aggregation to occur in an Google App Environment --- .../internal/util/logging/Messages.java | 8 ++++++++ .../PlatformResourceBundleLocator.java | 18 +++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java index 2a99d47278..5fa4787693 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java @@ -84,6 +84,14 @@ public interface Messages { @Message(value = "Custom parameterized types with more than one type argument are not supported and " + "will not be checked for type use constraints.") String parameterizedTypesWithMoreThanOneTypeArgument(); + + @Message(value = "Hibernate Validator cannot instantiate AggregateResourceBundle.CONTROL. " + + "This can happen most notably in a Google App Engine environment. " + + "A PlatformResourceBundleLocator without bundle aggregation was created. " + + "This only effects you in case you are using multiple ConstraintDefinitionContributor jars. " + + "ConstraintDefinitionContributors are a Hibernate Validator specific feature. All Bean Validation " + + "features work as expected. See also https://hibernate.atlassian.net/browse/HV-1023.") + String unableToUseResourceBundleAggregation(); } diff --git a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java index b8bf636f56..f181a21c86 100644 --- a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java +++ b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java @@ -26,6 +26,7 @@ import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; /** * A resource bundle locator, that loads resource bundles by invoking {@code ResourceBundle.loadBundle(String, Local, ClassLoader)}. @@ -38,6 +39,7 @@ */ public class PlatformResourceBundleLocator implements ResourceBundleLocator { private static final Logger log = Logger.getLogger( PlatformResourceBundleLocator.class.getName() ); + private static Boolean resourceBundleControlInstantiable; private final String bundleName; private final ClassLoader classLoader; @@ -77,7 +79,21 @@ public PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader, this.bundleName = bundleName; this.classLoader = classLoader; - this.aggregate = aggregate; + + // HV-1023 In an Google App Engine environment bundle aggregation is not possible, since ResourceBundle.Control + // is not on the list of white listed classes in this environment. See http://code.google.com/appengine/docs/java/jrewhitelist.html + // Try to create AggregateResourceBundle.CONTROL proactively, if it fails skip resource aggregation + if ( resourceBundleControlInstantiable == null ) { + try { + ResourceBundle.Control dummyControl = AggregateResourceBundle.CONTROL; + resourceBundleControlInstantiable = true; + } + catch ( NoClassDefFoundError e ) { + resourceBundleControlInstantiable = false; + log.info( MESSAGES.unableToUseResourceBundleAggregation() ); + } + } + this.aggregate = aggregate && resourceBundleControlInstantiable; } /** From c34fe9fd36a246bc2328e933ee29743c8c04674d Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Wed, 4 Nov 2015 09:16:39 +0100 Subject: [PATCH 011/189] HV-1023 Doing availability check of ResourceBundle.Control once at class load time --- .../PlatformResourceBundleLocator.java | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java index f181a21c86..015439f53a 100644 --- a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java +++ b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java @@ -18,12 +18,11 @@ import java.util.ResourceBundle; import java.util.Set; -import org.jboss.logging.Logger; - import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; import org.hibernate.validator.internal.util.privilegedactions.GetResources; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; +import org.jboss.logging.Logger; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; @@ -38,8 +37,9 @@ * @author Gunnar Morling */ public class PlatformResourceBundleLocator implements ResourceBundleLocator { + private static final Logger log = Logger.getLogger( PlatformResourceBundleLocator.class.getName() ); - private static Boolean resourceBundleControlInstantiable; + private static final boolean RESOURCE_BUNDLE_CONTROLE_INSTANTIABLE = determineAvailabilityOfResourceBundleControl(); private final String bundleName; private final ClassLoader classLoader; @@ -80,20 +80,27 @@ public PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader, this.bundleName = bundleName; this.classLoader = classLoader; - // HV-1023 In an Google App Engine environment bundle aggregation is not possible, since ResourceBundle.Control - // is not on the list of white listed classes in this environment. See http://code.google.com/appengine/docs/java/jrewhitelist.html - // Try to create AggregateResourceBundle.CONTROL proactively, if it fails skip resource aggregation - if ( resourceBundleControlInstantiable == null ) { - try { - ResourceBundle.Control dummyControl = AggregateResourceBundle.CONTROL; - resourceBundleControlInstantiable = true; - } - catch ( NoClassDefFoundError e ) { - resourceBundleControlInstantiable = false; - log.info( MESSAGES.unableToUseResourceBundleAggregation() ); - } + this.aggregate = aggregate && RESOURCE_BUNDLE_CONTROLE_INSTANTIABLE; + } + + /** + * + * In an Google App Engine environment bundle aggregation is not possible, since ResourceBundle.Control + * is not on the list of white listed classes in this environment. See http://code.google.com/appengine/docs/java/jrewhitelist.html + * to create AggregateResourceBundle.CONTROL proactively, if it fails skip resource aggregation. + *

+ * Also see HV-1023. + */ + private static boolean determineAvailabilityOfResourceBundleControl() { + try { + @SuppressWarnings("unused") + ResourceBundle.Control dummyControl = AggregateResourceBundle.CONTROL; + return true; + } + catch ( NoClassDefFoundError e ) { + log.info( MESSAGES.unableToUseResourceBundleAggregation() ); + return false; } - this.aggregate = aggregate && resourceBundleControlInstantiable; } /** From 357ad6db9eb2d1b7757fa99027ef2b2cbdca0845 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Wed, 4 Nov 2015 11:32:38 +0100 Subject: [PATCH 012/189] HV-1023 Typo and javadoc fixes --- .../PlatformResourceBundleLocator.java | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java index 015439f53a..08a0f87baf 100644 --- a/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java +++ b/engine/src/main/java/org/hibernate/validator/resourceloading/PlatformResourceBundleLocator.java @@ -39,7 +39,7 @@ public class PlatformResourceBundleLocator implements ResourceBundleLocator { private static final Logger log = Logger.getLogger( PlatformResourceBundleLocator.class.getName() ); - private static final boolean RESOURCE_BUNDLE_CONTROLE_INSTANTIABLE = determineAvailabilityOfResourceBundleControl(); + private static final boolean RESOURCE_BUNDLE_CONTROL_INSTANTIABLE = determineAvailabilityOfResourceBundleControl(); private final String bundleName; private final ClassLoader classLoader; @@ -80,27 +80,7 @@ public PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader, this.bundleName = bundleName; this.classLoader = classLoader; - this.aggregate = aggregate && RESOURCE_BUNDLE_CONTROLE_INSTANTIABLE; - } - - /** - * - * In an Google App Engine environment bundle aggregation is not possible, since ResourceBundle.Control - * is not on the list of white listed classes in this environment. See http://code.google.com/appengine/docs/java/jrewhitelist.html - * to create AggregateResourceBundle.CONTROL proactively, if it fails skip resource aggregation. - *

- * Also see HV-1023. - */ - private static boolean determineAvailabilityOfResourceBundleControl() { - try { - @SuppressWarnings("unused") - ResourceBundle.Control dummyControl = AggregateResourceBundle.CONTROL; - return true; - } - catch ( NoClassDefFoundError e ) { - log.info( MESSAGES.unableToUseResourceBundleAggregation() ); - return false; - } + this.aggregate = aggregate && RESOURCE_BUNDLE_CONTROL_INSTANTIABLE; } /** @@ -183,6 +163,27 @@ private static T run(PrivilegedAction action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } + /** + * + * In an Google App Engine environment bundle aggregation is not possible, since ResourceBundle.Control + * is not on the list of white listed classes in this environment. + * to create AggregateResourceBundle.CONTROL proactively, if it fails skip resource aggregation. + * + * @see JRE whitelist + * @see HV-1023 + */ + private static boolean determineAvailabilityOfResourceBundleControl() { + try { + @SuppressWarnings("unused") + ResourceBundle.Control dummyControl = AggregateResourceBundle.CONTROL; + return true; + } + catch ( NoClassDefFoundError e ) { + log.info( MESSAGES.unableToUseResourceBundleAggregation() ); + return false; + } + } + /** * Inspired by this * Stack Overflow question. From 7d350f612f3541c497f8ddf2a56304dc5c270110 Mon Sep 17 00:00:00 2001 From: Carlo de Wolf Date: Wed, 11 Nov 2015 11:09:13 +0100 Subject: [PATCH 013/189] HV-1026: Provide exception context to class loading exceptions and adding test case --- .../validator/internal/util/logging/Log.java | 6 +- .../util/privilegedactions/LoadClass.java | 33 +++++------ .../util/privilegedactions/LoadClassTest.java | 57 +++++++++++++++++++ 3 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/LoadClassTest.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index f840a93b90..15a90248db 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -233,10 +233,8 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp @Message(id = 64, value = "Unable to instantiate %1$s: %2$s.") ValidationException getUnableToInstantiateException(String message, Class clazz, @Cause Exception e); - @Message(id = 65, value = "Unable to load class: %s.") - ValidationException getUnableToLoadClassException(String className); - - ValidationException getUnableToLoadClassException(String className, @Cause Exception e); + @Message(id = 65, value = "Unable to load class: %s from %s.") + ValidationException getUnableToLoadClassException(String className, ClassLoader loader, @Cause Exception e); @Message(id = 68, value = "Start index cannot be negative: %d.") IllegalArgumentException getStartIndexCannotBeNegativeException(int startIndex); diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java index b96cb9e5fa..5ccf24c869 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java @@ -66,33 +66,34 @@ public Class run() { } // HV-363 - library internal classes are loaded via Class.forName first - private Class loadClassInValidatorNameSpace() { + final ClassLoader loader = HibernateValidator.class.getClassLoader(); + final Exception exception; try { return Class.forName( className, true, HibernateValidator.class.getClassLoader() ); } catch ( ClassNotFoundException e ) { - //ignore -- try using the class loader of context first + exception = e; } catch ( RuntimeException e ) { - // ignore + exception = e; } if ( fallbackOnTCCL ) { - try { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - if ( contextClassLoader != null ) { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if ( contextClassLoader != null ) { + try { return Class.forName( className, false, contextClassLoader ); } - else { - throw log.getUnableToLoadClassException( className ); + catch ( ClassNotFoundException e ) { + throw log.getUnableToLoadClassException( className, contextClassLoader, e ); } } - catch ( ClassNotFoundException e ) { - throw log.getUnableToLoadClassException( className, e ); + else { + throw log.getUnableToLoadClassException( className, loader, exception ); } } else { - throw log.getUnableToLoadClassException( className ); + throw log.getUnableToLoadClassException( className, loader, exception ); } } @@ -104,11 +105,9 @@ private Class loadNonValidatorClass() { } } catch ( ClassNotFoundException e ) { - // ignore - try using the classloader of the caller first exception = e; } catch ( RuntimeException e ) { - // ignore exception = e; } if ( fallbackOnTCCL ) { @@ -120,19 +119,21 @@ private Class loadNonValidatorClass() { } catch ( ClassNotFoundException e ) { // ignore - try using the classloader of the caller first + // TODO: might be wise to somehow log this } catch ( RuntimeException e ) { // ignore } + final ClassLoader loader = LoadClass.class.getClassLoader(); try { - return Class.forName( className, true, LoadClass.class.getClassLoader() ); + return Class.forName( className, true, loader ); } catch ( ClassNotFoundException e ) { - throw log.getUnableToLoadClassException( className, e ); + throw log.getUnableToLoadClassException( className, loader, e ); } } else { - throw log.getUnableToLoadClassException( className, exception ); + throw log.getUnableToLoadClassException( className, classLoader, exception ); } } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/LoadClassTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/LoadClassTest.java new file mode 100644 index 0000000000..27f376970c --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/LoadClassTest.java @@ -0,0 +1,57 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.util.privilegedactions; + +import javax.validation.ValidationException; + +import org.testng.annotations.Test; + +import org.hibernate.validator.internal.util.privilegedactions.LoadClass; + +import static java.lang.Thread.currentThread; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; +import static org.testng.AssertJUnit.assertTrue; + +/** + * @author Carlo de Wolf + */ +public class LoadClassTest { + @Test + public void test_loading_dummy_class_throws_exception_without_fallback_to_tcl() { + final LoadClass action = LoadClass.action( "org.hibernate.validator.Dummy", null, false ); + runLoadClass( action ); + } + + @Test + public void test_loading_dummy_class_throws_exception_with_fallback_to_tcl() { + final LoadClass action = LoadClass.action( "org.hibernate.validator.Dummy", null, true ); + final ClassLoader current = currentThread().getContextClassLoader(); + try { + currentThread().setContextClassLoader( null ); + runLoadClass( action ); + } + finally { + currentThread().setContextClassLoader( current ); + } + } + + private void runLoadClass(LoadClass action) { + try { + action.run(); + fail( "Should have thrown javax.validation.ValidationException" ); + } + catch ( ValidationException e ) { + String expectedMessageId = "HV000065"; + assertTrue( + "Wrong error message. Expected " + expectedMessageId + " ,but got " + e.getMessage(), + e.getMessage().startsWith( expectedMessageId ) + ); + assertNotNull( e.getCause(), "HV-1026: exception cause should be set" ); + } + } +} From 20d3c90fb88eb0321b770e0ba2022615c17e7620 Mon Sep 17 00:00:00 2001 From: "Kellin.lee" Date: Thu, 26 Nov 2015 14:25:44 +0900 Subject: [PATCH 014/189] HV-1035 Fixing mixed up validation messages in Korean. The NotNull and Null constraint messages are changed with each other. --- .../org/hibernate/validator/ValidationMessages_ko.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties index 2e7d2fd7a8..5416afc411 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties @@ -6,8 +6,8 @@ javax.validation.constraints.Digits.message = \uc22b\uc790 \uac12\uc774 \ud5c8\u javax.validation.constraints.Future.message = \ubc18\ub4dc\uc2dc \ubbf8\ub798 \ub0a0\uc9dc\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. javax.validation.constraints.Max.message = \ubc18\ub4dc\uc2dc {value}\ubcf4\ub2e4 \uac19\uac70\ub098 \uc791\uc544\uc57c \ud569\ub2c8\ub2e4. javax.validation.constraints.Min.message = \ubc18\ub4dc\uc2dc {value}\ubcf4\ub2e4 \uac19\uac70\ub098 \ucee4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.NotNull.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc5c6\uc5b4\uc57c \ud569\ub2c8\ub2e4. -javax.validation.constraints.Null.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4. +javax.validation.constraints.NotNull.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4. +javax.validation.constraints.Null.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc5c6\uc5b4\uc57c \ud569\ub2c8\ub2e4. javax.validation.constraints.Past.message = \ubc18\ub4dc\uc2dc \uacfc\uac70 \ub0a0\uc9dc\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. javax.validation.constraints.Pattern.message = \uc815\uaddc \ud45c\ud604\uc2dd "{regexp}" \ud328\ud134\uacfc \uc77c\uce58\ud574\uc57c \ud569\ub2c8\ub2e4. javax.validation.constraints.Size.message = \ubc18\ub4dc\uc2dc \ucd5c\uc18c\uac12 {min}\uacfc(\uc640) \ucd5c\ub300\uac12 {max} \uc0ac\uc774\uc758 \ud06c\uae30\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. From 3daf761042b81a2931e73f223f33dd00ab8a5328 Mon Sep 17 00:00:00 2001 From: Christian Ivan Date: Fri, 4 Dec 2015 18:20:32 +0700 Subject: [PATCH 015/189] HV-1037 Code Quality Improvements - Constructors should only call non-overridable methods - String literals should be placed on the left side when checking for equality --- .../validator/internal/engine/ConfigurationImpl.java | 2 +- .../messageinterpolation/parser/TokenCollector.java | 2 +- .../validator/internal/engine/path/NodeImpl.java | 2 +- .../aggregated/AbstractConstraintMetaData.java | 2 +- .../metadata/aggregated/ExecutableMetaData.java | 2 +- .../internal/metadata/aggregated/PropertyMetaData.java | 2 +- .../test/internal/engine/proxy/ProxyTest.java | 4 ++-- .../validator/test/internal/util/TypeHelperTest.java | 10 +++++----- .../xml/mixedconfiguration/annotation/Competition.java | 2 +- .../xml/mixedconfiguration/annotation/GameDetail.java | 2 +- .../xml/mixedconfiguration/xml/Competition.java | 2 +- .../xml/mixedconfiguration/xml/GameDetail.java | 2 +- .../validator/testutil/ValidationXmlTestHelper.java | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java index 8b402f71be..346c81136b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java @@ -252,7 +252,7 @@ public Set getConstraintDefinitionContributors( } @Override - public HibernateValidatorConfiguration addConstraintDefinitionContributor(ConstraintDefinitionContributor contributor) { + public final HibernateValidatorConfiguration addConstraintDefinitionContributor(ConstraintDefinitionContributor contributor) { Contracts.assertNotNull( contributor, MESSAGES.parameterMustNotBeNull( "contributor" ) ); constraintDefinitionContributors.add( contributor ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java index c65b7ac8c1..1443eee7f5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/TokenCollector.java @@ -102,7 +102,7 @@ public void next() throws MessageDescriptorFormatException { terminateToken(); } - public void parse() throws MessageDescriptorFormatException { + public final void parse() throws MessageDescriptorFormatException { currentParserState.start( this ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/path/NodeImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/path/NodeImpl.java index f987f54a7e..97f51a3a01 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/path/NodeImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/path/NodeImpl.java @@ -319,7 +319,7 @@ else if ( key != null ) { return builder.toString(); } - public int buildHashCode() { + public final int buildHashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( index == null ) ? 0 : index.hashCode() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java index 2dcbf9049c..edff98381a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java @@ -75,7 +75,7 @@ public ElementKind getKind() { } @Override - public boolean isCascading() { + public final boolean isCascading() { return isCascading; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java index ba3cdd6e60..fd28dc97b2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java @@ -333,7 +333,7 @@ public boolean accepts(ConstrainedElement constrainedElement) { } @Override - public void add(ConstrainedElement constrainedElement) { + public final void add(ConstrainedElement constrainedElement) { super.add( constrainedElement ); ConstrainedExecutable constrainedExecutable = (ConstrainedExecutable) constrainedElement; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java index a6ea6b2346..7505f54736 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/PropertyMetaData.java @@ -219,7 +219,7 @@ public boolean accepts(ConstrainedElement constrainedElement) { } @Override - public void add(ConstrainedElement constrainedElement) { + public final void add(ConstrainedElement constrainedElement) { super.add( constrainedElement ); // HV-925 diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/proxy/ProxyTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/proxy/ProxyTest.java index c79a75e8b3..1f47508c4f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/proxy/ProxyTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/proxy/ProxyTest.java @@ -58,11 +58,11 @@ public CustomInvocationHandler(Object o) { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if ( method.getName().equals( "getInteger" ) ) { + if ( "getInteger".equals( method.getName() ) ) { method.setAccessible( true ); return 0; } - if ( method.getName().equals( "getString" ) ) { + if ( "getString".equals( method.getName() ) ) { return "a"; } return method.invoke( o, args ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java index 59268e6fa1..a3679ee8d0 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/TypeHelperTest.java @@ -962,16 +962,16 @@ public String toString() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if ( method.getName().equals( "getBounds" ) ) { - return getBounds(); + if ( "getBounds".equals( method.getName() ) ) { + return getBounds(); } - else if ( method.getName().equals( "getGenericDeclaration" ) ) { + else if ( "getGenericDeclaration".equals( method.getName() ) ) { return getGenericDeclaration(); } - else if ( method.getName().equals( "getName" ) ) { + else if ( "getName".equals( method.getName() ) ) { return getName(); } - else if ( method.getName().equals( "toString" ) ) { + else if ( "toString".equals( method.getName() ) ) { return toString(); } else { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/Competition.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/Competition.java index eea3906986..c74a443428 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/Competition.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/Competition.java @@ -29,7 +29,7 @@ public String getName() { return name; } - public void setName(String name) { + public final void setName(String name) { this.name = name; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/GameDetail.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/GameDetail.java index 6e4a61ac76..388b209801 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/GameDetail.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/annotation/GameDetail.java @@ -27,7 +27,7 @@ public Competition getCompetition() { return competition; } - public void setCompetition(Competition competition) { + public final void setCompetition(Competition competition) { this.competition = competition; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/Competition.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/Competition.java index cd900c7861..d8dc901521 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/Competition.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/Competition.java @@ -24,7 +24,7 @@ public String getName() { return name; } - public void setName(String name) { + public final void setName(String name) { this.name = name; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/GameDetail.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/GameDetail.java index 7d00b371d3..2b626500fc 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/GameDetail.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/mixedconfiguration/xml/GameDetail.java @@ -22,7 +22,7 @@ public Competition getCompetition() { return competition; } - public void setCompetition(Competition competition) { + public final void setCompetition(Competition competition) { this.competition = competition; } } diff --git a/test-utils/src/main/java/org/hibernate/validator/testutil/ValidationXmlTestHelper.java b/test-utils/src/main/java/org/hibernate/validator/testutil/ValidationXmlTestHelper.java index a92851ef16..b1b11f7718 100644 --- a/test-utils/src/main/java/org/hibernate/validator/testutil/ValidationXmlTestHelper.java +++ b/test-utils/src/main/java/org/hibernate/validator/testutil/ValidationXmlTestHelper.java @@ -42,7 +42,7 @@ public void runWithCustomValidationXml(final String validationXmlName, Runnable new ClassLoader( previousContextCl ) { @Override public InputStream getResourceAsStream(String name) { - if ( name.equals( "META-INF/validation.xml" ) ) { + if ( "META-INF/validation.xml".equals( name ) ) { return clazz.getResourceAsStream( validationXmlName ); } From 45cdc60bf749fa5e3bc29c3bff6f72e09a34f41c Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Wed, 9 Dec 2015 15:35:47 +0100 Subject: [PATCH 016/189] Updating copyright.txt --- copyright.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/copyright.txt b/copyright.txt index 3d59c790d6..9052820f09 100644 --- a/copyright.txt +++ b/copyright.txt @@ -5,6 +5,8 @@ Brent Douglas Carlos Vara Dag Hovland Davide Marchignoli +Carlo de Wolf +Christian Ivan Denis Tiago Doug Lea Emmanuel Bernard @@ -22,6 +24,7 @@ Juraci Krohling Justin Nauman Kevin Pollet Khalid Alqinyah +Lee KyoungIl Leonardo Loch Zanivan Lucas Pouzac Mark Hobson From f8458d78daab70319d9c4d6b94dc3a88fdaebd74 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 18 Dec 2015 11:00:09 +0100 Subject: [PATCH 017/189] HV-1041 Upgrading to jaxb2-maven-plugin 2.2 to benefit from automatic Eclipse lifecycle mapping --- engine/pom.xml | 21 ++++++++------------- pom.xml | 2 +- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/engine/pom.xml b/engine/pom.xml index d1e24ca127..5b9b4be031 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -166,20 +166,15 @@ org.hibernate.validator.internal.xml true - validation-configuration-1.1.xsd,validation-mapping-1.1.xsd + + true + + 2.1 + + src/main/xsd/validation-configuration-1.1.xsd + src/main/xsd/validation-mapping-1.1.xsd + - - - javax.xml.bind - jaxb-api - 2.2.5 - - - com.sun.xml.bind - jaxb-impl - 2.1.13 - - org.apache.maven.plugins diff --git a/pom.xml b/pom.xml index 3363fad7ab..1eb46d5963 100644 --- a/pom.xml +++ b/pom.xml @@ -502,7 +502,7 @@ org.codehaus.mojo jaxb2-maven-plugin - 1.3.1 + 2.2 org.jboss.maven.plugins From 99e1a8ebcb51d60c91356eb251be8f2eccd18720 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 18 Dec 2015 11:00:32 +0100 Subject: [PATCH 018/189] HV-1041 Ignoring Maven dependency plug-in in Eclipse --- pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pom.xml b/pom.xml index 1eb46d5963..241886d200 100644 --- a/pom.xml +++ b/pom.xml @@ -705,6 +705,20 @@ + + + org.apache.maven.plugins + maven-dependency-plugin + [2.0,) + + copy-dependencies + copy + + + + + + From c161cb61a78a7ce34dc706339da5c7f5af667071 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 18 Dec 2015 11:02:44 +0100 Subject: [PATCH 019/189] HV-1041 Updating URLs in POM --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 241886d200..15b4d27580 100644 --- a/pom.xml +++ b/pom.xml @@ -728,13 +728,13 @@ - Hudson - http://hudson.qa.jboss.com/hudson/job/beanvalidation + Jenkins + https://hibernate-validator.ci.cloudbees.com/ JIRA - http://opensource.atlassian.com/projects/hibernate/browse/HV + https://hibernate.atlassian.net/projects/HV/summary 2007 From bde8ec93a3eed840ecc90d08a1862ee1b813c294 Mon Sep 17 00:00:00 2001 From: Chris Beckey Date: Wed, 12 Mar 2014 14:18:51 -0400 Subject: [PATCH 020/189] HV-872 Optional relaxation of parameter validation --- copyright.txt | 1 + .../cfgtest/resources/META-INF/validation.xml | 12 + .../HibernateValidatorConfiguration.java | 81 ++++++- .../validator/HibernateValidatorContext.java | 58 ++++- .../internal/engine/ConfigurationImpl.java | 47 +++- .../engine/MethodValidationConfiguration.java | 148 ++++++++++++ .../internal/engine/ValidatorContextImpl.java | 25 +- .../internal/engine/ValidatorFactoryImpl.java | 118 +++++++-- .../internal/engine/ValidatorImpl.java | 3 +- .../metadata/BeanMetaDataManager.java | 52 +++- .../metadata/aggregated/BeanMetaDataImpl.java | 59 +++-- .../aggregated/ExecutableMetaData.java | 68 ++++-- .../metadata/raw/ConstrainedExecutable.java | 4 +- .../ConfigurationFilePropertiesTest.java | 144 +++++++++++ .../HibernateValidatorConfigurationTest.java | 16 +- ...IllegalMethodParameterConstraintsTest.java | 10 +- ...RelaxedMethodParameterConstraintsTest.java | 224 ++++++++++++++++++ .../subject/ConcreteClass.java | 22 ++ .../methodvalidation/subject/Interface.java | 16 ++ .../methodvalidation/subject/SubClass.java | 23 ++ .../methodvalidation/subject/ValueObject.java | 27 +++ .../subject/ValueObjectSubClass.java | 29 +++ 22 files changed, 1090 insertions(+), 97 deletions(-) create mode 100644 engine/src/cfgtest/resources/META-INF/validation.xml create mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java create mode 100644 engine/src/test/java/org/hibernate/validator/cfgtest/ConfigurationFilePropertiesTest.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/RelaxedMethodParameterConstraintsTest.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ConcreteClass.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/Interface.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/SubClass.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObject.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObjectSubClass.java diff --git a/copyright.txt b/copyright.txt index 9052820f09..b14eeb10d5 100644 --- a/copyright.txt +++ b/copyright.txt @@ -6,6 +6,7 @@ Carlos Vara Dag Hovland Davide Marchignoli Carlo de Wolf +Chris Beckey Christian Ivan Denis Tiago Doug Lea diff --git a/engine/src/cfgtest/resources/META-INF/validation.xml b/engine/src/cfgtest/resources/META-INF/validation.xml new file mode 100644 index 0000000000..2125518c63 --- /dev/null +++ b/engine/src/cfgtest/resources/META-INF/validation.xml @@ -0,0 +1,12 @@ + + + org.hibernate.validator.HibernateValidator + + true + true + true + true + \ No newline at end of file diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index 0884d160b7..dd38358c91 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -22,6 +22,7 @@ * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Hardy Ferentschik + * @author Chris Beckey <cbeckey@paypal.com> */ public interface HibernateValidatorConfiguration extends Configuration { /** @@ -30,6 +31,27 @@ public interface HibernateValidatorConfiguration extends Configuration + * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may + * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation. + * This would pose a strengthening of preconditions to be fulfilled by the caller." + * + * + * @param allow flag determining whether validation will allow overriding to alter parameter constraints. + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + HibernateValidatorConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow); + + /** + * Define whether more than one constraint on a return value may be marked for cascading validation are allowed. + * The default value is {@code false}, i.e. do not allow. + * + * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy. + * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations) + * cannot mark the return value for cascaded validation if the return value has already been marked on the + * overridden method of the super type or interface." + * + * @param allow flag determining whether validation will allow multiple cascaded validation on return values. + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + HibernateValidatorConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow); + + /** + * Define whether parallel methods that define constraints should throw a {@code ConstraintDefinitionException}. The + * default value is {@code false}, i.e. do not allow. + * + * See Section 4.5.5 of JSR-349 Specification, specifically + * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy + * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class), + * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation. + * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller." + * + * @param allow flag determining whether validation will allow parameter constraints in parallel hierarchies + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow); } diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java index 21732553e5..13b3bd76b4 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java @@ -24,6 +24,7 @@ * @author Emmanuel Bernard * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Gunnar Morling + * @author Chris Beckey <cbeckey@paypal.com> */ public interface HibernateValidatorContext extends ValidatorContext { @@ -71,8 +72,7 @@ public interface HibernateValidatorContext extends ValidatorContext { * current time when validating {@code @Future} and {@code @Past} constraints. If not set or if {@code null} is * passed as a parameter, the time provider of the {@link javax.validation.ValidatorFactory} is used. * - * @param timeProvider - * the time provider to register. + * @param timeProvider the time provider to register. * * @return {@code this} following the chaining method pattern * @@ -80,4 +80,58 @@ public interface HibernateValidatorContext extends ValidatorContext { * @since 5.2 */ HibernateValidatorContext timeProvider(TimeProvider timeProvider); + + /** + * Define whether overriding methods that override constraints should throw a {@code ConstraintDefinitionException}. + * The default value is {@code false}, i.e. do not allow. + * + * See Section 4.5.5 of JSR-349 Specification, specifically + *

+	 * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
+	 * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.
+	 * This would pose a strengthening of preconditions to be fulfilled by the caller."
+	 * 
+ * + * @param allow flag determining whether validation will allow overriding to alter parameter constraints. + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + HibernateValidatorContext allowOverridingMethodAlterParameterConstraint(boolean allow); + + /** + * Define whether more than one constraint on a return value may be marked for cascading validation are allowed. + * The default value is {@code false}, i.e. do not allow. + * + * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy. + * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations) + * cannot mark the return value for cascaded validation if the return value has already been marked on the + * overridden method of the super type or interface." + * + * @param allow flag determining whether validation will allow multiple cascaded validation on return values. + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + HibernateValidatorContext allowMultipleCascadedValidationOnReturnValues(boolean allow); + + /** + * Define whether parallel methods that define constraints should throw a {@code ConstraintDefinitionException}. The + * default value is {@code false}, i.e. do not allow. + * + * See Section 4.5.5 of JSR-349 Specification, specifically + * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy + * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class), + * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation. + * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller." + * + * @param allow flag determining whether validation will allow parameter constraints in parallel hierarchies + * + * @return {@code this} following the chaining method pattern + * + * @since 5.3 + */ + HibernateValidatorContext allowParallelMethodsDefineParameterConstraints(boolean allow); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java index 346c81136b..7e73a778ab 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java @@ -14,7 +14,6 @@ import java.util.List; import java.util.Map; import java.util.Set; - import javax.validation.BootstrapConfiguration; import javax.validation.ConstraintValidatorFactory; import javax.validation.MessageInterpolator; @@ -33,7 +32,6 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; import org.hibernate.validator.internal.engine.resolver.DefaultTraversableResolver; import org.hibernate.validator.internal.engine.valuehandling.OptionalValueUnwrapper; -import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.Version; @@ -60,6 +58,7 @@ * @author Hardy Ferentschik * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Chris Beckey <cbeckey@paypal.com> */ public class ConfigurationImpl implements HibernateValidatorConfiguration, ConfigurationState { @@ -81,16 +80,17 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi private ValidationProviderResolver providerResolver; private final ValidationBootstrapParameters validationBootstrapParameters; private boolean ignoreXmlConfiguration = false; - private final Set configurationStreams = CollectionHelper.newHashSet(); + private final Set configurationStreams = newHashSet(); private BootstrapConfiguration bootstrapConfiguration; // HV-specific options - private final Set programmaticMappings = CollectionHelper.newHashSet(); + private final Set programmaticMappings = newHashSet(); private boolean failFast; private final Set constraintDefinitionContributors = newHashSet(); private final List> validatedValueHandlers = newArrayList(); private ClassLoader externalClassLoader; private TimeProvider timeProvider; + private MethodValidationConfiguration methodValidationConfiguration = new MethodValidationConfiguration(); public ConfigurationImpl(BootstrapState state) { this(); @@ -136,9 +136,10 @@ private ConfigurationImpl() { private ValidatedValueUnwrapper createJavaFXUnwrapperClass(TypeResolutionHelper typeResolutionHelper) { try { Class jfxUnwrapperClass = run( LoadClass.action( JFX_UNWRAPPER_CLASS, getClass().getClassLoader() ) ); - return (ValidatedValueUnwrapper) ( jfxUnwrapperClass.getConstructor( TypeResolutionHelper.class ).newInstance( typeResolutionHelper ) ); + return (ValidatedValueUnwrapper) ( jfxUnwrapperClass.getConstructor( TypeResolutionHelper.class ) + .newInstance( typeResolutionHelper ) ); } - catch (Exception e) { + catch ( Exception e ) { throw log.validatedValueUnwrapperCannotBeCreated( JFX_UNWRAPPER_CLASS, e ); } } @@ -213,6 +214,39 @@ public final HibernateValidatorConfiguration failFast(boolean failFast) { return this; } + @Override + public HibernateValidatorConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow) { + this.methodValidationConfiguration.allowOverridingMethodAlterParameterConstraint( allow ); + return this; + } + + public boolean isAllowOverridingMethodAlterParameterConstraint() { + return this.methodValidationConfiguration.isAllowOverridingMethodAlterParameterConstraint(); + } + + @Override + public HibernateValidatorConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow) { + this.methodValidationConfiguration.allowMultipleCascadedValidationOnReturnValues( allow ); + return this; + } + + public boolean isAllowMultipleCascadedValidationOnReturnValues() { + return this.methodValidationConfiguration.isAllowMultipleCascadedValidationOnReturnValues(); + } + + public HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow) { + this.methodValidationConfiguration.allowParallelMethodsDefineParameterConstraints( allow ); + return this; + } + + public boolean isAllowParallelMethodsDefineParameterConstraints() { + return this.methodValidationConfiguration.isAllowParallelMethodsDefineParameterConstraints(); + } + + public MethodValidationConfiguration getMethodValidationConfiguration() { + return this.methodValidationConfiguration; + } + @Override public final ConstraintMapping createConstraintMapping() { return new DefaultConstraintMapping(); @@ -234,6 +268,7 @@ public final HibernateValidatorConfiguration addProperty(String name, String val return this; } + @Override public HibernateValidatorConfiguration addValidatedValueHandler(ValidatedValueUnwrapper handler) { Contracts.assertNotNull( handler, MESSAGES.parameterMustNotBeNull( "handler" ) ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java b/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java new file mode 100644 index 0000000000..03165aedd6 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java @@ -0,0 +1,148 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.engine; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.validator.internal.metadata.aggregated.rule.MethodConfigurationRule; +import org.hibernate.validator.internal.metadata.aggregated.rule.OverridingMethodMustNotAlterParameterConstraints; +import org.hibernate.validator.internal.metadata.aggregated.rule.ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue; +import org.hibernate.validator.internal.metadata.aggregated.rule.ParallelMethodsMustNotDefineParameterConstraints; +import org.hibernate.validator.internal.metadata.aggregated.rule.ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine; +import org.hibernate.validator.internal.metadata.aggregated.rule.VoidMethodsMustNotBeReturnValueConstrained; + +/** + * These properties modify the behavior of the {@code }Validator} with respect to the Bean Validation + * specification section 4.5.5. In particular: + *
+ * "Out of the box, a conforming Bean Validation provider must throw a
+ * ConstraintDeclarationException when discovering that any of these rules are violated.
+ * In addition providers may implement alternative, potentially more liberal, approaches
+ * for handling constrained methods in inheritance hierarchies. Possible means for activating
+ * such alternative behavior include provider-specific configuration properties or annotations.
+ * Note that client code relying on such alternative behavior is not portable between Bean
+ * Validation providers."
+ * 
+ * + * @author Chris Beckey <cbeckey@paypal.com> + */ +public class MethodValidationConfiguration { + private boolean allowOverridingMethodAlterParameterConstraint = false; + private boolean allowMultipleCascadedValidationOnReturnValues = false; + private boolean allowParallelMethodsDefineParameterConstraints = false; + + /** + * Define whether overriding methods that override constraints should throw a {@code ConstraintDefinitionException}. + * The default value is {@code false}, i.e. do not allow. + * + * See Section 4.5.5 of JSR-349 Specification, specifically + *
+	 * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
+	 * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.
+	 * This would pose a strengthening of preconditions to be fulfilled by the caller."
+	 * 
+ * + * @param allow flag determining whether validation will allow overriding to alter parameter constraints. + * + * @return {@code this} following the chaining method pattern + */ + public MethodValidationConfiguration allowOverridingMethodAlterParameterConstraint(boolean allow) { + this.allowOverridingMethodAlterParameterConstraint = allow; + return this; + } + + /** + * Define whether more than one constraint on a return value may be marked for cascading validation are allowed. + * The default value is {@code false}, i.e. do not allow. + * + * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy. + * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations) + * cannot mark the return value for cascaded validation if the return value has already been marked on the + * overridden method of the super type or interface." + * + * @param allow flag determining whether validation will allow multiple cascaded validation on return values. + * + * @return {@code this} following the chaining method pattern + */ + public MethodValidationConfiguration allowMultipleCascadedValidationOnReturnValues(boolean allow) { + this.allowMultipleCascadedValidationOnReturnValues = allow; + return this; + } + + + /** + * Define whether parallel methods that define constraints should throw a {@code ConstraintDefinitionException}. The + * default value is {@code false}, i.e. do not allow. + * + * See Section 4.5.5 of JSR-349 Specification, specifically + * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy + * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class), + * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation. + * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller." + * + * @param allow flag determining whether validation will allow parameter constraints in parallel hierarchies + * + * @return {@code this} following the chaining method pattern + */ + public MethodValidationConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow) { + this.allowParallelMethodsDefineParameterConstraints = allow; + return this; + } + + /** + * @return {@code true} if more than one return value within a class hierarchy can be marked for cascaded + * validation, {@code false} otherwise. + */ + public boolean isAllowOverridingMethodAlterParameterConstraint() { + return this.allowOverridingMethodAlterParameterConstraint; + } + + /** + * @return {@code true} if more than one return value within a class hierarchy can be marked for cascaded + * validation, {@code false} otherwise. + */ + public boolean isAllowMultipleCascadedValidationOnReturnValues() { + return this.allowMultipleCascadedValidationOnReturnValues; + } + + /** + * @return {@code true} if constraints on methods in parallel class hierarchy are allowed, {@code false} otherwise. + */ + public boolean isAllowParallelMethodsDefineParameterConstraints() { + return this.allowParallelMethodsDefineParameterConstraints; + } + + /** + * Return an unmodifiable Set of MethodConfigurationRule that are to be + * enforced based on the configuration. + * + * @return a set of method configuration rules based on this configuration state + */ + public Set> getConfiguredRuleSet() { + HashSet> result = new HashSet>(); + + if ( !this.isAllowOverridingMethodAlterParameterConstraint() ) { + result.add( OverridingMethodMustNotAlterParameterConstraints.class ); + } + + if ( !this.isAllowParallelMethodsDefineParameterConstraints() ) { + result.add( ParallelMethodsMustNotDefineParameterConstraints.class ); + } + + result.add( VoidMethodsMustNotBeReturnValueConstrained.class ); + + if ( !this.isAllowMultipleCascadedValidationOnReturnValues() ) { + result.add( ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine.class ); + } + + result.add( ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue.class ); + + return Collections.unmodifiableSet( result ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java index c052041808..3d16a50b85 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorContextImpl.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.List; - import javax.validation.ConstraintValidatorFactory; import javax.validation.MessageInterpolator; import javax.validation.ParameterNameProvider; @@ -24,6 +23,7 @@ * @author Hardy Ferentschik * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Gunnar Morling + * @author Chris Beckey <cbeckey@paypal.com> */ public class ValidatorContextImpl implements HibernateValidatorContext { @@ -36,6 +36,8 @@ public class ValidatorContextImpl implements HibernateValidatorContext { private boolean failFast; private final List> validatedValueHandlers; private TimeProvider timeProvider; + private MethodValidationConfiguration methodValidationConfiguration = new MethodValidationConfiguration(); + public ValidatorContextImpl(ValidatorFactoryImpl validatorFactory) { this.validatorFactory = validatorFactory; @@ -117,6 +119,24 @@ public HibernateValidatorContext timeProvider(TimeProvider timeProvider) { return this; } + @Override + public HibernateValidatorContext allowOverridingMethodAlterParameterConstraint(boolean allow) { + this.methodValidationConfiguration.allowOverridingMethodAlterParameterConstraint( allow ); + return this; + } + + @Override + public HibernateValidatorContext allowMultipleCascadedValidationOnReturnValues(boolean allow) { + this.methodValidationConfiguration.allowMultipleCascadedValidationOnReturnValues( allow ); + return this; + } + + @Override + public HibernateValidatorContext allowParallelMethodsDefineParameterConstraints(boolean allow) { + this.methodValidationConfiguration.allowParallelMethodsDefineParameterConstraints( allow ); + return this; + } + @Override public Validator getValidator() { return validatorFactory.createValidator( @@ -126,7 +146,8 @@ public Validator getValidator() { parameterNameProvider, failFast, validatedValueHandlers, - timeProvider + timeProvider, + methodValidationConfiguration ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index ff735c1cf8..f36ebab18f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -59,6 +59,7 @@ * @author Hardy Ferentschik * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Chris Beckey <cbeckey@paypal.com> */ public class ValidatorFactoryImpl implements HibernateValidatorFactory { @@ -117,6 +118,11 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { */ private final boolean failFast; + /** + * Hibernate validator specific flags to relax constraints on parameters. + */ + private final MethodValidationConfiguration methodValidationConfiguration; + /** * Metadata provider for XML configuration. */ @@ -162,6 +168,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { Map properties = configurationState.getProperties(); boolean tmpFailFast = false; + boolean tmpAllowOverridingMethodAlterParameterConstraint = false; + boolean tmpAllowMultipleCascadedValidationOnReturnValues = false; + boolean tmpAllowParallelMethodsDefineParameterConstraints = false; + List> tmpValidatedValueHandlers = newArrayList( 5 ); if ( configurationState instanceof ConfigurationImpl ) { @@ -170,20 +180,71 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { // check whether fail fast is programmatically enabled tmpFailFast = hibernateSpecificConfig.getFailFast(); + tmpAllowOverridingMethodAlterParameterConstraint = + hibernateSpecificConfig.getMethodValidationConfiguration() + .isAllowOverridingMethodAlterParameterConstraint(); + tmpAllowMultipleCascadedValidationOnReturnValues = + hibernateSpecificConfig.getMethodValidationConfiguration() + .isAllowMultipleCascadedValidationOnReturnValues(); + tmpAllowParallelMethodsDefineParameterConstraints = + hibernateSpecificConfig.getMethodValidationConfiguration() + .isAllowParallelMethodsDefineParameterConstraints(); + tmpValidatedValueHandlers.addAll( hibernateSpecificConfig.getValidatedValueHandlers() ); - registerCustomConstraintValidators( hibernateSpecificConfig, properties, externalClassLoader, constraintHelper ); + registerCustomConstraintValidators( + hibernateSpecificConfig, + properties, + externalClassLoader, constraintHelper + ); } - this.constraintMappings = Collections.unmodifiableSet( getConstraintMappings( configurationState, externalClassLoader ) ); + this.constraintMappings = Collections.unmodifiableSet( + getConstraintMappings( + configurationState, + externalClassLoader + ) + ); - tmpFailFast = checkPropertiesForFailFast( - properties, tmpFailFast + tmpValidatedValueHandlers.addAll( + getPropertyConfiguredValidatedValueHandlers( + properties, + externalClassLoader + ) ); + this.validatedValueHandlers = Collections.unmodifiableList( tmpValidatedValueHandlers ); + + tmpFailFast = checkPropertiesForBoolean( properties, HibernateValidatorConfiguration.FAIL_FAST, tmpFailFast ); this.failFast = tmpFailFast; - tmpValidatedValueHandlers.addAll( getPropertyConfiguredValidatedValueHandlers( properties, externalClassLoader ) ); - this.validatedValueHandlers = Collections.unmodifiableList( tmpValidatedValueHandlers ); + this.methodValidationConfiguration = new MethodValidationConfiguration(); + + tmpAllowOverridingMethodAlterParameterConstraint = checkPropertiesForBoolean( + properties, + HibernateValidatorConfiguration.ALLOW_PARAMETER_CONSTRAINT_OVERRIDE, + tmpAllowOverridingMethodAlterParameterConstraint + ); + this.methodValidationConfiguration.allowOverridingMethodAlterParameterConstraint( + tmpAllowOverridingMethodAlterParameterConstraint + ); + + tmpAllowMultipleCascadedValidationOnReturnValues = checkPropertiesForBoolean( + properties, + HibernateValidatorConfiguration.ALLOW_MULTIPLE_CASCADED_VALIDATION_ON_RESULT, + tmpAllowMultipleCascadedValidationOnReturnValues + ); + this.methodValidationConfiguration.allowMultipleCascadedValidationOnReturnValues( + tmpAllowMultipleCascadedValidationOnReturnValues + ); + + tmpAllowParallelMethodsDefineParameterConstraints = checkPropertiesForBoolean( + properties, + HibernateValidatorConfiguration.ALLOW_PARALLEL_METHODS_DEFINE_PARAMETER_CONSTRAINTS, + tmpAllowParallelMethodsDefineParameterConstraints + ); + this.methodValidationConfiguration.allowParallelMethodsDefineParameterConstraints( + tmpAllowParallelMethodsDefineParameterConstraints + ); this.constraintValidatorManager = new ConstraintValidatorManager( configurationState.getConstraintValidatorFactory() ); } @@ -240,12 +301,15 @@ private static TimeProvider getTimeProvider(ConfigurationState configurationStat // XML config if ( timeProvider == null ) { - String timeProviderClassName = configurationState.getProperties().get( HibernateValidatorConfiguration.TIME_PROVIDER ); + String timeProviderClassName = configurationState.getProperties() + .get( HibernateValidatorConfiguration.TIME_PROVIDER ); if ( timeProviderClassName != null ) { @SuppressWarnings("unchecked") - Class handlerType = (Class) run( LoadClass - .action( timeProviderClassName, externalClassLoader ) ); + Class handlerType = (Class) run( + LoadClass + .action( timeProviderClassName, externalClassLoader ) + ); timeProvider = run( NewInstance.action( handlerType, "time provider class" ) ); } } @@ -262,7 +326,8 @@ public Validator getValidator() { parameterNameProvider, failFast, validatedValueHandlers, - timeProvider + timeProvider, + methodValidationConfiguration ); } @@ -298,6 +363,10 @@ TimeProvider getTimeProvider() { return timeProvider; } + public MethodValidationConfiguration getMethodValidationConfiguration() { + return this.methodValidationConfiguration; + } + @Override public T unwrap(Class type) { //allow unwrapping into public super types @@ -329,11 +398,12 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, ParameterNameProvider parameterNameProvider, boolean failFast, List> validatedValueHandlers, - TimeProvider timeProvider) { + TimeProvider timeProvider, + MethodValidationConfiguration methodValidationConfiguration) { // HV-793 - To fail eagerly in case we have no EL dependencies on the classpath we try to load the expression // factory - if( messageInterpolator instanceof ResourceBundleMessageInterpolator ) { + if ( messageInterpolator instanceof ResourceBundleMessageInterpolator ) { if ( missingElDependencies == null ) { try { run( @@ -353,14 +423,14 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, throw log.getMissingELDependenciesException(); } } - BeanMetaDataManager beanMetaDataManager; if ( !beanMetaDataManagerMap.containsKey( parameterNameProvider ) ) { beanMetaDataManager = new BeanMetaDataManager( constraintHelper, executableHelper, parameterNameProvider, - buildDataProviders( parameterNameProvider ) + buildDataProviders( parameterNameProvider ), + methodValidationConfiguration ); beanMetaDataManagerMap.put( parameterNameProvider, beanMetaDataManager ); } @@ -400,17 +470,18 @@ private List buildDataProviders(ParameterNameProvider paramete return metaDataProviders; } - private boolean checkPropertiesForFailFast(Map properties, boolean programmaticConfiguredFailFast) { - boolean failFast = programmaticConfiguredFailFast; - String failFastPropValue = properties.get( HibernateValidatorConfiguration.FAIL_FAST ); - if ( failFastPropValue != null ) { - boolean tmpFailFast = Boolean.valueOf( failFastPropValue ); - if ( programmaticConfiguredFailFast && !tmpFailFast ) { + private boolean checkPropertiesForBoolean(Map properties, String propertyKey, boolean programmaticValue) { + boolean value = programmaticValue; + String propertyStringValue = properties.get( propertyKey ); + if ( propertyStringValue != null ) { + boolean configurationValue = Boolean.valueOf( propertyStringValue ); + // throw an exception if the programmatic value is true and it overrides a false configured value + if ( programmaticValue && !configurationValue ) { throw log.getInconsistentFailFastConfigurationException(); } - failFast = tmpFailFast; + value = configurationValue; } - return failFast; + return value; } /** @@ -518,7 +589,8 @@ private static T run(PrivilegedAction action) { /** * The one and only {@link ConstraintMappingContributor.ConstraintMappingBuilder} implementation. */ - private static class DefaultConstraintMappingBuilder implements ConstraintMappingContributor.ConstraintMappingBuilder { + private static class DefaultConstraintMappingBuilder + implements ConstraintMappingContributor.ConstraintMappingBuilder { private final Set mappings = newHashSet(); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 8b55b30401..957f3113c6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -174,7 +174,7 @@ public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory, this.validatedValueHandlers = validatedValueHandlers; this.constraintValidatorManager = constraintValidatorManager; this.failFast = failFast; - + validationOrderGenerator = new ValidationOrderGenerator(); this.accessibleMembers = new ConcurrentReferenceHashMap( @@ -331,6 +331,7 @@ public final T unwrap(Class type) { if ( type.isAssignableFrom( Validator.class ) ) { return type.cast( this ); } + throw log.getTypeNotSupportedForUnwrappingException( type ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java index 5caffbe21f..723a44f3b7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java @@ -6,10 +6,13 @@ */ package org.hibernate.validator.internal.metadata; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import javax.validation.ParameterNameProvider; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl.BeanMetaDataBuilder; @@ -18,8 +21,8 @@ import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; -import org.hibernate.validator.internal.metadata.provider.TypeAnnotationAwareMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.metadata.provider.TypeAnnotationAwareMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap; import org.hibernate.validator.internal.util.Contracts; @@ -47,7 +50,8 @@ *

* * @author Gunnar Morling - */ + * @author Chris Beckey <cbeckey@paypal.com> +*/ public class BeanMetaDataManager { /** * The default initial capacity for this cache. @@ -85,6 +89,24 @@ public class BeanMetaDataManager { */ private final ExecutableHelper executableHelper; + /** + * the three properties in this field affect the invocation of rules associated to section 4.5.5 + * of the V1.1 specification. By default they are all false, if true they allow + * for relaxation of the Liskov Substitution Principal. + */ + private final MethodValidationConfiguration methodValidationConfiguration; + + /** + * Creates a new {@code BeanMetaDataManager}. {@link DefaultParameterNameProvider} is used as parameter name + * provider, no meta data providers besides the annotation-based providers are used. + * + * @param constraintHelper the constraint helper + * @param executableHelper the executable helper + */ + public BeanMetaDataManager(ConstraintHelper constraintHelper, ExecutableHelper executableHelper) { + this( constraintHelper, executableHelper, new DefaultParameterNameProvider(), Collections.emptyList() ); + } + /** * Creates a new {@code BeanMetaDataManager}. * @@ -94,14 +116,28 @@ public class BeanMetaDataManager { * @param optionalMetaDataProviders optional meta data provider used on top of the annotation based provider */ public BeanMetaDataManager(ConstraintHelper constraintHelper, - ExecutableHelper executableHelper, - ParameterNameProvider parameterNameProvider, - List optionalMetaDataProviders) { + ExecutableHelper executableHelper, + ParameterNameProvider parameterNameProvider, + List optionalMetaDataProviders) { + this( + constraintHelper, executableHelper, + parameterNameProvider, optionalMetaDataProviders, + new MethodValidationConfiguration() + ); + } + + public BeanMetaDataManager(ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + ParameterNameProvider parameterNameProvider, + List optionalMetaDataProviders, + MethodValidationConfiguration methodValidationConfiguration) { this.constraintHelper = constraintHelper; this.metaDataProviders = newArrayList(); this.metaDataProviders.addAll( optionalMetaDataProviders ); this.executableHelper = executableHelper; + this.methodValidationConfiguration = methodValidationConfiguration; + this.beanMetaDataCache = new ConcurrentReferenceHashMap, BeanMetaData>( DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, @@ -111,9 +147,8 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, EnumSet.of( IDENTITY_COMPARISONS ) ); - AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders(); - AnnotationMetaDataProvider defaultProvider = null; + AnnotationMetaDataProvider defaultProvider; if ( Version.getJavaRelease() >= 8 ) { defaultProvider = new TypeAnnotationAwareMetaDataProvider( constraintHelper, @@ -157,7 +192,8 @@ public int numberOfCachedBeanMetaDataInstances() { * @return A bean meta data object for the given type. */ private BeanMetaDataImpl createBeanMetaData(Class clazz) { - BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( constraintHelper, executableHelper, clazz ); + BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( + constraintHelper, executableHelper, clazz, methodValidationConfiguration); for ( MetaDataProvider provider : metaDataProviders ) { for ( BeanConfiguration beanConfiguration : provider.getBeanConfigurationForHierarchy( clazz ) ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index c7c01d6571..e296548ee9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; - import javax.validation.ElementKind; import javax.validation.groups.Default; import javax.validation.metadata.BeanDescriptor; @@ -24,6 +23,7 @@ import javax.validation.metadata.MethodType; import javax.validation.metadata.PropertyDescriptor; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.BeanDescriptorImpl; @@ -58,6 +58,7 @@ * @author Hardy Ferentschik * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Chris Beckey <cbeckey@paypal.com> */ public final class BeanMetaDataImpl implements BeanMetaData { @@ -438,14 +439,11 @@ public ElementType getPartition(MetaConstraint constraint) { @Override public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append( "BeanMetaDataImpl" ); - sb.append( "{beanClass=" ).append( beanClass.getSimpleName() ); - sb.append( ", constraintCount=" ).append( getMetaConstraints().size() ); - sb.append( ", cascadedPropertiesCount=" ).append( cascadedProperties.size() ); - sb.append( ", defaultGroupSequence=" ).append( getDefaultGroupSequence( null ) ); - sb.append( '}' ); - return sb.toString(); + return "BeanMetaDataImpl" + + "{beanClass=" + beanClass.getSimpleName() + + ", constraintCount=" + getMetaConstraints().size() + + ", cascadedPropertiesCount=" + cascadedProperties.size() + + ", defaultGroupSequence=" + getDefaultGroupSequence( null ) + '}'; } public static class BeanMetaDataBuilder { @@ -466,14 +464,29 @@ public static class BeanMetaDataBuilder { private DefaultGroupSequenceProvider defaultGroupSequenceProvider; - private BeanMetaDataBuilder(ConstraintHelper constraintHelper, ExecutableHelper executableHelper, Class beanClass) { + private final MethodValidationConfiguration methodValidationConfiguration; + + private BeanMetaDataBuilder( + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + Class beanClass, + MethodValidationConfiguration methodValidationConfiguration) { this.beanClass = beanClass; this.constraintHelper = constraintHelper; this.executableHelper = executableHelper; + this.methodValidationConfiguration = methodValidationConfiguration; } - public static BeanMetaDataBuilder getInstance(ConstraintHelper constraintHelper, ExecutableHelper executableHelper, Class beanClass) { - return new BeanMetaDataBuilder( constraintHelper, executableHelper, beanClass ); + public static BeanMetaDataBuilder getInstance( + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + Class beanClass, + MethodValidationConfiguration methodValidationConfiguration) { + return new BeanMetaDataBuilder( + constraintHelper, + executableHelper, + beanClass, + methodValidationConfiguration); } public void add(BeanConfiguration configuration) { @@ -514,7 +527,8 @@ private void addMetaDataToBuilder(ConstrainedElement constrainableElement, Set beanClass, ConstrainedElement constrainedElement, ConstraintHelper constraintHelper, ExecutableHelper executableHelper) { + private final MethodValidationConfiguration methodValidationConfiguration; + + + public BuilderDelegate( + Class beanClass, + ConstrainedElement constrainedElement, + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + MethodValidationConfiguration methodValidationConfiguration + ) { this.beanClass = beanClass; this.constraintHelper = constraintHelper; this.executableHelper = executableHelper; + this.methodValidationConfiguration = methodValidationConfiguration; switch ( constrainedElement.getKind() ) { case FIELD: @@ -568,7 +591,8 @@ public BuilderDelegate(Class beanClass, ConstrainedElement constrainedElement beanClass, constrainedExecutable, constraintHelper, - executableHelper + executableHelper, + methodValidationConfiguration ); } @@ -608,7 +632,8 @@ public boolean add(ConstrainedElement constrainedElement) { beanClass, constrainedMethod, constraintHelper, - executableHelper + executableHelper, + methodValidationConfiguration ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java index fd28dc97b2..6c3d82f416 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java @@ -7,23 +7,21 @@ package org.hibernate.validator.internal.metadata.aggregated; import java.lang.reflect.Type; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; - import javax.validation.ElementKind; import javax.validation.metadata.ParameterDescriptor; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.valuehandling.UnwrapMode; import org.hibernate.validator.internal.metadata.aggregated.rule.MethodConfigurationRule; -import org.hibernate.validator.internal.metadata.aggregated.rule.OverridingMethodMustNotAlterParameterConstraints; -import org.hibernate.validator.internal.metadata.aggregated.rule.ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue; -import org.hibernate.validator.internal.metadata.aggregated.rule.ParallelMethodsMustNotDefineParameterConstraints; -import org.hibernate.validator.internal.metadata.aggregated.rule.ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine; -import org.hibernate.validator.internal.metadata.aggregated.rule.VoidMethodsMustNotBeReturnValueConstrained; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl; @@ -31,11 +29,11 @@ import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; import org.hibernate.validator.internal.metadata.raw.ExecutableElement; -import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; @@ -157,8 +155,8 @@ public Set getSignatures() { * method or constructor. * * @return the cross-parameter constraints declared for the represented - * method or constructor. May be empty but will never be - * {@code null}. + * method or constructor. May be empty but will never be + * {@code null}. */ public Set> getCrossParameterConstraints() { return crossParameterConstraints; @@ -263,20 +261,6 @@ public boolean equals(Object obj) { * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI */ public static class Builder extends MetaDataBuilder { - - /** - * The rules applying for the definition of executable constraints. - */ - private static final Set rules = Collections.unmodifiableSet( - CollectionHelper.asSet( - new OverridingMethodMustNotAlterParameterConstraints(), - new ParallelMethodsMustNotDefineParameterConstraints(), - new VoidMethodsMustNotBeReturnValueConstrained(), - new ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine(), - new ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue() - ) - ); - private final Set signatures = newHashSet(); /** @@ -287,7 +271,9 @@ public static class Builder extends MetaDataBuilder { private ExecutableElement executable; private final Set> crossParameterConstraints = newHashSet(); private final Set> typeArgumentsConstraints = newHashSet(); + private final Set rules; private boolean isConstrained = false; + private final MethodValidationConfiguration methodValidationConfiguration; /** * Holds a merged representation of the configurations for one method @@ -307,14 +293,33 @@ public static class Builder extends MetaDataBuilder { * @param constraintHelper the constraint helper * @param executableHelper the executable helper * @param beanClass the bean class + * @param methodValidationConfiguration configuration instance for method validation behaviour */ - public Builder(Class beanClass, ConstrainedExecutable constrainedExecutable, ConstraintHelper constraintHelper, ExecutableHelper executableHelper) { + public Builder( + Class beanClass, + ConstrainedExecutable constrainedExecutable, + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + MethodValidationConfiguration methodValidationConfiguration) { super( beanClass, constraintHelper ); this.executableHelper = executableHelper; kind = constrainedExecutable.getKind(); executable = constrainedExecutable.getExecutable(); add( constrainedExecutable ); + this.methodValidationConfiguration = methodValidationConfiguration; + + // Build the rules that will be enforced through the metadata created here + this.rules = new HashSet(); + for ( Class ruleClass : this.methodValidationConfiguration.getConfiguredRuleSet() ) { + MethodConfigurationRule rule = run( + NewInstance.action( + ruleClass, + "Method configuration rule" + ) + ); + this.rules.add( rule ); + } } @Override @@ -348,7 +353,10 @@ public final void add(ConstrainedElement constrainedElement) { // keep the "lowest" executable in hierarchy to make sure any type parameters declared on super-types (and // used in overridden methods) are resolved for the specific sub-type we are interested in - if ( executable != null && executableHelper.overrides( constrainedExecutable.getExecutable(), executable ) ) { + if ( executable != null && executableHelper.overrides( + constrainedExecutable.getExecutable(), + executable + ) ) { executable = constrainedExecutable.getExecutable(); } } @@ -462,5 +470,15 @@ private void assertCorrectnessOfConfiguration() { } } } + + /** + * Runs the given privileged action, using a privileged block if required. + *

+ * NOTE: This must never be changed into a publicly available method to avoid execution of arbitrary + * privileged actions within HV's protection domain. + */ + private T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java index 57c18f61f5..e360024bda 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/ConstrainedExecutable.java @@ -257,8 +257,8 @@ public boolean isEquallyParameterConstrained(ConstrainedExecutable other) { int i = 0; for ( ConstrainedParameter parameter : parameterMetaData ) { ConstrainedParameter otherParameter = other.getParameterMetaData( i ); - if ( parameter.isCascading != otherParameter.isCascading || !getDescriptors( parameter.getConstraints() ) - .equals( getDescriptors( otherParameter.getConstraints() ) ) ) { + if ( parameter.isCascading != otherParameter.isCascading + || !getDescriptors( parameter.getConstraints() ).equals( getDescriptors( otherParameter.getConstraints() ) ) ) { return false; } i++; diff --git a/engine/src/test/java/org/hibernate/validator/cfgtest/ConfigurationFilePropertiesTest.java b/engine/src/test/java/org/hibernate/validator/cfgtest/ConfigurationFilePropertiesTest.java new file mode 100644 index 0000000000..d76098624d --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/cfgtest/ConfigurationFilePropertiesTest.java @@ -0,0 +1,144 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.cfgtest; + +import java.lang.reflect.Field; +import java.net.URL; +import javax.validation.Configuration; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.bootstrap.GenericBootstrap; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.ValidatorImpl; +import org.hibernate.validator.internal.metadata.BeanMetaDataManager; + +public class ConfigurationFilePropertiesTest { + + @Test + public void testConfigurationAvailable() { + URL validationUrl = Thread.currentThread().getContextClassLoader().getResource( "META-INF/validation.xml" ); + Assert.assertNotNull( validationUrl, "unable to find 'META-INF/validation.xml' on classpath" ); + } + + /** + * The following test assumes that the file META-INF/validation.xml is present and + * contains: + * true + * true + * true + * true + * + * The Maven build runs this test in a separate execution of surefire, which adds the + * path to the required file onto its classpath. + */ + @Test + public void testAllowMultipleCascadedValidationOnReturnValues() { + GenericBootstrap provider = Validation.byDefaultProvider(); + Assert.assertNotNull( provider ); + + Configuration config = provider.configure(); + Assert.assertNotNull( config ); + Assert.assertTrue( config instanceof HibernateValidatorConfiguration ); + + HibernateValidatorConfiguration hibernateConfig = (HibernateValidatorConfiguration) config; + + // Note that the configuration from the XML is not read until the + // buildValidatorFactory() method is called. + ValidatorFactory factory = hibernateConfig.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; + BeanMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, BeanMetaDataManager.class ); + MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); + + Assert.assertTrue( methodConfig.isAllowMultipleCascadedValidationOnReturnValues() ); + } + + @Test + public void testAllowOverridingMethodAlterParameterConstraint() { + GenericBootstrap provider = Validation.byDefaultProvider(); + Assert.assertNotNull( provider ); + + Configuration config = provider.configure(); + Assert.assertNotNull( config ); + Assert.assertTrue( config instanceof HibernateValidatorConfiguration ); + + HibernateValidatorConfiguration hibernateConfig = (HibernateValidatorConfiguration) config; + + // Note that the configuration from the XML is not read until the + // buildValidatorFactory() method is called. + ValidatorFactory factory = hibernateConfig.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; + BeanMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, BeanMetaDataManager.class ); + MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); + + Assert.assertTrue( methodConfig.isAllowOverridingMethodAlterParameterConstraint() ); + } + + @Test + public void testAllowParallelMethodsDefineParameterConstraints() { + GenericBootstrap provider = Validation.byDefaultProvider(); + Assert.assertNotNull( provider ); + + Configuration config = provider.configure(); + Assert.assertNotNull( config ); + Assert.assertTrue( config instanceof HibernateValidatorConfiguration ); + + HibernateValidatorConfiguration hibernateConfig = (HibernateValidatorConfiguration) config; + + // Note that the configuration from the XML is not read until the + // buildValidatorFactory() method is called. + ValidatorFactory factory = hibernateConfig.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + ValidatorImpl hibernateValidatorImpl = (ValidatorImpl) validator; + BeanMetaDataManager bmdm = findPropertyOfType( hibernateValidatorImpl, BeanMetaDataManager.class ); + MethodValidationConfiguration methodConfig = findPropertyOfType( bmdm, MethodValidationConfiguration.class ); + + Assert.assertTrue( methodConfig.isAllowParallelMethodsDefineParameterConstraints() ); + } + + /** + * Reflect into the subject and find the first property of the given type. + * + * @param subject - the instance to reflect on + * @param clazz - exactly the class to match on + * + * @return + */ + private T findPropertyOfType(Object subject, Class clazz) { + Field[] fields = subject.getClass().getDeclaredFields(); + for ( Field field : fields ) { + if ( field.getType().equals( clazz ) ) { + boolean accessible = field.isAccessible(); + try { + field.setAccessible( true ); + return (T) field.get( subject ); + } + catch ( IllegalArgumentException e ) { + e.printStackTrace(); + } + catch ( IllegalAccessException e ) { + e.printStackTrace(); + } + finally { + field.setAccessible( accessible ); + } + } + } + return null; + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/HibernateValidatorConfigurationTest.java b/engine/src/test/java/org/hibernate/validator/test/HibernateValidatorConfigurationTest.java index 506b5a1762..1afd9a6f58 100644 --- a/engine/src/test/java/org/hibernate/validator/test/HibernateValidatorConfigurationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/HibernateValidatorConfigurationTest.java @@ -6,14 +6,14 @@ */ package org.hibernate.validator.test; -import javax.validation.Validation; - +import org.testng.Assert; import org.testng.annotations.Test; -import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.internal.engine.ConfigurationImpl; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; +import static org.hibernate.validator.testutil.ValidatorUtil.getConfiguration; import static org.testng.Assert.assertNotNull; /** @@ -25,9 +25,17 @@ public class HibernateValidatorConfigurationTest { @Test public void defaultResourceBundleLocatorCanBeRetrieved() { - HibernateValidatorConfiguration configure = Validation.byProvider( HibernateValidator.class ).configure(); + HibernateValidatorConfiguration configure = getConfiguration(); ResourceBundleLocator defaultResourceBundleLocator = configure.getDefaultResourceBundleLocator(); assertNotNull( defaultResourceBundleLocator ); } + + @Test + public void relaxationPropertiesAreProperDefault() { + ConfigurationImpl configuration = (ConfigurationImpl) getConfiguration(); + Assert.assertFalse( configuration.isAllowOverridingMethodAlterParameterConstraint() ); + Assert.assertFalse( configuration.isAllowMultipleCascadedValidationOnReturnValues() ); + Assert.assertFalse( configuration.isAllowParallelMethodsDefineParameterConstraints() ); + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java index e91241eadb..ba63546b6f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java @@ -31,7 +31,7 @@ public class IllegalMethodParameterConstraintsTest { @Test(expectedExceptions = IllegalArgumentException.class) public void testNullParameterArrayThrowsException() { getValidator().forExecutables().validateParameters( - new FooImpl(), FooImpl.class.getDeclaredMethods()[0], new Object[] { }, (Class) null + new FooImpl(), FooImpl.class.getDeclaredMethods()[0], new Object[] {}, (Class) null ); } @@ -45,28 +45,28 @@ public void testNullGroupsVarargThrowsException() { @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000151.*") public void parameterConstraintsAddedInSubTypeCausesDeclarationException() { getValidator().forExecutables().validateParameters( - new FooImpl(), FooImpl.class.getDeclaredMethods()[0], new Object[] { } + new FooImpl(), FooImpl.class.getDeclaredMethods()[0], new Object[] {} ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000151.*") public void atValidAddedInSubTypeCausesDeclarationException() { getValidator().forExecutables().validateParameters( - new ZapImpl(), ZapImpl.class.getDeclaredMethods()[0], new Object[] { } + new ZapImpl(), ZapImpl.class.getDeclaredMethods()[0], new Object[] {} ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000151.*") public void constraintStrengtheningInSubTypeCausesDeclarationException() { getValidator().forExecutables().validateParameters( - new BarImpl(), BarImpl.class.getDeclaredMethods()[0], new Object[] { } + new BarImpl(), BarImpl.class.getDeclaredMethods()[0], new Object[] {} ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000152.*") public void parameterConstraintsInHierarchyWithMultipleRootMethodsCausesDeclarationException() { getValidator().forExecutables().validateParameters( - new BazImpl(), BazImpl.class.getDeclaredMethods()[0], new Object[] { } + new BazImpl(), BazImpl.class.getDeclaredMethods()[0], new Object[] {} ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/RelaxedMethodParameterConstraintsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/RelaxedMethodParameterConstraintsTest.java new file mode 100644 index 0000000000..012dcaf51b --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/RelaxedMethodParameterConstraintsTest.java @@ -0,0 +1,224 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation; + +import java.util.Set; +import javax.validation.ConstraintDeclarationException; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.testng.annotations.Test; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.testutil.ConstraintViolationAssert; + +/** + * Integration test for {@link org.hibernate.validator.internal.engine.ValidatorImpl} which tests that illegal method parameter constraints are properly allowed + * when relaxed constraint properties are in effect. + * + * @author Chris Beckey <cbeckey@paypal.com> + */ +public class RelaxedMethodParameterConstraintsTest { + + /** + * The converse of disallowParameterConstraintsAddedInSubType, + * relaxes constraint. + */ + @Test + public void allowParameterConstraintsAddedInSubType() { + HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ).configure(); + + configuration.allowOverridingMethodAlterParameterConstraint( true ); + + ValidatorFactory factory = configuration.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + Set> violations = validator.forExecutables().validateParameters( + new RealizationWithMethodParameterConstraint(), + RealizationWithMethodParameterConstraint.class.getDeclaredMethods()[0], + new Object[] { "foo" } + ); + + ConstraintViolationAssert.assertNumberOfViolations( violations, 0 ); + + configuration.allowOverridingMethodAlterParameterConstraint( false ); + } + + @Test(expectedExceptions = { ConstraintDeclarationException.class }) + public void disallowStrengtheningInSubType() { + HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ).configure(); + + ValidatorFactory factory = configuration.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + @SuppressWarnings("unused") + Set> violations = validator.forExecutables() + .validateParameters( + new RealizationWithAdditionalMethodParameterConstraint(), + RealizationWithAdditionalMethodParameterConstraint.class.getDeclaredMethods()[0], + new Object[] {} + ); + } + + @Test + public void allowStrengtheningInSubType() { + HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ).configure(); + + configuration.allowOverridingMethodAlterParameterConstraint( true ); + + ValidatorFactory factory = configuration.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + Set> violations = + validator.forExecutables().validateParameters( + new RealizationWithAdditionalMethodParameterConstraint(), + RealizationWithAdditionalMethodParameterConstraint.class.getDeclaredMethods()[0], + new Object[] { "foo" } + ); + + ConstraintViolationAssert.assertNumberOfViolations( violations, 0 ); + + configuration.allowOverridingMethodAlterParameterConstraint( false ); + } + + @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000131.*") + public void disallowValidAddedInSubType() { + HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ).configure(); + + ValidatorFactory factory = configuration.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + validator.forExecutables().validateParameters( + new SubRealizationWithValidConstraintOnMethodParameter(), + SubRealizationWithValidConstraintOnMethodParameter.class.getDeclaredMethods()[0], + new Object[] {} + ); + } + + @Test + public void allowValidAddedInSubType() { + HibernateValidatorConfiguration configure = Validation.byProvider( HibernateValidator.class ).configure(); + + configure.allowMultipleCascadedValidationOnReturnValues( true ); + + ValidatorFactory factory = configure.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + validator.forExecutables().validateParameters( + new SubRealizationWithValidConstraintOnMethodParameter(), + SubRealizationWithValidConstraintOnMethodParameter.class.getDeclaredMethods()[0], + new Object[] { "foo" } + ); + + configure.allowMultipleCascadedValidationOnReturnValues( false ); + } + + @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000152.*") + public void disallowParameterConstraintsInHierarchyWithMultipleRootMethods() { + HibernateValidatorConfiguration configure = Validation.byProvider( HibernateValidator.class ).configure(); + + ValidatorFactory factory = configure.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + validator.forExecutables().validateParameters( + new RealizationOfTwoInterface(), + RealizationOfTwoInterface.class.getDeclaredMethods()[0], + new Object[] {} + ); + } + + @Test + public void allowParameterConstraintsInHierarchyWithMultipleRootMethods() { + HibernateValidatorConfiguration configure = Validation.byProvider( HibernateValidator.class ).configure(); + + configure.allowParallelMethodsDefineParameterConstraints( true ); + + ValidatorFactory factory = configure.buildValidatorFactory(); + Validator validator = factory.getValidator(); + + validator.forExecutables().validateParameters( + new RealizationOfTwoInterface(), + RealizationOfTwoInterface.class.getDeclaredMethods()[0], + new Object[] { "foo" } + ); + + configure.allowParallelMethodsDefineParameterConstraints( false ); + } + + private interface InterfaceWithNoConstraints { + String foo(String s); + } + + private interface AnotherInterfaceWithMethodParameterConstraint { + String foo(@NotNull String s); + } + + private static class RealizationWithMethodParameterConstraint implements InterfaceWithNoConstraints { + /** + * Adds constraints to an un-constrained method from a super-type, which is not allowed. + */ + @Override + public String foo(@NotNull String s) { + return "Hello World"; + } + } + + private static class RealizationWithValidConstraintOnMethodParameter + implements InterfaceWithNoConstraints { + /** + * Adds @Valid to an un-constrained method from a super-type, which is not allowed. + */ + @Override + @Valid + public String foo(String s) { + return "Hello Valid World"; + } + } + + private static class SubRealizationWithValidConstraintOnMethodParameter + extends RealizationWithValidConstraintOnMethodParameter { + /** + * Adds @Valid to an un-constrained method from a super-type, which is not allowed. + */ + @Override + @Valid + public String foo(String s) { + return "Hello Valid World"; + } + } + + private static class RealizationOfTwoInterface + implements InterfaceWithNoConstraints, AnotherInterfaceWithMethodParameterConstraint { + /** + * Implement a method that is declared by two interfaces, one of which has a constraint + */ + @Override + public String foo(String s) { + return "Hello World"; + } + } + + private interface InterfaceWithNotNullMethodParameterConstraint { + void bar(@NotNull String s); + } + + private static class RealizationWithAdditionalMethodParameterConstraint + implements InterfaceWithNotNullMethodParameterConstraint { + /** + * Adds constraints to a constrained method from a super-type, which is not allowed. + */ + @Override + public void bar(@Size(min = 3) String s) { + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ConcreteClass.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ConcreteClass.java new file mode 100644 index 0000000000..deb8a4a7a7 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ConcreteClass.java @@ -0,0 +1,22 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation.subject; + +/** + * @author cbeckey@paypal.com + */ +public class ConcreteClass +implements Interface{ + + /** + * + */ + public String doSomething(T vo) { + return "class-" + vo.getName(); + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/Interface.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/Interface.java new file mode 100644 index 0000000000..ab49508ac4 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/Interface.java @@ -0,0 +1,16 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation.subject; + +/** + * @author cbeckey@paypal.com + */ +public interface Interface { + + String doSomething(T vo); + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/SubClass.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/SubClass.java new file mode 100644 index 0000000000..bdf661c598 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/SubClass.java @@ -0,0 +1,23 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation.subject; + +/** + * @author cbeckey@paypal.com + */ +public class SubClass +extends ConcreteClass { + + /** + * + */ + @Override + public String doSomething(ValueObjectSubClass vo) { + return "subclass-" + vo.getName(); + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObject.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObject.java new file mode 100644 index 0000000000..e3e8813f60 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObject.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation.subject; + +/** + * @author cbeckey@paypal.com + */ +public class ValueObject { + private String name; + + public ValueObject(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObjectSubClass.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObjectSubClass.java new file mode 100644 index 0000000000..860325d8f2 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/subject/ValueObjectSubClass.java @@ -0,0 +1,29 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation.subject; + +/** + * @author cbeckey@paypal.com + */ +public class ValueObjectSubClass + extends ValueObject { + private Integer age; + + public ValueObjectSubClass(String name, Integer age) { + super( name ); + this.age = age; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + +} From eaa9be2e15def03bd98f80367bdf6f3c29c5f9c1 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Sun, 29 Nov 2015 18:01:34 +0100 Subject: [PATCH 021/189] HV-872 Documenting ability to relax method validation preconditions --- documentation/src/main/asciidoc/ch03.asciidoc | 8 +++ documentation/src/main/asciidoc/ch11.asciidoc | 63 ++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/ch03.asciidoc b/documentation/src/main/asciidoc/ch03.asciidoc index dc659a04d3..143476fdb9 100644 --- a/documentation/src/main/asciidoc/ch03.asciidoc +++ b/documentation/src/main/asciidoc/ch03.asciidoc @@ -299,6 +299,7 @@ public class Garage { ---- ==== +[[section-method-constraints-inheritance-hierarchies]] ==== Method constraints in inheritance hierarchies When declaring method constraints in inheritance hierarchies, it is important to be aware of the @@ -450,6 +451,13 @@ return value of a constructor invocation only the constraints declared on the co apply, but never any constraints declared on supertype constructors. ==== +[TIP] +==== +Enforcement of these rules may be relaxed by setting the configuration parameters contained in +the `MethodValidationConfiguration` property of the `HibernateValidatorConfiguration` before creating +the `Validator` instance. See also <>. +==== + [[section-validating-executable-constraints]] === Validating method constraints diff --git a/documentation/src/main/asciidoc/ch11.asciidoc b/documentation/src/main/asciidoc/ch11.asciidoc index 7debbc7498..3b9d90e58e 100644 --- a/documentation/src/main/asciidoc/ch11.asciidoc +++ b/documentation/src/main/asciidoc/ch11.asciidoc @@ -111,7 +111,6 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/failfast/F Here the validated object actually fails to satisfy both the constraints declared on the `Car` class, yet the validation call yields only one `ConstraintViolation` since the fail fast mode is enabled. - [NOTE] ==== There is no guarantee in which order the constraints are evaluated, i.e. it is not deterministic @@ -123,6 +122,68 @@ required, a deterministic evaluation order can be enforced using group sequences Refer to <> to learn about the different ways of enabling the fail fast mode when bootstrapping a validator. +[[section-method-validation-prerequisite-relaxation]] +=== Relaxation of requirements for method validation in class hierarchies + +The Bean Validation specification defines a set of preconditions which apply when defining +constraints on methods within class hierarchies. These preconditions are defined in +http://beanvalidation.org/1.1/spec/#constraintdeclarationvalidationprocess-methodlevelconstraints-inheritance[section 4.5.5] +of the Bean Validation 1.1 specification. See also <> +in this guide. + +As per specification a Bean Validation provider is allowed to relax these preconditions. +With Hibernate Validator you can do this in one of two ways. + +First you can use the configuration properties _hibernate.validator.allow_parameter_constraint_override_, +_hibernate.validator.allow_multiple_cascaded_validation_on_result_ and +_hibernate.validator.allow_parallel_method_parameter_constraint_ in _validation.xml_. See example +<>. + + +[[example-relaxing-method-validation-xml]] +.Configuring method validation behaviour in class hierarchies via properties +==== +[source, XML] +---- + + + org.hibernate.validator.HibernateValidator + + true + true + true + +---- +==== + +Alternatively these settings can be applied during programmatic bootstrapping. + +[[example-relaxing-method-validation]] +.Configuring method validation behaviour in class hierarchies +==== +[source, JAVA] +---- +HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class).configure(); + +configuration.allowMultipleCascadedValidationOnReturnValues(true) + .allowOverridingMethodAlterParameterConstraint(true) + .allowParallelMethodsDefineParameterConstraints(true); +---- +==== + +By default, all of these properties are false, implementing the default behavior as defined in the +Bean Validation specification. + +[WARNING] +==== +Changing the default behaviour for method validation will result in non specification conform and non +portable application. Make sure to understand what you are doing and that your use case really +requires changes to the default behaviour. +==== + [[section-programmatic-api]] === Programmatic constraint declaration From 2336b176d0c01b76a24d24999a9c9e1902900bfc Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Wed, 23 Dec 2015 15:51:19 +0100 Subject: [PATCH 022/189] HV-872 Some clean-up; * Using plain instantiation over reflection for rules * Avoiding unneeded field * Consistent JavaDoc --- .../HibernateValidatorConfiguration.java | 15 ++++++++----- .../validator/HibernateValidatorContext.java | 15 ++++++++----- .../engine/MethodValidationConfiguration.java | 16 ++++++++------ .../internal/engine/ValidatorFactoryImpl.java | 5 +---- .../internal/engine/ValidatorImpl.java | 5 +++-- .../aggregated/ExecutableMetaData.java | 22 +++++-------------- 6 files changed, 38 insertions(+), 40 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index dd38358c91..29df15504d 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -213,8 +213,8 @@ public interface HibernateValidatorConfiguration extends Configuration + * See Section 4.5.5 of the JSR 349 specification, specifically *

 	 * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
 	 * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.
@@ -232,11 +232,14 @@ public interface HibernateValidatorConfiguration extends Configuration
+	 * See Section 4.5.5 of the JSR 349 specification, specifically
+	 * 
 	 * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy.
 	 * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations)
 	 * cannot mark the return value for cascaded validation if the return value has already been marked on the
 	 * overridden method of the super type or interface."
+	 * 
* * @param allow flag determining whether validation will allow multiple cascaded validation on return values. * @@ -249,12 +252,14 @@ public interface HibernateValidatorConfiguration extends Configuration + * See Section 4.5.5 of the JSR 349 specification, specifically + *
 	 * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy
 	 * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class),
 	 * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation.
 	 * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller."
+	 * 
* * @param allow flag determining whether validation will allow parameter constraints in parallel hierarchies * diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java index 13b3bd76b4..8e14141edc 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorContext.java @@ -84,8 +84,8 @@ public interface HibernateValidatorContext extends ValidatorContext { /** * Define whether overriding methods that override constraints should throw a {@code ConstraintDefinitionException}. * The default value is {@code false}, i.e. do not allow. - * - * See Section 4.5.5 of JSR-349 Specification, specifically + *

+ * See Section 4.5.5 of the JSR 349 specification, specifically *

 	 * "In sub types (be it sub classes/interfaces or interface implementations), no parameter constraints may
 	 * be declared on overridden or implemented methods, nor may parameters be marked for cascaded validation.
@@ -103,11 +103,14 @@ public interface HibernateValidatorContext extends ValidatorContext {
 	/**
 	 * Define whether more than one constraint on a return value may be marked for cascading validation are allowed.
 	 * The default value is {@code false}, i.e. do not allow.
-	 *
+	 * 

+ * See Section 4.5.5 of the JSR 349 specification, specifically + *

 	 * "One must not mark a method return value for cascaded validation more than once in a line of a class hierarchy.
 	 * In other words, overriding methods on sub types (be it sub classes/interfaces or interface implementations)
 	 * cannot mark the return value for cascaded validation if the return value has already been marked on the
 	 * overridden method of the super type or interface."
+	 * 
* * @param allow flag determining whether validation will allow multiple cascaded validation on return values. * @@ -120,12 +123,14 @@ public interface HibernateValidatorContext extends ValidatorContext { /** * Define whether parallel methods that define constraints should throw a {@code ConstraintDefinitionException}. The * default value is {@code false}, i.e. do not allow. - * - * See Section 4.5.5 of JSR-349 Specification, specifically + *

+ * See Section 4.5.5 of the JSR 349 specification, specifically + *

 	 * "If a sub type overrides/implements a method originally defined in several parallel types of the hierarchy
 	 * (e.g. two interfaces not extending each other, or a class and an interface not implemented by said class),
 	 * no parameter constraints may be declared for that method at all nor parameters be marked for cascaded validation.
 	 * This again is to avoid an unexpected strengthening of preconditions to be fulfilled by the caller."
+	 * 
* * @param allow flag determining whether validation will allow parameter constraints in parallel hierarchies * diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java b/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java index 03165aedd6..006784ed6c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/MethodValidationConfiguration.java @@ -17,6 +17,8 @@ import org.hibernate.validator.internal.metadata.aggregated.rule.ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine; import org.hibernate.validator.internal.metadata.aggregated.rule.VoidMethodsMustNotBeReturnValueConstrained; +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + /** * These properties modify the behavior of the {@code }Validator} with respect to the Bean Validation * specification section 4.5.5. In particular: @@ -124,24 +126,24 @@ public boolean isAllowParallelMethodsDefineParameterConstraints() { * * @return a set of method configuration rules based on this configuration state */ - public Set> getConfiguredRuleSet() { - HashSet> result = new HashSet>(); + public Set getConfiguredRuleSet() { + HashSet result = newHashSet(); if ( !this.isAllowOverridingMethodAlterParameterConstraint() ) { - result.add( OverridingMethodMustNotAlterParameterConstraints.class ); + result.add( new OverridingMethodMustNotAlterParameterConstraints() ); } if ( !this.isAllowParallelMethodsDefineParameterConstraints() ) { - result.add( ParallelMethodsMustNotDefineParameterConstraints.class ); + result.add( new ParallelMethodsMustNotDefineParameterConstraints() ); } - result.add( VoidMethodsMustNotBeReturnValueConstrained.class ); + result.add( new VoidMethodsMustNotBeReturnValueConstrained() ); if ( !this.isAllowMultipleCascadedValidationOnReturnValues() ) { - result.add( ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine.class ); + result.add( new ReturnValueMayOnlyBeMarkedOnceAsCascadedPerHierarchyLine() ); } - result.add( ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue.class ); + result.add( new ParallelMethodsMustNotDefineGroupConversionForCascadedReturnValue() ); return Collections.unmodifiableSet( result ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index f36ebab18f..067c8b1227 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + import javax.validation.ConstraintValidatorFactory; import javax.validation.MessageInterpolator; import javax.validation.ParameterNameProvider; @@ -363,10 +364,6 @@ TimeProvider getTimeProvider() { return timeProvider; } - public MethodValidationConfiguration getMethodValidationConfiguration() { - return this.methodValidationConfiguration; - } - @Override public T unwrap(Class type) { //allow unwrapping into public super types diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 957f3113c6..ab1bdbf9fe 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; + import javax.validation.ConstraintValidatorFactory; import javax.validation.ConstraintViolation; import javax.validation.ElementKind; @@ -174,7 +175,7 @@ public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory, this.validatedValueHandlers = validatedValueHandlers; this.constraintValidatorManager = constraintValidatorManager; this.failFast = failFast; - + validationOrderGenerator = new ValidationOrderGenerator(); this.accessibleMembers = new ConcurrentReferenceHashMap( @@ -331,7 +332,7 @@ public final T unwrap(Class type) { if ( type.isAssignableFrom( Validator.class ) ) { return type.cast( this ); } - + throw log.getTypeNotSupportedForUnwrappingException( type ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java index 6c3d82f416..ebfefa2e55 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java @@ -16,6 +16,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; + import javax.validation.ElementKind; import javax.validation.metadata.ParameterDescriptor; @@ -33,7 +34,6 @@ import org.hibernate.validator.internal.util.ReflectionHelper; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; @@ -273,7 +273,6 @@ public static class Builder extends MetaDataBuilder { private final Set> typeArgumentsConstraints = newHashSet(); private final Set rules; private boolean isConstrained = false; - private final MethodValidationConfiguration methodValidationConfiguration; /** * Holds a merged representation of the configurations for one method @@ -304,22 +303,11 @@ public Builder( super( beanClass, constraintHelper ); this.executableHelper = executableHelper; - kind = constrainedExecutable.getKind(); - executable = constrainedExecutable.getExecutable(); + this.kind = constrainedExecutable.getKind(); + this.executable = constrainedExecutable.getExecutable(); + this.rules = new HashSet( methodValidationConfiguration.getConfiguredRuleSet() ); + add( constrainedExecutable ); - this.methodValidationConfiguration = methodValidationConfiguration; - - // Build the rules that will be enforced through the metadata created here - this.rules = new HashSet(); - for ( Class ruleClass : this.methodValidationConfiguration.getConfiguredRuleSet() ) { - MethodConfigurationRule rule = run( - NewInstance.action( - ruleClass, - "Method configuration rule" - ) - ); - this.rules.add( rule ); - } } @Override From ca9abbd17ee96a34c68562303eeee289ba2dcf21 Mon Sep 17 00:00:00 2001 From: Julien May Date: Wed, 7 Oct 2015 12:32:05 +0200 Subject: [PATCH 023/189] HV-1020 Support for additional context information for HibernateConstraintViolation --- copyright.txt | 1 + .../HibernateConstraintValidatorContext.java | 15 ++++ .../engine/HibernateConstraintViolation.java | 24 +++++++ .../engine/ConstraintViolationImpl.java | 68 +++++++++++++----- .../internal/engine/ValidationContext.java | 51 +++++++------- .../ConstraintValidatorContextImpl.java | 20 ++++-- .../ConstraintViolationCreationContext.java | 11 ++- ...bernateConstraintValidatorContextTest.java | 69 +++++++++++++++---- 8 files changed, 194 insertions(+), 65 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/engine/HibernateConstraintViolation.java diff --git a/copyright.txt b/copyright.txt index b14eeb10d5..eaad1a6008 100644 --- a/copyright.txt +++ b/copyright.txt @@ -21,6 +21,7 @@ Gunnar Morling Hardy Ferentschik Henno Vermeulen Jason T. Greene +Julien May Juraci Krohling Justin Nauman Kevin Pollet diff --git a/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java b/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java index bcaf1e6a1f..5fbe7af83f 100644 --- a/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java @@ -68,4 +68,19 @@ public interface HibernateConstraintValidatorContext extends ConstraintValidator * @since 5.2 */ TimeProvider getTimeProvider(); + + /** + * + * Allows to set an object that may further describe the violation. + * + * The user is responsible himself to ensure that this violation context is serializable in case the + * {@code javax.validation.ConstraintViolation} has to be serialized. + * + * @param payload an object representing additional information about the violation + * + * @return a reference to itself to allow method chaining + * + * @since 5.3 + */ + HibernateConstraintValidatorContext withDynamicPayload(Object payload); } diff --git a/engine/src/main/java/org/hibernate/validator/engine/HibernateConstraintViolation.java b/engine/src/main/java/org/hibernate/validator/engine/HibernateConstraintViolation.java new file mode 100644 index 0000000000..5f73222a53 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/engine/HibernateConstraintViolation.java @@ -0,0 +1,24 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.engine; + +import javax.validation.ConstraintViolation; + +/** + * A custom {@link ConstraintViolation} which allows to get additional information for a constraint violation. + * + * @since 5.3 + */ +public interface HibernateConstraintViolation extends ConstraintViolation { + /** + * @param type The type of payload to retrieve + * + * @return an instance of the specified type set by the user via + * {@link org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext#withDynamicPayload(Object)}. + */ + C getDynamicPayload(Class type); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java index 47879d4c17..72daa74505 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConstraintViolationImpl.java @@ -6,21 +6,22 @@ */ package org.hibernate.validator.internal.engine; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; - -import javax.validation.ConstraintViolation; -import javax.validation.Path; -import javax.validation.metadata.ConstraintDescriptor; import java.io.Serializable; import java.lang.annotation.ElementType; import java.util.Map; +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.engine.HibernateConstraintViolation; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; /** * @author Emmanuel Bernard * @author Hardy Ferentschik */ -public class ConstraintViolationImpl implements ConstraintViolation, Serializable { +public class ConstraintViolationImpl implements HibernateConstraintViolation, Serializable { private static final Log log = LoggerFactory.make(); private static final long serialVersionUID = -4970067626703103139L; @@ -36,6 +37,7 @@ public class ConstraintViolationImpl implements ConstraintViolation, Seria private final ElementType elementType; private final Object[] executableParameters; private final Object executableReturnValue; + private final Object dynamicPayload; private final int hashCode; public static ConstraintViolation forBeanValidation(String messageTemplate, @@ -47,7 +49,8 @@ public static ConstraintViolation forBeanValidation(String messageTemplat Object value, Path propertyPath, ConstraintDescriptor constraintDescriptor, - ElementType elementType) { + ElementType elementType, + Object dynamicPayload) { return new ConstraintViolationImpl( messageTemplate, expressionVariables, @@ -60,7 +63,8 @@ public static ConstraintViolation forBeanValidation(String messageTemplat constraintDescriptor, elementType, null, - null + null, + dynamicPayload ); } @@ -74,7 +78,8 @@ public static ConstraintViolation forParameterValidation(String messageTe Path propertyPath, ConstraintDescriptor constraintDescriptor, ElementType elementType, - Object[] executableParameters) { + Object[] executableParameters, + Object dynamicPayload) { return new ConstraintViolationImpl( messageTemplate, expressionVariables, @@ -87,7 +92,8 @@ public static ConstraintViolation forParameterValidation(String messageTe constraintDescriptor, elementType, executableParameters, - null + null, + dynamicPayload ); } @@ -101,7 +107,8 @@ public static ConstraintViolation forReturnValueValidation(String message Path propertyPath, ConstraintDescriptor constraintDescriptor, ElementType elementType, - Object executableReturnValue) { + Object executableReturnValue, + Object dynamicPayload) { return new ConstraintViolationImpl( messageTemplate, expressionVariables, @@ -114,7 +121,8 @@ public static ConstraintViolation forReturnValueValidation(String message constraintDescriptor, elementType, null, - executableReturnValue + executableReturnValue, + dynamicPayload ); } @@ -129,7 +137,8 @@ private ConstraintViolationImpl(String messageTemplate, ConstraintDescriptor constraintDescriptor, ElementType elementType, Object[] executableParameters, - Object executableReturnValue) { + Object executableReturnValue, + Object dynamicPayload) { this.messageTemplate = messageTemplate; this.expressionVariables = expressionVariables; this.interpolatedMessage = interpolatedMessage; @@ -142,6 +151,7 @@ private ConstraintViolationImpl(String messageTemplate, this.elementType = elementType; this.executableParameters = executableParameters; this.executableReturnValue = executableReturnValue; + this.dynamicPayload = dynamicPayload; // pre-calculate hash code, the class is immutable and hashCode is needed often this.hashCode = createHashCode(); } @@ -196,9 +206,13 @@ public final ConstraintDescriptor getConstraintDescriptor() { @Override public C unwrap(Class type) { + // Keep backward compatibility if ( type.isAssignableFrom( ConstraintViolation.class ) ) { return type.cast( this ); } + if ( type.isAssignableFrom( HibernateConstraintViolation.class ) ) { + return type.cast( this ); + } throw log.getTypeNotSupportedForUnwrappingException( type ); } @@ -213,11 +227,25 @@ public Object getExecutableReturnValue() { } @Override - // IMPORTANT - some behaviour of Validator depends on the correct implementation of this equals method! (HF) + public C getDynamicPayload(Class type) { + if ( type.isAssignableFrom( this.dynamicPayload.getClass() ) ) { + return type.cast( this.dynamicPayload ); + } + else { + return null; + } + } - // Do not take expressionVariables into account here. If everything else matches, the two CV should be considered - // equals (and because of the scary comment above). After all, expressionVariables is just a hint about how we got - // to the actual CV. (NF) + /** + * IMPORTANT - some behaviour of Validator depends on the correct implementation of this equals method! (HF) + * + * {@code expressionVariables} and {@code dynamicPayload} are not taken into account for equality. These + * variables solely enrich the actual Constraint Violation with additional information e.g how we actually + * got to this CV. + * + * @return true if the two ConstraintViolation's are considered equals; false otherwise + */ + @Override public boolean equals(Object o) { if ( this == o ) { return true; @@ -276,7 +304,9 @@ public String toString() { return sb.toString(); } - // Same as for equals, do not take expressionVariables into account here. + /** + * @see #equals(Object) on which fields are taken into account + */ private int createHashCode() { int result = interpolatedMessage != null ? interpolatedMessage.hashCode() : 0; result = 31 * result + ( propertyPath != null ? propertyPath.hashCode() : 0 ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java index 4e45c84054..c379ec1811 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java @@ -6,6 +6,26 @@ */ package org.hibernate.validator.internal.engine; +import java.lang.annotation.ElementType; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.MessageInterpolator; +import javax.validation.ParameterNameProvider; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.ValidationException; +import javax.validation.metadata.ConstraintDescriptor; + import com.fasterxml.classmate.ResolvedType; import com.fasterxml.classmate.TypeResolver; @@ -23,27 +43,6 @@ import org.hibernate.validator.spi.time.TimeProvider; import org.hibernate.validator.spi.valuehandling.ValidatedValueUnwrapper; -import javax.validation.ConstraintValidatorFactory; -import javax.validation.ConstraintViolation; -import javax.validation.MessageInterpolator; -import javax.validation.ParameterNameProvider; -import javax.validation.Path; -import javax.validation.TraversableResolver; -import javax.validation.ValidationException; -import javax.validation.metadata.ConstraintDescriptor; - -import java.lang.annotation.ElementType; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; @@ -308,6 +307,7 @@ public ConstraintViolation createConstraintViolation(ValueContext local //same for expression variables Map expressionVariables = Collections.unmodifiableMap( constraintViolationCreationContext.getExpressionVariables() ); + Object dynamicPayload = constraintViolationCreationContext.getDynamicPayload(); if ( executableParameters != null ) { return ConstraintViolationImpl.forParameterValidation( messageTemplate, @@ -320,7 +320,8 @@ public ConstraintViolation createConstraintViolation(ValueContext local path, descriptor, localContext.getElementType(), - executableParameters + executableParameters, + dynamicPayload ); } else if ( executableReturnValue != null ) { @@ -335,7 +336,8 @@ else if ( executableReturnValue != null ) { path, descriptor, localContext.getElementType(), - executableReturnValue + executableReturnValue, + dynamicPayload ); } else { @@ -349,7 +351,8 @@ else if ( executableReturnValue != null ) { localContext.getCurrentValidatedValue(), path, descriptor, - localContext.getElementType() + localContext.getElementType(), + dynamicPayload ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java index e7750c1a21..7346f7357a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java @@ -6,10 +6,12 @@ */ package org.hibernate.validator.internal.engine.constraintvalidation; +import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; +import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; + import java.util.ArrayList; import java.util.List; import java.util.Map; - import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext; import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderDefinedContext; @@ -27,9 +29,6 @@ import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.spi.time.TimeProvider; -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; - /** * @author Hardy Ferentschik * @author Gunnar Morling @@ -45,6 +44,7 @@ public class ConstraintValidatorContextImpl implements HibernateConstraintValida private final PathImpl basePath; private final ConstraintDescriptor constraintDescriptor; private boolean defaultDisabled; + private Object dynamicPayload; public ConstraintValidatorContextImpl(List methodParameterNames, TimeProvider timeProvider, PathImpl propertyPath, ConstraintDescriptor constraintDescriptor) { @@ -94,6 +94,12 @@ public TimeProvider getTimeProvider() { return timeProvider; } + @Override + public HibernateConstraintValidatorContext withDynamicPayload(Object violationContext) { + this.dynamicPayload = violationContext; + return this; + } + public final ConstraintDescriptor getConstraintDescriptor() { return constraintDescriptor; } @@ -113,7 +119,8 @@ public final List getConstraintViolationCrea new ConstraintViolationCreationContext( getDefaultConstraintMessageTemplate(), basePath, - parameterMapCopy + parameterMapCopy, + dynamicPayload ) ); } @@ -140,7 +147,8 @@ public ConstraintValidatorContext addConstraintViolation() { new ConstraintViolationCreationContext( messageTemplate, propertyPath, - parameterMapCopy + parameterMapCopy, + dynamicPayload ) ); return ConstraintValidatorContextImpl.this; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintViolationCreationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintViolationCreationContext.java index 45cad51e63..fc4283a50e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintViolationCreationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintViolationCreationContext.java @@ -20,15 +20,17 @@ public class ConstraintViolationCreationContext { private final String message; private final PathImpl propertyPath; private final Map expressionVariables; + private final Object dynamicPayload; public ConstraintViolationCreationContext(String message, PathImpl property) { - this( message, property, Collections.emptyMap() ); + this( message, property, Collections.emptyMap(), null ); } - public ConstraintViolationCreationContext(String message, PathImpl property, Map expressionVariables) { + public ConstraintViolationCreationContext(String message, PathImpl property, Map expressionVariables, Object dynamicPayload) { this.message = message; this.propertyPath = property; this.expressionVariables = expressionVariables; + this.dynamicPayload = dynamicPayload; } public final String getMessage() { @@ -43,12 +45,17 @@ public Map getExpressionVariables() { return expressionVariables; } + public Object getDynamicPayload() { + return dynamicPayload; + } + @Override public String toString() { final StringBuilder sb = new StringBuilder( "ConstraintViolationCreationContext{" ); sb.append( "message='" ).append( message ).append( '\'' ); sb.append( ", propertyPath=" ).append( propertyPath ); sb.append( ", messageParameters=" ).append( expressionVariables ); + sb.append( ", dynamicPayload=" ).append( dynamicPayload ); sb.append( '}' ); return sb.toString(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java index f4189df9da..c2c8db3ea4 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/HibernateConstraintValidatorContextTest.java @@ -6,12 +6,17 @@ */ package org.hibernate.validator.test.internal.engine.constraintvalidation; -import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; -import org.hibernate.validator.internal.engine.ConstraintViolationImpl; -import org.hibernate.validator.testutil.TestForIssue; -import org.junit.Assert; -import org.testng.annotations.Test; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations; +import static org.hibernate.validator.testutil.ValidatorUtil.getValidator; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.Map; +import java.util.Set; import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; @@ -19,16 +24,14 @@ import javax.validation.Payload; import javax.validation.ValidationException; import javax.validation.Validator; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Map; -import java.util.Set; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations; -import static org.hibernate.validator.testutil.ValidatorUtil.getValidator; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; +import org.hibernate.validator.engine.HibernateConstraintViolation; +import org.hibernate.validator.internal.engine.ConstraintViolationImpl; +import org.hibernate.validator.testutil.TestForIssue; +import org.junit.Assert; +import org.testng.annotations.Test; +import org.testng.collections.Lists; /** * @author Hardy Ferentschik @@ -38,6 +41,9 @@ public class HibernateConstraintValidatorContextTest { private static final String QUESTION_1 = "The answer to life?"; private static final String QUESTION_2 = "What is 1+1 and what is the answer to life?"; private static final String QUESTION_3 = "This is a trick question"; + private static final String QUESTION_4 = "What keywords are not allowed?"; + + private static final List INVALID_KEYWORDS = Lists.newArrayList( "foo", "bar", "baz" ); @Test @TestForIssue(jiraKey = "HV-701") @@ -91,6 +97,38 @@ public void testExpressionVariablesAreExposedInConstraintViolation() throws Exce Assert.assertEquals( 42, expressionVariables.get( "answer" ) ); } + @Test + @TestForIssue( jiraKey = "HV-1020") + public void testDynamicPayloadExposedInHibernateConstraintViolation() { + Validator validator = getValidator(); + Set> constraintViolations = validator.validate( new Foo( QUESTION_4 ) ); + + assertNumberOfViolations( constraintViolations, 1 ); + + ConstraintViolation constraintViolation = constraintViolations.iterator().next(); + @SuppressWarnings("unchecked") + HibernateConstraintViolation hibernateConstraintViolation = constraintViolation.unwrap( + HibernateConstraintViolation.class + ); + + Assert.assertEquals( INVALID_KEYWORDS, (List) hibernateConstraintViolation.getDynamicPayload( List.class ) ); + } + + @Test + @TestForIssue( jiraKey = "HV-1020") + public void testNullIsReturnedForNonExistingPayloadType() { + Validator validator = getValidator(); + Set> constraintViolations = validator.validate( new Foo( QUESTION_4 ) ); + + assertNumberOfViolations( constraintViolations, 1 ); + + ConstraintViolation constraintViolation = constraintViolations.iterator().next(); + @SuppressWarnings("unchecked") + HibernateConstraintViolation hibernateConstraintViolation = constraintViolation.unwrap( HibernateConstraintViolation.class ); + + Assert.assertEquals( null, hibernateConstraintViolation.getDynamicPayload( String.class ) ); + } + public class Foo { @OracleConstraint private final String question; @@ -132,6 +170,9 @@ else if ( question.equals( QUESTION_2 ) ) { else if ( question.equals( QUESTION_3 ) ) { hibernateContext.addExpressionVariable( "answer", "${foo}" ); } + else if ( question.equals( QUESTION_4 ) ) { + hibernateContext.withDynamicPayload( INVALID_KEYWORDS ); + } else { tryingToIllegallyUseNullAttributeName( hibernateContext ); } From 44b392f9cf47eddd3733cbcf54b8961f83580878 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Wed, 23 Dec 2015 00:20:05 +0100 Subject: [PATCH 024/189] HV-1020 Documentation update for dynamic payloads in constraint violations --- documentation/src/main/asciidoc/ch11.asciidoc | 73 ++++++++++++++----- .../chapter11/dynamicpayload/Car.java | 28 +++++++ .../dynamicpayload/DynamicPayLoadTest.java | 48 ++++++++++++ .../chapter11/dynamicpayload/Person.java | 5 ++ .../dynamicpayload/ValidPassengerCount.java | 25 +++++++ .../ValidPassengerCountValidator.java | 51 +++++++++++++ 6 files changed, 211 insertions(+), 19 deletions(-) create mode 100644 documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Car.java create mode 100644 documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/DynamicPayLoadTest.java create mode 100644 documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Person.java create mode 100644 documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCount.java create mode 100644 documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCountValidator.java diff --git a/documentation/src/main/asciidoc/ch11.asciidoc b/documentation/src/main/asciidoc/ch11.asciidoc index 3b9d90e58e..24e2fa9ae7 100644 --- a/documentation/src/main/asciidoc/ch11.asciidoc +++ b/documentation/src/main/asciidoc/ch11.asciidoc @@ -395,6 +395,39 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/propertypa This is specifically useful to obtain the element of `Set` properties on the property path (e.g. `apartments` in the example) which otherwise could not be identified (unlike for `Map` and `List`, there is no key nor index in this case). +[[section-dynamic-payload]] +=== Dynamic payload as part of ConstraintViolation + +In some cases automatic processing of violations can be aided, if the constraint violation provides additional +data - a so called dynamic payload. This dynamic payload could for example contain hints to the user on how to +resolve the violation. + +Dynamic payloads can be set in <> using `HibernateConstraintValidatorContext`. +This is shown in example <> where the +`javax.validation.ConstraintValidatorContext` is unwrapped to `HibernateConstraintValidatorContext` in order to call +`withDynamicPayload`. + +[[example-constraint-validator-setting-dynamic-payload]] +.ConstraintValidator implementation setting a dynamic payload +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCountValidator.java[tags=include] +---- +==== + +On the constraint violation processing side, a `javax.validation.ConstraintViolation` can then in turn be +unwrapped to `HibernateConstraintViolation` in order to retrieve the dynamic payload for further processing. + +[[example-retrieving-dynamic-payload]] +.Retrieval of a ConstraintViolations's dynamic payload +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/DynamicPayloadTest.java[tags=include] +---- +==== + [[non-el-message-interpolator]] === `ParameterMessageInterpolator` @@ -438,16 +471,26 @@ custom extensions for both of these interfaces. `HibernateConstraintValidatorContext` is a subtype of `ConstraintValidatorContext` which allows you to: * set arbitrary parameters for interpolation via the Expression Language message interpolation -facility (see <>) -* obtain the `TimeProvider` for getting the current time when validating the `Future` and `@Past` constraints -(see <>) - -This is useful if you for instance would like to customize the message of the `@Future` constraint. -By default the message just is "must be in the future". <> shows +facility using `HibernateConstraintValidatorContext#addExpressionVariable(String, Object)`. +For an example refer to <>. ++ +[NOTE] +==== +Note that the parameters specified via `addExpressionVariable(String, Object)` are global and apply +for all constraint violations created by this `isValid()` invocation. This includes the default +constraint violation, but also all violations created by the `ConstraintViolationBuilder`. You can, +however, update the parameters between invocations of +`ConstraintViolationBuilder#addConstraintViolation()`. +==== +* obtain the `TimeProvider` for getting the current time when validating `@Future` and `@Past` constraints +(see also <>). ++ +This is useful if you want to customize the message of the `@Future` constraint. +By default the message is just "must be in the future". <> shows how to include the current date in order to make the message more explicit. - ++ [[example-custom-message-parameter]] -.Custom `@Future` validator with message parameters +.Custom @Future validator with message parameters ==== [source, JAVA] ---- @@ -483,20 +526,12 @@ public class MyFutureValidator implements ConstraintValidator { } ---- ==== - -[NOTE] -==== -Note that the parameters specified via `addExpressionVariable(String, Object)` are global and apply -for all constraint violations created by this `isValid()` invocation. This includes the default -constraint violation, but also all violations created by the `ConstraintViolationBuilder`. You can, -however, update the parameters between invocations of -`ConstraintViolationBuilder#addConstraintViolation()`. -==== - ++ [WARNING] ==== This functionality is currently experimental and might change in future versions. ==== +* set an arbitrary dynamic payload - see <> ==== `HibernateMessageInterpolatorContext` @@ -886,7 +921,7 @@ validator factory still is referenced by application code. ==== [[section-time-provider]] -=== Time providers for `@Future` and `@Past` +=== Time providers for @Future and @Past By default the current system time is used when validating the `@Future` and `@Past` constraints. In some cases it can be necessary though to work with another "logical" date rather than the system time, diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Car.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Car.java new file mode 100644 index 0000000000..bcd2d0a39b --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Car.java @@ -0,0 +1,28 @@ +package org.hibernate.validator.referenceguide.chapter11.dynamicpayload; + +import java.util.ArrayList; +import java.util.List; + +@ValidPassengerCount +public class Car { + private final int seatCount; + private final List passengers; + + public Car(int seatCount) { + this.seatCount = seatCount; + this.passengers = new ArrayList<>(); + } + + public int getSeatCount() { + return seatCount; + } + + public List getPassengers() { + return passengers; + } + + public void addPassenger(Person passenger) { + passengers.add( passenger ); + } +} + diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/DynamicPayLoadTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/DynamicPayLoadTest.java new file mode 100644 index 0000000000..f90a2e75e8 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/DynamicPayLoadTest.java @@ -0,0 +1,48 @@ +package org.hibernate.validator.referenceguide.chapter11.dynamicpayload; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.junit.BeforeClass; +import org.junit.Test; + +import org.hibernate.validator.engine.HibernateConstraintViolation; + +import static org.junit.Assert.assertEquals; + +public class DynamicPayLoadTest { + + private static Validator validator; + + + @BeforeClass + public static void setUpValidator() { + ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); + validator = validatorFactory.getValidator(); + } + + //tag::include[] + @Test + public void testDynamicPayloadAddedToConstraintViolation() throws Exception { + Car car = new Car( 2 ); + car.addPassenger( new Person() ); + car.addPassenger( new Person() ); + car.addPassenger( new Person() ); + Set> constraintViolations = validator.validate( car ); + + assertEquals( 1, constraintViolations.size() ); + + ConstraintViolation constraintViolation = constraintViolations.iterator().next(); + @SuppressWarnings("unchecked") + HibernateConstraintViolation hibernateConstraintViolation = constraintViolation.unwrap( + HibernateConstraintViolation.class + ); + String suggestedCar = hibernateConstraintViolation.getDynamicPayload( String.class ); + assertEquals( "Toyota Volta", suggestedCar ); + } + //end::include[] +} + diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Person.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Person.java new file mode 100644 index 0000000000..46d920215a --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/Person.java @@ -0,0 +1,5 @@ +package org.hibernate.validator.referenceguide.chapter11.dynamicpayload; + +public class Person { +} + diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCount.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCount.java new file mode 100644 index 0000000000..aa89d92c7c --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCount.java @@ -0,0 +1,25 @@ +package org.hibernate.validator.referenceguide.chapter11.dynamicpayload; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.validation.Constraint; +import javax.validation.Payload; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({ TYPE, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@Constraint(validatedBy = { ValidPassengerCountValidator.class }) +@Documented +public @interface ValidPassengerCount { + + String message() default "The passenger count is exceeding the number of seats in the car"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} + diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCountValidator.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCountValidator.java new file mode 100644 index 0000000000..75304c7a47 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/dynamicpayload/ValidPassengerCountValidator.java @@ -0,0 +1,51 @@ +package org.hibernate.validator.referenceguide.chapter11.dynamicpayload; + +import java.util.Map; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; + +//tag::include[] +public class ValidPassengerCountValidator implements ConstraintValidator { + + private static final Map suggestedCars = newHashMap(); + + static { + suggestedCars.put( 2, "Chevrolet Corvette" ); + suggestedCars.put( 3, "Toyota Volta" ); + suggestedCars.put( 4, "Maserati GranCabrio" ); + suggestedCars.put( 5, " Mercedes-Benz E-Class" ); + } + + @Override + public void initialize(ValidPassengerCount constraintAnnotation) { + } + + @Override + public boolean isValid(Car car, ConstraintValidatorContext context) { + if ( car == null ) { + return true; + } + + int passangerCount = car.getPassengers().size(); + if ( car.getSeatCount() >= passangerCount ) { + return true; + } + else { + + if ( suggestedCars.containsKey( passangerCount ) ) { + HibernateConstraintValidatorContext hibernateContext = context.unwrap( + HibernateConstraintValidatorContext.class + ); + hibernateContext.withDynamicPayload( suggestedCars.get( passangerCount ) ); + } + return false; + } + } +} +//end::include[] + + From 8a1402d2d34b929dbdc630a92e158cdae8c8da11 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Wed, 23 Dec 2015 16:16:36 +0100 Subject: [PATCH 025/189] HV-1020 comment fix --- .../HibernateConstraintValidatorContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java b/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java index 5fbe7af83f..b389fb3767 100644 --- a/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorContext.java @@ -73,7 +73,7 @@ public interface HibernateConstraintValidatorContext extends ConstraintValidator * * Allows to set an object that may further describe the violation. * - * The user is responsible himself to ensure that this violation context is serializable in case the + * The user is responsible himself to ensure that this payload is serializable in case the * {@code javax.validation.ConstraintViolation} has to be serialized. * * @param payload an object representing additional information about the violation From e8e97b95f7a1d684a2c02d853d1f34b1af5e6205 Mon Sep 17 00:00:00 2001 From: Benson Margulies Date: Wed, 23 Dec 2015 11:41:11 +0100 Subject: [PATCH 026/189] HV-1039 Allowing to pass EL factory into bootstrap; Making Karaf feature work with Karaf 4.0.x; * Adding constructor to ResourceBundleMessageInterpolator to allow caller to supply an ExpressionFactory, avoiding SPI / TCCL issues in OSGi * Updating JBoss Logging * Moving ParaNamer wrap to its own feature * Moving to current PaxExam --- copyright.txt | 1 + .../internal/engine/ValidatorFactoryImpl.java | 24 +-------- .../messageinterpolation/ElTermResolver.java | 19 ++++--- .../InterpolationTerm.java | 11 +++- .../ResourceBundleMessageInterpolator.java | 12 ++++- osgi/integrationtest/pom.xml | 32 ++++++++++-- .../java/com/example/CustomerDecimalMin.java | 18 +++++++ .../integrationtest/OsgiIntegrationTest.java | 50 ++++++++++++++++--- .../src/test/resources/log4j.properties | 8 +++ .../src/main/features/features.xml | 13 +++-- pom.xml | 2 +- 11 files changed, 141 insertions(+), 49 deletions(-) create mode 100644 osgi/integrationtest/src/test/java/com/example/CustomerDecimalMin.java create mode 100644 osgi/integrationtest/src/test/resources/log4j.properties diff --git a/copyright.txt b/copyright.txt index eaad1a6008..b5d34e63e2 100644 --- a/copyright.txt +++ b/copyright.txt @@ -1,6 +1,7 @@ Adam Stawicki Alaa Nassef Andrey Rodionov +Benson Margulies Brent Douglas Carlos Vara Dag Hovland diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 067c8b1227..17c4be5595 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -397,29 +397,7 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, List> validatedValueHandlers, TimeProvider timeProvider, MethodValidationConfiguration methodValidationConfiguration) { - - // HV-793 - To fail eagerly in case we have no EL dependencies on the classpath we try to load the expression - // factory - if ( messageInterpolator instanceof ResourceBundleMessageInterpolator ) { - if ( missingElDependencies == null ) { - try { - run( - LoadClass.action( - "javax.el.ExpressionFactory", - messageInterpolator.getClass().getClassLoader(), - false - ) - ); - missingElDependencies = false; - } - catch ( ValidationException e ) { - missingElDependencies = true; - } - } - if ( missingElDependencies ) { - throw log.getMissingELDependenciesException(); - } - } + BeanMetaDataManager beanMetaDataManager; if ( !beanMetaDataManagerMap.containsKey( parameterNameProvider ) ) { beanMetaDataManager = new BeanMetaDataManager( diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ElTermResolver.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ElTermResolver.java index 6810425b79..6da9307118 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ElTermResolver.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ElTermResolver.java @@ -43,14 +43,21 @@ public class ElTermResolver implements TermResolver { /** * Factory for creating EL expressions */ - private static final ExpressionFactory expressionFactory; + private final ExpressionFactory expressionFactory; - static { - expressionFactory = ExpressionFactory.newInstance(); - } - - public ElTermResolver(Locale locale) { + /** + * Construct the resolver. The expression factory has to be passed in to ensure that it is + * set up early and to allow for application control. + * @param locale the locale. + * @param expressionFactory the expression factory. + */ + public ElTermResolver(Locale locale, ExpressionFactory expressionFactory) { this.locale = locale; + /* + * This call to newInstance might throw. Bad news; error is not until call to validate for case where + * EL is nonfunctional. Good news; cases that don't need EL don't get any exception. + */ + this.expressionFactory = expressionFactory != null ? expressionFactory : ExpressionFactory.newInstance(); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/InterpolationTerm.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/InterpolationTerm.java index c381ed934b..883160ff1d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/InterpolationTerm.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/InterpolationTerm.java @@ -8,6 +8,7 @@ import java.util.Locale; +import javax.el.ExpressionFactory; import javax.validation.MessageInterpolator; /** @@ -37,11 +38,17 @@ public class InterpolationTerm { */ private final TermResolver resolver; - public InterpolationTerm(String expression, Locale locale) { + /** + * Create an interpolation term for an expression. + * @param expression the expression. + * @param locale the locale. + * @param expressionFactory the expression factory to use if the expression uses EL. + */ + public InterpolationTerm(String expression, Locale locale, ExpressionFactory expressionFactory) { this.expression = expression; if ( isElExpression( expression ) ) { this.type = InterpolationTermType.EL; - this.resolver = new ElTermResolver(locale); + this.resolver = new ElTermResolver( locale, expressionFactory ); } else { this.type = InterpolationTermType.PARAMETER; diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java index af0cb138f7..dc270c4d25 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java @@ -8,6 +8,8 @@ import java.util.Locale; +import javax.el.ExpressionFactory; + import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTerm; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; @@ -21,6 +23,9 @@ * @author Adam Stawicki */ public class ResourceBundleMessageInterpolator extends AbstractMessageInterpolator { + + private ExpressionFactory expressionFactory; + public ResourceBundleMessageInterpolator() { super(); } @@ -44,9 +49,14 @@ public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundl super( userResourceBundleLocator, null, cachingEnabled ); } + public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, boolean cachingEnabled, ExpressionFactory expressionFactory) { + super( userResourceBundleLocator, null, cachingEnabled ); + this.expressionFactory = expressionFactory; + } + @Override public String interpolate(Context context, Locale locale, String term) { - InterpolationTerm expression = new InterpolationTerm( term, locale ); + InterpolationTerm expression = new InterpolationTerm( term, locale, expressionFactory ); return expression.interpolate( context ); } } diff --git a/osgi/integrationtest/pom.xml b/osgi/integrationtest/pom.xml index a4056ff0e7..73af4ed1fa 100644 --- a/osgi/integrationtest/pom.xml +++ b/osgi/integrationtest/pom.xml @@ -23,7 +23,8 @@ true - 4.0.0 + 4.7.0 + 2.4.4 3.0.3 @@ -46,6 +47,12 @@ pom test + + javax.inject + javax.inject + 1 + test + @@ -60,7 +67,8 @@ ${apache.karaf.version} tar.gz test - + * @@ -83,7 +91,13 @@ org.ops4j.pax.url pax-url-aether - 1.6.0 + ${pax.url.version} + test + + + org.osgi + org.osgi.core + 6.0.0 test @@ -102,6 +116,11 @@ slf4j-log4j12 test + + javax.el + javax.el-api + test + @@ -130,6 +149,13 @@ org.apache.maven.plugins maven-checkstyle-plugin + + org.apache.maven.plugins + maven-surefire-plugin + + false + + diff --git a/osgi/integrationtest/src/test/java/com/example/CustomerDecimalMin.java b/osgi/integrationtest/src/test/java/com/example/CustomerDecimalMin.java new file mode 100644 index 0000000000..b57c1fd96f --- /dev/null +++ b/osgi/integrationtest/src/test/java/com/example/CustomerDecimalMin.java @@ -0,0 +1,18 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package com.example; + +import javax.validation.constraints.DecimalMin; + +/** + * @author Benson Margulies + */ +public class CustomerDecimalMin { + + @DecimalMin("1.00") + private final int cannotBeZero = 0; +} diff --git a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java index 4c7318b76f..db44ac2521 100644 --- a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java +++ b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/OsgiIntegrationTest.java @@ -11,15 +11,17 @@ import java.util.List; import java.util.Locale; import java.util.Set; + +import javax.el.ExpressionFactory; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.ValidationProviderResolver; import javax.validation.spi.ValidationProvider; -import com.example.Customer; -import com.example.ExampleConstraintValidatorFactory; -import com.example.Order; -import com.example.RetailOrder; +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,8 +33,11 @@ import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerClass; -import org.hibernate.validator.HibernateValidator; -import org.hibernate.validator.HibernateValidatorConfiguration; +import com.example.Customer; +import com.example.CustomerDecimalMin; +import com.example.ExampleConstraintValidatorFactory; +import com.example.Order; +import com.example.RetailOrder; import static org.junit.Assert.assertEquals; import static org.ops4j.pax.exam.CoreOptions.maven; @@ -181,6 +186,39 @@ public void canObtainValuesFromValidationMessages() { assertEquals( "Not a valid retail order name", constraintViolations.iterator().next().getMessage() ); } + @Test + public void canUseExpressionLanguageInConstraintMessage() { + ExpressionFactory expressionFactory = buildExpressionFactory(); + + Set> constraintViolations = Validation.byProvider( HibernateValidator.class ) + .providerResolver( new MyValidationProviderResolver() ) + .configure() + .externalClassLoader( getClass().getClassLoader() ) + .messageInterpolator( new ResourceBundleMessageInterpolator( + new PlatformResourceBundleLocator( ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES ), + true, + expressionFactory ) + ) + .buildValidatorFactory() + .getValidator() + .validate( new CustomerDecimalMin() ); + + assertEquals( 1, constraintViolations.size() ); + assertEquals( "must be greater than or equal to 1.00", constraintViolations.iterator().next().getMessage() ); + } + + private ExpressionFactory buildExpressionFactory() { + ClassLoader oldTccl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); + + try { + return ExpressionFactory.newInstance(); + } + finally { + Thread.currentThread().setContextClassLoader( oldTccl ); + } + } + public static class MyValidationProviderResolver implements ValidationProviderResolver { @Override diff --git a/osgi/integrationtest/src/test/resources/log4j.properties b/osgi/integrationtest/src/test/resources/log4j.properties new file mode 100644 index 0000000000..6e6c7c0720 --- /dev/null +++ b/osgi/integrationtest/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +### direct log messages to stdout ### +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n + +### set log levels - for more verbose logging change 'info' to 'debug' ### +log4j.rootLogger=debug, stdout diff --git a/osgi/karaf-features/src/main/features/features.xml b/osgi/karaf-features/src/main/features/features.xml index cb99409067..62b858e346 100644 --- a/osgi/karaf-features/src/main/features/features.xml +++ b/osgi/karaf-features/src/main/features/features.xml @@ -14,16 +14,15 @@ mvn:org.hibernate/hibernate-validator/${project.version} mvn:javax.validation/validation-api/${bv.api.version} - - wrap:mvn:org.jboss.logging/jboss-logging/${jboss.logging.version}$overwrite=merge&Bundle-SymbolicName=org.hibernate.org.jboss.logging&Import-Package=org.apache.log4j;resolution:=optional,org.apache.logging.log4j;version="[2.0,3)";resolution:=optional,org.apache.logging.log4j.message;version="[2.0,3)";resolution:=optional,org.apache.logging.log4j.spi;version="[2.0,3)";resolution:=optional,org.jboss.logmanager;version="[1.5,2)";resolution:=optional,org.slf4j;version="[1.7,2)";resolution:=optional,org.slf4j.spi;version="[1.7,2)";resolution:=optional + mvn:org.jboss.logging/jboss-logging/${jboss.logging.version} mvn:com.fasterxml/classmate/${classmate.version} mvn:javax.el/javax.el-api/${javax.el.version} mvn:org.glassfish.web/javax.el/${javax.el.version} + mvn:org.jsoup/jsoup/${jsoup.version} + mvn:joda-time/joda-time/${joda-time.version} + + + hibernate-validator wrap:mvn:com.thoughtworks.paranamer:paranamer:${paranamer.version} - org.jsoup:jsoup:${jsoup.version} - joda-time:joda-time:${joda-time.version} diff --git a/pom.xml b/pom.xml index 15b4d27580..213b524776 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ 10.0.0.CR2 1.1.9.Final 1.7.2 - 3.2.1.Final + 3.3.0.Final 2.2.4 1.2.0.Final From 6bfde6e4fcc64c055715c25221aac48c8f191ad2 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Wed, 23 Dec 2015 15:09:11 +0100 Subject: [PATCH 027/189] HV-1039 Moving check for EL factory presence to ResourceBundleMessageInterpolator --- .../internal/engine/ValidatorFactoryImpl.java | 4 --- .../ResourceBundleMessageInterpolator.java | 15 +++++++++++ .../ValidatorFactoryNoELBootstrapTest.java | 25 ++++++++++++------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 17c4be5595..4a6b9f83ce 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -19,7 +19,6 @@ import javax.validation.MessageInterpolator; import javax.validation.ParameterNameProvider; import javax.validation.TraversableResolver; -import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.spi.ConfigurationState; @@ -43,7 +42,6 @@ import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.privilegedactions.LoadClass; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; -import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; import org.hibernate.validator.spi.time.TimeProvider; @@ -66,8 +64,6 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private static final Log log = LoggerFactory.make(); - private static Boolean missingElDependencies; - /** * The default message interpolator for this factory. */ diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java index dc270c4d25..8e53370389 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/ResourceBundleMessageInterpolator.java @@ -11,6 +11,8 @@ import javax.el.ExpressionFactory; import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTerm; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; /** @@ -24,8 +26,21 @@ */ public class ResourceBundleMessageInterpolator extends AbstractMessageInterpolator { + private static final Log LOG = LoggerFactory.make(); + private ExpressionFactory expressionFactory; + // HV-793 - To fail eagerly in case we have no EL dependencies on the classpath we try to load the expression + // factory type eagerly + static { + try { + ExpressionFactory.class.getName(); + } + catch (NoClassDefFoundError e) { + throw LOG.getMissingELDependenciesException(); + } + } + public ResourceBundleMessageInterpolator() { super(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java index de02448129..e6f5a68792 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryNoELBootstrapTest.java @@ -11,11 +11,11 @@ import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import javax.validation.Validation; -import org.testng.annotations.Test; +import javax.validation.Validation; import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -33,18 +33,15 @@ public void testMissingELDependencyThrowsExceptionDuringFactoryBootstrap() throw Object validation = clazz.newInstance(); Method m = clazz.getMethod( "buildDefaultValidatorFactory" ); - - Object validatorFactory = m.invoke( validation ); - Method getValidatorMethod = validatorFactory.getClass().getMethod( "getValidator" ); + try { - getValidatorMethod.invoke( validatorFactory ); + m.invoke( validation ); fail( "An exception should have been thrown" ); } catch ( InvocationTargetException e ) { - Exception exceptionInValidation = (Exception) e.getTargetException(); assertTrue( - exceptionInValidation.getMessage().startsWith( "HV000183" ), - "Bootstrapping in Validation should threw an unexpected exception: " + exceptionInValidation.getMessage() + getRootCause( e ).getMessage().startsWith( "HV000183" ), + "Bootstrapping in Validation should threw an unexpected exception: " + e.getMessage() ); } } @@ -113,4 +110,14 @@ private byte[] loadClassData(String className) throws IOException { buffer.flush(); return buffer.toByteArray(); } + + private Throwable getRootCause(Throwable throwable) { + while ( true ) { + Throwable cause = throwable.getCause(); + if ( cause == null ) { + return throwable; + } + throwable = cause; + } + } } From 0b352ce1dac814afa6f56ed2f64d0915a0041373 Mon Sep 17 00:00:00 2001 From: Nicola Ferraro Date: Wed, 30 Dec 2015 18:32:53 +0100 Subject: [PATCH 028/189] HV-864 Implemented support for cross-parameter constraints --- .../ap/checks/ConstraintCheckFactory.java | 9 +- .../checks/CrossParameterConstraintCheck.java | 189 +++++++++++ .../validator/ap/checks/GetterCheck.java | 9 +- .../ap/checks/MethodAnnotationCheck.java | 92 +++++ .../validator/ap/checks/TypeCheck.java | 30 +- .../validator/ap/util/ConstraintHelper.java | 317 +++++++++++++++++- .../validator/ap/util/TypeNames.java | 5 + .../ap/ValidationProcessorMessages.properties | 7 + .../ap/AnnotationTypeValidationTest.java | 134 +++++++- .../ap/ConstraintValidationProcessorTest.java | 42 ++- .../DoubleValidatorConstraint.java | 33 ++ .../DoubleValidatorDummyValidator.java | 27 ++ .../GenericCrossParameterValidator.java | 29 ++ ...ricCrossParameterValidatorObjectArray.java | 29 ++ .../GenericNormalValidator.java | 26 ++ .../crossparameters/InvalidValidator.java | 27 ++ .../InvalidValidatorConstraint.java | 33 ++ ...idationUsingCrossParameterConstraints.java | 84 +++++ ...alidCrossParameterAndNormalConstraint.java | 36 ++ .../ValidCrossParameterConstraint.java | 33 ++ ...terConstraintWithObjectArrayValidator.java | 33 ++ ...AppliesToConstraintWithInvalidDefault.java | 37 ++ ...liesToConstraintWithInvalidReturnType.java | 35 ++ ...pliesToConstraintWithMissingAttribute.java | 36 ++ 24 files changed, 1283 insertions(+), 49 deletions(-) create mode 100644 annotation-processor/src/main/java/org/hibernate/validator/ap/checks/CrossParameterConstraintCheck.java create mode 100644 annotation-processor/src/main/java/org/hibernate/validator/ap/checks/MethodAnnotationCheck.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorConstraint.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorDummyValidator.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidator.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidatorObjectArray.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericNormalValidator.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidator.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidatorConstraint.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/MethodLevelValidationUsingCrossParameterConstraints.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterAndNormalConstraint.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraint.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraintWithObjectArrayValidator.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidDefault.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidReturnType.java create mode 100644 annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithMissingAttribute.java diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/ConstraintCheckFactory.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/ConstraintCheckFactory.java index 2df09780a8..4af31bff6c 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/ConstraintCheckFactory.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/ConstraintCheckFactory.java @@ -70,16 +70,16 @@ public ConstraintCheckFactory(Types typeUtils, ConstraintHelper constraintHelper methodChecks = CollectionHelper.newHashMap(); methodChecks.put( AnnotationType.CONSTRAINT_ANNOTATION, - new SingleValuedChecks( new GetterCheck(methodConstraintsSupported), new StaticCheck(), new TypeCheck( constraintHelper ) ) + new SingleValuedChecks( new GetterCheck(methodConstraintsSupported), new StaticCheck(), new MethodAnnotationCheck(constraintHelper), new TypeCheck( constraintHelper ) ) ); methodChecks.put( AnnotationType.MULTI_VALUED_CONSTRAINT_ANNOTATION, new MultiValuedChecks( - constraintHelper, new GetterCheck(methodConstraintsSupported), new StaticCheck(), new TypeCheck( constraintHelper ) + constraintHelper, new GetterCheck(methodConstraintsSupported), new StaticCheck(), new MethodAnnotationCheck(constraintHelper), new TypeCheck( constraintHelper ) ) ); methodChecks.put( AnnotationType.GRAPH_VALIDATION_ANNOTATION, - new SingleValuedChecks( new GetterCheck(methodConstraintsSupported), new StaticCheck(), new PrimitiveCheck() ) + new SingleValuedChecks( new GetterCheck(methodConstraintsSupported), new StaticCheck(), new MethodAnnotationCheck(constraintHelper), new PrimitiveCheck() ) ); methodChecks.put( AnnotationType.NO_CONSTRAINT_ANNOTATION, NULL_CHECKS ); @@ -98,7 +98,8 @@ constraintHelper, new GetterCheck(methodConstraintsSupported), new StaticCheck() new RetentionPolicyCheck( annotationApiHelper ), new TargetCheck( annotationApiHelper ), new ConstraintValidatorCheck( constraintHelper, annotationApiHelper ), - new AnnotationTypeMemberCheck( annotationApiHelper, typeUtils ) + new AnnotationTypeMemberCheck( annotationApiHelper, typeUtils ), + new CrossParameterConstraintCheck(annotationApiHelper, constraintHelper, typeUtils) ) ); annotationTypeChecks.put( AnnotationType.NO_CONSTRAINT_ANNOTATION, NULL_CHECKS ); diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/CrossParameterConstraintCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/CrossParameterConstraintCheck.java new file mode 100644 index 0000000000..7d2b390104 --- /dev/null +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/CrossParameterConstraintCheck.java @@ -0,0 +1,189 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.checks; + +import java.util.Collections; +import java.util.Set; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.ElementKindVisitor6; +import javax.lang.model.util.SimpleAnnotationValueVisitor6; +import javax.lang.model.util.TypeKindVisitor6; +import javax.lang.model.util.Types; + +import org.hibernate.validator.ap.util.AnnotationApiHelper; +import org.hibernate.validator.ap.util.CollectionHelper; +import org.hibernate.validator.ap.util.ConstraintHelper; +import org.hibernate.validator.ap.util.ConstraintHelper.AnnotationProcessorValidationTarget; +import org.hibernate.validator.ap.util.ConstraintHelper.ConstraintCheckResult; +import org.hibernate.validator.ap.util.TypeNames.BeanValidationTypes; + +/** + * Checks that a cross-parameter constraint is defined correctly with reference to the specifications. + *

+ *

    + *
  • It must have at most one cross-parameter validator.
  • + *
  • The cross-parameter validator must resolve to Object or Object[].
  • + *
  • If the constraint is both normal and cross-parameter, it must define a 'validationAppliesTo()' attribute.
  • + *
  • The 'validationAppliesTo' method, if any, must return a {@code ConstraintTarget}.
  • + *
  • The 'validationAppliesTo' method, if any, must declare {@code ConstraintTarget#IMPLICIT} as default return value.
  • + *
+ * + * @author Nicola Ferraro + */ +public class CrossParameterConstraintCheck extends AbstractConstraintCheck { + + private final AnnotationApiHelper annotationApiHelper; + + private final ConstraintHelper constraintHelper; + + private final Types typeUtils; + + public CrossParameterConstraintCheck(AnnotationApiHelper annotationApiHelper, ConstraintHelper constraintHelper, Types typeUtils) { + this.annotationApiHelper = annotationApiHelper; + this.constraintHelper = constraintHelper; + this.typeUtils = typeUtils; + } + + @Override + public Set checkAnnotationType(TypeElement element, AnnotationMirror annotation) { + + // this check applies to constraint annotations + if ( !constraintHelper.isConstraintAnnotation( element ) ) { + return Collections.emptySet(); + } + + DeclaredType elementType = element.asType().accept( new TypeKindVisitor6() { + + @Override + public DeclaredType visitDeclared(DeclaredType t, Void p) { + return t; + } + }, null ); + + Set targets = constraintHelper.getSupportedValidationTargets( elementType ); + if ( !targets.contains( AnnotationProcessorValidationTarget.PARAMETERS ) ) { + return Collections.emptySet(); + } + + // Check cross parameter validators + ConstraintCheckResult res = constraintHelper.checkCrossParameterTypes( elementType ); + if ( res == ConstraintCheckResult.MULTIPLE_VALIDATORS_FOUND ) { + return CollectionHelper.asSet( + new ConstraintCheckError( + element, + annotation, + "CROSS_PARAMETER_CONSTRAINT_MULTIPLE_VALIDATORS", + element.getSimpleName().toString() ) ); + } + else if ( res == ConstraintCheckResult.DISALLOWED ) { + return CollectionHelper.asSet( + new ConstraintCheckError( + element, + annotation, + "CROSS_PARAMETER_CONSTRAINT_VALIDATOR_HAS_INVALID_TYPE", + element.getSimpleName() ) ); + } + + // Check validationAppliesTo method + ExecutableElement validationAppliesTo = getValidationAppliesToMethod( element ); + + if ( validationAppliesTo == null && targets.size() > 1 ) { + // validationAppliesTo is required to let the user specify the constraint target + return CollectionHelper.asSet( + new ConstraintCheckError( + element, + annotation, + "CROSS_PARAMETER_VALIDATION_APPLIES_TO_REQUIRED", + element.getSimpleName() ) ); + } + + if ( validationAppliesTo != null ) { + + if ( !checkValidationAppliesToReturnType( validationAppliesTo ) ) { + return CollectionHelper.asSet( + new ConstraintCheckError( + element, + annotation, + "CROSS_PARAMETER_VALIDATION_APPLIES_TO_MUST_HAVE_CONSTRAINT_TARGET_RETURN_TYPE", + element.getSimpleName() ) ); + } + else if ( !checkValidationAppliesToDefaultValue( validationAppliesTo ) ) { + return CollectionHelper.asSet( + new ConstraintCheckError( + element, + annotation, + "CROSS_PARAMETER_VALIDATION_APPLIES_TO_MUST_HAVE_IMPLICIT_DEFAULT_VALUE", + element.getSimpleName() ) ); + } + + } + + return Collections.emptySet(); + } + + private boolean checkValidationAppliesToReturnType(ExecutableElement validationAppliesToMethod) { + + final DeclaredType constraintTargetType = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); + + // Check the return type + return validationAppliesToMethod.getReturnType().accept( new TypeKindVisitor6() { + + @Override + public Boolean visitDeclared(DeclaredType t, Void p) { + if ( typeUtils.isSameType( constraintTargetType, t ) ) { + return true; + } + + return false; + } + }, null ); + } + + private boolean checkValidationAppliesToDefaultValue(ExecutableElement validationAppliesToMethod) { + + final DeclaredType constraintTargetType = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); + + // Check the return type + return validationAppliesToMethod.getDefaultValue().accept( new SimpleAnnotationValueVisitor6() { + + @Override + public Boolean visitEnumConstant(VariableElement c, Void p) { + if ( typeUtils.isSameType( constraintTargetType, c.asType() ) ) { + return c.getSimpleName().contentEquals( "IMPLICIT" ); + } + return false; + } + }, null ); + } + + private ExecutableElement getValidationAppliesToMethod(Element annotation) { + for ( Element e : annotation.getEnclosedElements() ) { + ExecutableElement method = e.accept( new ElementKindVisitor6() { + + @Override + public ExecutableElement visitExecutableAsMethod(ExecutableElement e, Void p) { + if ( e.getSimpleName().contentEquals( "validationAppliesTo" ) ) { + return e; + } + return null; + } + }, null ); + + if ( method != null ) { + return method; + } + } + return null; + } + +} diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/GetterCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/GetterCheck.java index b2228f9bc1..1cc29ca643 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/GetterCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/GetterCheck.java @@ -37,13 +37,8 @@ public Set checkMethod(ExecutableElement element, ) ); } - else if ( !hasReturnValue( element ) ) { - return CollectionHelper.asSet( - new ConstraintCheckError( - element, annotation, "ONLY_NON_VOID_METHODS_MAY_BE_ANNOTATED" - ) - ); - } + + // HV-864: void methods support cross-parameter constraints. We do not enforce the check here. return Collections.emptySet(); } diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/MethodAnnotationCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/MethodAnnotationCheck.java new file mode 100644 index 0000000000..bc27e99ad1 --- /dev/null +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/MethodAnnotationCheck.java @@ -0,0 +1,92 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.checks; + +import java.util.Collections; +import java.util.Set; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeKind; + +import org.hibernate.validator.ap.util.CollectionHelper; +import org.hibernate.validator.ap.util.ConstraintHelper; +import org.hibernate.validator.ap.util.ConstraintHelper.AnnotationProcessorValidationTarget; + +/** + * Checks whether a method is correctly annotated with a valid constraint involving the return type or the method + * parameters (cross-parameters). + * + * @author Nicola Ferraro + */ +public class MethodAnnotationCheck extends AbstractConstraintCheck { + + private ConstraintHelper constraintHelper; + + public MethodAnnotationCheck(ConstraintHelper constraintHelper) { + this.constraintHelper = constraintHelper; + } + + public Set checkMethod(ExecutableElement element, + AnnotationMirror annotation) { + + // Annotations on methods/constructors can refer to return type or parameters (not both) + AnnotationProcessorValidationTarget target; + + // Constraint annotations can define a different validation target + if ( constraintHelper.isConstraintAnnotation( annotation.getAnnotationType().asElement() ) ) { + + Set supportedTargets = constraintHelper.getSupportedValidationTargets( annotation.getAnnotationType() ); + // at least one target is always returned + if ( supportedTargets.size() != 1 ) { + // when multiple targets are supported, the actual target must be disambiguated using a + // 'validationAppliesTo' property + // resolve the actual target depending on the element on which it is applied + target = constraintHelper.resolveValidationTarget( element, annotation ); + + if ( target == null ) { + return CollectionHelper.asSet( + new ConstraintCheckError( + element, annotation, "CROSS_PARAMETER_TARGET_NOT_INFERABLE", + annotation.getAnnotationType().asElement().getSimpleName() ) ); + } + + } + else { + // get the single validation target + target = supportedTargets.toArray( new AnnotationProcessorValidationTarget[1] )[0]; + } + } + else { + target = AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT; + } + + if ( target == AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT && !hasReturnValue( element ) ) { + return CollectionHelper.asSet( + new ConstraintCheckError( + element, annotation, "ONLY_NON_VOID_METHODS_MAY_BE_ANNOTATED" ) ); + } + + if ( target == AnnotationProcessorValidationTarget.PARAMETERS && !hasParameters( element ) ) { + return CollectionHelper.asSet( + new ConstraintCheckError( + element, annotation, "CROSS_PARAMETER_VALIDATION_ON_PARAMETERLESS_METHOD", + annotation.getAnnotationType().asElement().getSimpleName() ) ); + } + + return Collections.emptySet(); + } + + private boolean hasParameters(ExecutableElement method) { + return method.getParameters().size() > 0; + } + + private boolean hasReturnValue(ExecutableElement method) { + return method.getReturnType().getKind() != TypeKind.VOID; + } + +} diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/TypeCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/TypeCheck.java index de110e2c49..c1131b5173 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/TypeCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/checks/TypeCheck.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.Set; + import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -18,11 +19,11 @@ import org.hibernate.validator.ap.util.CollectionHelper; import org.hibernate.validator.ap.util.ConstraintHelper; import org.hibernate.validator.ap.util.ConstraintHelper.ConstraintCheckResult; +import org.hibernate.validator.ap.util.ConstraintHelper.AnnotationProcessorValidationTarget; /** - * Checks, that constraint annotations are only specified at elements - * of a type supported by the constraints. Applies to fields, methods and - * non-annotation type declarations. + * Checks, that constraint annotations are only specified at elements of a type supported by the constraints. Applies to + * fields, methods and non-annotation type declarations. * * @author Gunnar Morling */ @@ -36,15 +37,25 @@ public TypeCheck(ConstraintHelper constraintHelper) { @Override public Set checkField(VariableElement element, - AnnotationMirror annotation) { + AnnotationMirror annotation) { return checkInternal( element, annotation, element.asType(), "NOT_SUPPORTED_TYPE" ); } @Override public Set checkMethod(ExecutableElement element, - AnnotationMirror annotation) { + AnnotationMirror annotation) { + + AnnotationProcessorValidationTarget target = AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT; + if ( constraintHelper.isConstraintAnnotation( annotation.getAnnotationType().asElement() ) ) { + target = constraintHelper.resolveValidationTarget( element, annotation ); + } + + if ( target == AnnotationProcessorValidationTarget.PARAMETERS ) { + return Collections.emptySet(); + } + // check the return type return checkInternal( element, annotation, element.getReturnType(), "NOT_SUPPORTED_RETURN_TYPE" ); } @@ -56,18 +67,15 @@ public Set checkNonAnnotationType( } private Set checkInternal(Element element, - AnnotationMirror annotation, TypeMirror type, String messageKey) { + AnnotationMirror annotation, TypeMirror type, String messageKey) { if ( constraintHelper.checkConstraint( - annotation.getAnnotationType(), type - ) != ConstraintCheckResult.ALLOWED ) { + annotation.getAnnotationType(), type ) != ConstraintCheckResult.ALLOWED ) { return CollectionHelper.asSet( new ConstraintCheckError( element, annotation, messageKey, - annotation.getAnnotationType().asElement().getSimpleName() - ) - ); + annotation.getAnnotationType().asElement().getSimpleName() ) ); } return Collections.emptySet(); diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/util/ConstraintHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/util/ConstraintHelper.java index a30da81ab1..dc22ddd84e 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/util/ConstraintHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/util/ConstraintHelper.java @@ -10,18 +10,25 @@ import java.util.Calendar; import java.util.Collection; import java.util.Date; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; + import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementKindVisitor6; import javax.lang.model.util.SimpleAnnotationValueVisitor6; import javax.lang.model.util.TypeKindVisitor6; import javax.lang.model.util.Types; @@ -111,6 +118,42 @@ public enum AnnotationType { NO_CONSTRAINT_ANNOTATION } + /** + * Defines the object on which a validation is targeted. + */ + public enum AnnotationProcessorValidationTarget { + /** + * The validation targets the parameters of a method/constructor. + */ + PARAMETERS, + + /** + * The validation targets the value on which it is annotated or the return type of a method/constructor. + */ + ANNOTATED_ELEMENT + } + + /** + * The validation target of a constraint annotation. + */ + public enum AnnotationProcessorConstraintTarget { + /** + * Constraint applies to the parameters of a method or a constructor. + */ + PARAMETERS, + + /** + * Constraint applies to the return value of a method or a constructor. + */ + RETURN_VALUE, + + /** + * Discover the type when no ambiguity is present if neither on a method nor a constructor. + */ + IMPLICIT + } + + /** * Contains the supported types for given constraints. Keyed by constraint * annotation type names, each value is a set with the allowed types for the @@ -362,6 +405,118 @@ public Boolean visitDeclared(DeclaredType constraintValidatorImplementation, Voi ); } + /** + * Resolve the actual {@code AnnotationProcessorValidationTarget} of a constraint annotation, when applied to a method/constructor. + *

+ * When the annotation supports multiple {@link AnnotationProcessorValidationTarget}s (i.e. it is both cross-parameter and generic), the actual target is resolved using the + * 'validationAppliesTo()' attribute of the annotation. + * + * @param element the method/constructor on which the annotation is applied + * @param annotation the constraint annotation + * @return the resolved {@code AnnotationProcessorValidationTarget}, null if the target cannot be inferred + */ + public AnnotationProcessorValidationTarget resolveValidationTarget(ExecutableElement element, AnnotationMirror annotation) { + Set allowedTargets = getSupportedValidationTargets( annotation.getAnnotationType() ); + + // assume that at least one target (AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT) is present + + if(allowedTargets.size()==1) { + return allowedTargets.toArray( new AnnotationProcessorValidationTarget[1] )[0]; + } + + AnnotationProcessorConstraintTarget constrTarget = getConstraintTarget( annotation ); + if(constrTarget==null) { + return null; + } + + switch(constrTarget) { + case PARAMETERS: + return AnnotationProcessorValidationTarget.PARAMETERS; + case RETURN_VALUE: + return AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT; + case IMPLICIT: + default: + return resolveImplicitValidationTarget( element ); + } + } + + /** + * Returns the set of {@code AnnotationProcessorValidationTarget} supported by the given constraint annotation type. + *

+ * A constraint annotation can support {@link AnnotationProcessorValidationTarget#ANNOTATED_ELEMENT}, {@link AnnotationProcessorValidationTarget#PARAMETERS} or both. + * + * @param constraintAnnotationType the constraint annotation type + * @return the set of supported {@code AnnotationProcessorValidationTarget}s + */ + public Set getSupportedValidationTargets(DeclaredType constraintAnnotationType) { + + AnnotationMirror constraintMetaAnnotation = getConstraintMetaAnnotation( constraintAnnotationType ); + List validatorClassReferences = getValidatorClassesFromConstraintMetaAnnotation( constraintMetaAnnotation ); + + EnumSet supported = EnumSet.noneOf( AnnotationProcessorValidationTarget.class ); + + for ( AnnotationValue oneValidatorClassReference : validatorClassReferences ) { + supported.addAll( getSupportedValidationTargets( oneValidatorClassReference ) ); + } + + if(supported.isEmpty()) { + // case of built-in validation constraints + supported.add( AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT ); + } + + return supported; + } + + /** + * Check that a constraint has at most one cross-parameter validator that resolves to Object or Object[]. + * + * @param constraintAnnotationType the constraint type + * @return {@code ConstraintCheckResult#MULTIPLE_VALIDATORS_FOUND} if the constraint has more than one cross-parameter validator, + * {@code ConstraintCheckResult#DISALLOWED} if the constraint has one cross-parameter validator with a wrong generic type, + * {@code ConstraintCheckResult#ALLOWED} otherwise + */ + public ConstraintCheckResult checkCrossParameterTypes(DeclaredType constraintAnnotationType) { + + AnnotationMirror constraintMetaAnnotation = getConstraintMetaAnnotation( constraintAnnotationType ); + List validatorClassReferences = getValidatorClassesFromConstraintMetaAnnotation( constraintMetaAnnotation ); + + AnnotationValue crossParameterValidator = null; + for ( AnnotationValue oneValidatorClassReference : validatorClassReferences ) { + Set targets = getSupportedValidationTargets( oneValidatorClassReference ); + if( targets.contains( AnnotationProcessorValidationTarget.PARAMETERS ) ) { + if(crossParameterValidator!=null) { + return ConstraintCheckResult.MULTIPLE_VALIDATORS_FOUND; + } + crossParameterValidator = oneValidatorClassReference; + } + } + + if(crossParameterValidator!=null) { + + // Cross-parameter contraints must accept Object or Object[] as validated type + final TypeMirror objectMirror = annotationApiHelper.getMirrorForType( Object.class ); + + TypeMirror type = determineSupportedType( crossParameterValidator ); + Boolean supported = type.accept( new TypeKindVisitor6() { + @Override + public Boolean visitArray(ArrayType t, Void p) { + return typeUtils.isSameType( t.getComponentType(), objectMirror ); + } + + @Override + public Boolean visitDeclared(DeclaredType t, Void p) { + return typeUtils.isSameType( t, objectMirror ); + } + }, null ); + + if(!supported) { + return ConstraintCheckResult.DISALLOWED; + } + } + + return ConstraintCheckResult.ALLOWED; + } + // ================================== // private API below // ================================== @@ -531,8 +686,77 @@ private Set getSupportedTypes(DeclaredType constraintAnnotationType) return supportedTypes; } + private AnnotationProcessorValidationTarget resolveImplicitValidationTarget(ExecutableElement e) { + + if(e.getParameters().isEmpty()) { + return AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT; + } + else if(e.getReturnType().getKind()==TypeKind.VOID) { + return AnnotationProcessorValidationTarget.PARAMETERS; + } + + return null; + } + + /** + * Determines the {@code AnnotationProcessorConstraintTarget} of the annotation. + * + * @param annotation the cross-parameter annotation + * @return the {@code AnnotationProcessorConstraintTarget}, if it defined using the 'validationAppliesTo()' attribute of the annotation, the default value if a valid 'validationAppliesTo()' attribute is defined, or null + */ + private AnnotationProcessorConstraintTarget getConstraintTarget(AnnotationMirror annotation) { + + AnnotationValue validationAppliesTo = annotationApiHelper.getAnnotationValue( annotation, "validationAppliesTo" ); + if(validationAppliesTo==null) { + // validationAppliesTo not found on the annotation + + for(Element e : annotation.getAnnotationType().asElement().getEnclosedElements()) { + + Boolean isValidationAppliesToMethod = e.accept( new ElementKindVisitor6() { + @Override + public Boolean visitExecutableAsMethod(ExecutableElement e, Void p) { + if(e.getSimpleName().contentEquals( "validationAppliesTo" )) { + return true; + } + return false; + } + }, null ); + + if(Boolean.TRUE.equals( isValidationAppliesToMethod )) { + // validationAppliesTo method is present, so the default value is returned (IMPLICIT) + return AnnotationProcessorConstraintTarget.IMPLICIT; + } + } + + // cannot find the ConstraintTarget + return null; + } + + return validationAppliesTo.accept( + new SimpleAnnotationValueVisitor6() { + + private TypeMirror constraintTargetMirror = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); + + @Override + public AnnotationProcessorConstraintTarget visitEnumConstant(VariableElement c, Void p) { + if(typeUtils.isSameType( c.asType(), constraintTargetMirror )) { + return AnnotationProcessorConstraintTarget.valueOf( c.getSimpleName().toString() ); + } + return null; + } + + }, null + ); + } + + private Set determineSupportedTypes(DeclaredType constraintAnnotationType) { + return determineSupportedTypes( constraintAnnotationType, AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT ); + } + + private Set determineSupportedTypes(DeclaredType constraintAnnotationType, AnnotationProcessorValidationTarget target) { + //the Constraint meta-annotation at the type declaration, e.g. "@Constraint(validatedBy = CheckCaseValidator.class)" AnnotationMirror constraintMetaAnnotation = getConstraintMetaAnnotation( constraintAnnotationType ); @@ -545,28 +769,20 @@ private Set determineSupportedTypes(DeclaredType constraintAnnotatio for ( AnnotationValue oneValidatorClassReference : validatorClassReferences ) { - TypeMirror supportedType = getSupportedType( oneValidatorClassReference ); - supportedTypes.add( supportedType ); + if(isValidationTargetSupported( oneValidatorClassReference, target )) { + TypeMirror supportedType = determineSupportedType( oneValidatorClassReference); + supportedTypes.add( supportedType ); + } } return supportedTypes; } - private TypeMirror getSupportedType(AnnotationValue oneValidatorClassReference) { - - TypeMirror validatorType = oneValidatorClassReference.accept( - new SimpleAnnotationValueVisitor6() { - - @Override - public TypeMirror visitType(TypeMirror t, Void p) { - return t; - } - }, null - ); + private TypeMirror determineSupportedType(AnnotationValue validatorClassReference) { // contains the bindings of the type parameters from the implemented // ConstraintValidator interface, e.g. "ConstraintValidator" - TypeMirror constraintValidatorImplementation = getConstraintValidatorSuperType( validatorType ); + TypeMirror constraintValidatorImplementation = getConstraintValidatorSuperType( validatorClassReference ); return constraintValidatorImplementation.accept( new TypeKindVisitor6() { @@ -581,7 +797,76 @@ public TypeMirror visitDeclared(DeclaredType constraintValidatorImplementation, ); } - private TypeMirror getConstraintValidatorSuperType(TypeMirror type) { + private boolean isValidationTargetSupported(AnnotationValue oneValidatorClassReference, AnnotationProcessorValidationTarget target) { + return getSupportedValidationTargets( oneValidatorClassReference ).contains( target ); + } + + private Set getSupportedValidationTargets(AnnotationValue oneValidatorClassReference) { + // determine the class that could contain the @SupportedValidationTarget annotation. + TypeMirror validatorClass = oneValidatorClassReference.accept( + new SimpleAnnotationValueVisitor6() { + + @Override + public TypeMirror visitType(TypeMirror t, Void p) { + return t; + } + }, null + ); + + DeclaredType validatorType = validatorClass.accept( new TypeKindVisitor6() { + @Override + public DeclaredType visitDeclared(DeclaredType t, Void p) { + return t; + } + }, null ); + + DeclaredType supportedValidationTargetType = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.SUPPORTED_VALIDATION_TARGET ); + + AnnotationMirror supportedTargetDecl = null; + + for(AnnotationMirror mirr : validatorType.asElement().getAnnotationMirrors()) { + if(typeUtils.isSameType( mirr.getAnnotationType(), supportedValidationTargetType )) { + supportedTargetDecl = mirr; + break; + } + } + + EnumSet allowedTargets = EnumSet.noneOf( AnnotationProcessorValidationTarget.class ); + + if(supportedTargetDecl==null) { + // If @SupportedValidationTarget is not present, the ConstraintValidator targets the (returned) element annotated by the constraint. + allowedTargets.add( AnnotationProcessorValidationTarget.ANNOTATED_ELEMENT ); + } + else { + List values = annotationApiHelper.getAnnotationArrayValue( supportedTargetDecl, "value" ); + for(AnnotationValue val : values) { + AnnotationProcessorValidationTarget target = val.accept( new SimpleAnnotationValueVisitor6() { + @Override + public AnnotationProcessorValidationTarget visitEnumConstant(VariableElement c, Void p) { + return AnnotationProcessorValidationTarget.valueOf( c.getSimpleName().toString() ); + } + }, null ); + + allowedTargets.add( target ); + } + + } + + return allowedTargets; + } + + + private TypeMirror getConstraintValidatorSuperType(AnnotationValue oneValidatorClassReference) { + + TypeMirror type = oneValidatorClassReference.accept( + new SimpleAnnotationValueVisitor6() { + + @Override + public TypeMirror visitType(TypeMirror t, Void p) { + return t; + } + }, null + ); List superTypes = typeUtils.directSupertypes( type ); List nextSuperTypes = CollectionHelper.newArrayList(); @@ -602,7 +887,7 @@ private TypeMirror getConstraintValidatorSuperType(TypeMirror type) { } //HV-293: Actually this should never happen, as we can have only ConstraintValidator implementations - //here. The Eclipse JSR 269 implementation unfortunately doesn't always create the type hierarchy + //here. The Eclipse JSR 269 implementation unfortunately doesn't always create the type hierarchy //properly though. //TODO GM: create and report an isolated test case throw new IllegalStateException( "Expected type " + type + " to implement javax.validation.ConstraintValidator, but it doesn't." ); diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/util/TypeNames.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/util/TypeNames.java index 4a6e870666..0d90239b49 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/util/TypeNames.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/util/TypeNames.java @@ -20,6 +20,7 @@ public static class BeanValidationTypes { public static final String JAVAX_VALIDATION = "javax.validation"; public static final String CONSTRAINT = JAVAX_VALIDATION + ".Constraint"; + public static final String CONSTRAINT_TARGET = JAVAX_VALIDATION + ".ConstraintTarget"; public static final String CONSTRAINT_VALIDATOR = JAVAX_VALIDATION + ".ConstraintValidator"; public static final String GROUP_SEQUENCE = JAVAX_VALIDATION + ".GroupSequence"; public static final String PAYLOAD = JAVAX_VALIDATION + ".Payload"; @@ -40,6 +41,10 @@ public static class BeanValidationTypes { public static final String PAST = JAVAX_VALIDATION_CONSTRAINTS + ".Past"; public static final String PATTERN = JAVAX_VALIDATION_CONSTRAINTS + ".Pattern"; public static final String SIZE = JAVAX_VALIDATION_CONSTRAINTS + ".Size"; + + public static final String CONSTRAINTVALIDATION = "javax.validation.constraintvalidation"; + + public static final String SUPPORTED_VALIDATION_TARGET = CONSTRAINTVALIDATION + ".SupportedValidationTarget"; } public static class HibernateValidatorTypes { diff --git a/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties b/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties index 36fe35fe6f..2c69223406 100644 --- a/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties +++ b/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties @@ -26,3 +26,10 @@ GROUP_SEQUENCE_PROVIDER_ANNOTATION_VALUE_MUST_BE_AN_IMPLEMENTATION_CLASS=The ann GROUP_SEQUENCE_PROVIDER_ANNOTATION_VALUE_DEFINED_PROVIDER_CLASS_WITH_WRONG_TYPE=The annotation @GroupSequenceProvider defines a default group sequence provider for type {0} instead of {1} (super)type. GROUP_SEQUENCE_PROVIDER_ANNOTATION_VALUE_CLASS_MUST_HAVE_DEFAULT_CONSTRUCTOR=The annotation @GroupSequenceProvider defines a default group sequence provider class without a public default constructor. ONLY_NON_VOID_METHODS_MAY_BE_ANNOTATED=Void methods may not be annotated with constraint annotations. +CROSS_PARAMETER_TARGET_NOT_INFERABLE=The target of the constraint @{0} cannot be inferred. +CROSS_PARAMETER_CONSTRAINT_VALIDATOR_HAS_INVALID_TYPE=Validator for cross-parameter constraint @{0} does not validate Object nor Object[]. +CROSS_PARAMETER_CONSTRAINT_MULTIPLE_VALIDATORS=The constraint @{0} defines multiple cross-parameter validators. Only one is allowed. +CROSS_PARAMETER_VALIDATION_APPLIES_TO_REQUIRED=Constraints with generic as well as cross-parameter validators must define an attribute validationAppliesTo(), but constraint @{0} does not. +CROSS_PARAMETER_VALIDATION_APPLIES_TO_MUST_HAVE_IMPLICIT_DEFAULT_VALUE=Default value of the attribute validationAppliesTo() of the constraint @{0} must be ConstraintTarget\u0023IMPLICIT. +CROSS_PARAMETER_VALIDATION_APPLIES_TO_MUST_HAVE_CONSTRAINT_TARGET_RETURN_TYPE=Return type of the attribute validationAppliesTo() of the constraint @{0} must be javax.validation.ConstraintTarget. +CROSS_PARAMETER_VALIDATION_ON_PARAMETERLESS_METHOD=Cross-parameter constraint @{0} is illegally placed on a parameterless method or constructor. diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/AnnotationTypeValidationTest.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/AnnotationTypeValidationTest.java index 4ef1331030..f64b791cf3 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/AnnotationTypeValidationTest.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/AnnotationTypeValidationTest.java @@ -6,10 +6,13 @@ */ package org.hibernate.validator.ap; +import static org.hibernate.validator.ap.testutil.CompilerTestHelper.assertThatDiagnosticsMatch; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + import java.io.File; -import javax.tools.Diagnostic.Kind; -import org.testng.annotations.Test; +import javax.tools.Diagnostic.Kind; import org.hibernate.validator.ap.testmodel.constrainttypes.ConstraintsWithIllegalRetentionPolicies; import org.hibernate.validator.ap.testmodel.constrainttypes.ConstraintsWithIllegalTargets; @@ -19,10 +22,21 @@ import org.hibernate.validator.ap.testmodel.constrainttypes.ConstraintsWithoutValidator; import org.hibernate.validator.ap.testmodel.constrainttypes.DummyValidator; import org.hibernate.validator.ap.testmodel.constrainttypes.ValidCustomerNumber; +import org.hibernate.validator.ap.testmodel.crossparameters.DoubleValidatorConstraint; +import org.hibernate.validator.ap.testmodel.crossparameters.GenericCrossParameterValidator; +import org.hibernate.validator.ap.testmodel.crossparameters.GenericCrossParameterValidatorObjectArray; +import org.hibernate.validator.ap.testmodel.crossparameters.GenericNormalValidator; +import org.hibernate.validator.ap.testmodel.crossparameters.DoubleValidatorDummyValidator; +import org.hibernate.validator.ap.testmodel.crossparameters.InvalidValidator; +import org.hibernate.validator.ap.testmodel.crossparameters.InvalidValidatorConstraint; +import org.hibernate.validator.ap.testmodel.crossparameters.ValidCrossParameterAndNormalConstraint; +import org.hibernate.validator.ap.testmodel.crossparameters.ValidCrossParameterConstraint; +import org.hibernate.validator.ap.testmodel.crossparameters.ValidCrossParameterConstraintWithObjectArrayValidator; +import org.hibernate.validator.ap.testmodel.crossparameters.WrongValidationAppliesToConstraintWithInvalidDefault; +import org.hibernate.validator.ap.testmodel.crossparameters.WrongValidationAppliesToConstraintWithInvalidReturnType; +import org.hibernate.validator.ap.testmodel.crossparameters.WrongValidationAppliesToConstraintWithMissingAttribute; import org.hibernate.validator.ap.util.DiagnosticExpectation; - -import static org.hibernate.validator.ap.testutil.CompilerTestHelper.assertThatDiagnosticsMatch; -import static org.testng.Assert.assertFalse; +import org.testng.annotations.Test; /** * Test cases for {@link ConstraintValidationProcessor} testing the checking of constraint @@ -154,5 +168,115 @@ public void testThatConstraintAnnotationTypeWithMissingOrPayloadGroupsAttributeC new DiagnosticExpectation( Kind.ERROR, 132 ) ); } + + @Test + public void testThatConstraintAnnotationWithMultipleCrossParameterValidatorsCausesCompilationError() { + + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( DoubleValidatorConstraint.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidator.class ), + compilerHelper.getSourceFile( DoubleValidatorDummyValidator.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, sourceFiles); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 23 ) + ); + } + + @Test + public void testThatConstraintAnnotationWithInvalidCrossParameterValidatorsCausesCompilationError() { + + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( InvalidValidatorConstraint.class ), + compilerHelper.getSourceFile( InvalidValidator.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, sourceFiles); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 23 ) + ); + } + + @Test + public void testThatCrossParameterConstraintWithInvalidDefaultInValidationAppliesToCausesCompilationError() { + + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( WrongValidationAppliesToConstraintWithInvalidDefault.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidator.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, sourceFiles); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 24 ) + ); + } + + @Test + public void testThatCrossParameterConstraintWithInvalidReturnTypeInValidationAppliesToCausesCompilationError() { + + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( WrongValidationAppliesToConstraintWithInvalidReturnType.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidator.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, sourceFiles); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 23 ) + ); + } + + @Test + public void testThatCrossParameterConstraintWithoutRequiredValidationAppliesToCausesCompilationError() { + + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( WrongValidationAppliesToConstraintWithMissingAttribute.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidator.class ), + compilerHelper.getSourceFile( GenericNormalValidator.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, sourceFiles); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 26 ) + ); + } + + @Test + public void testThatValidCrossParameterConstraintsAreCompiledCorrectly() { + + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( ValidCrossParameterConstraint.class ), + compilerHelper.getSourceFile( ValidCrossParameterConstraintWithObjectArrayValidator.class ), + compilerHelper.getSourceFile( ValidCrossParameterAndNormalConstraint.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidator.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidatorObjectArray.class ), + compilerHelper.getSourceFile( GenericNormalValidator.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, sourceFiles); + + assertTrue( compilationResult ); + } } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java index 53bcee1919..a811275095 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java @@ -6,13 +6,16 @@ */ package org.hibernate.validator.ap; +import static org.hibernate.validator.ap.testutil.CompilerTestHelper.assertThatDiagnosticsMatch; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + import java.io.File; import java.util.EnumSet; + import javax.tools.Diagnostic; import javax.tools.Diagnostic.Kind; -import org.testng.annotations.Test; - import org.hibernate.validator.ap.testmodel.FieldLevelValidationUsingBuiltInConstraints; import org.hibernate.validator.ap.testmodel.MethodLevelValidationUsingBuiltInConstraints; import org.hibernate.validator.ap.testmodel.ModelWithDateConstraints; @@ -39,6 +42,12 @@ import org.hibernate.validator.ap.testmodel.composedconstraint2.ComposingConstraint2ValidatorForCalendar; import org.hibernate.validator.ap.testmodel.composedconstraint2.ComposingConstraint2ValidatorForCollection; import org.hibernate.validator.ap.testmodel.composedconstraint2.FieldLevelValidationUsingComplexComposedConstraint; +import org.hibernate.validator.ap.testmodel.crossparameters.GenericCrossParameterValidator; +import org.hibernate.validator.ap.testmodel.crossparameters.GenericCrossParameterValidatorObjectArray; +import org.hibernate.validator.ap.testmodel.crossparameters.GenericNormalValidator; +import org.hibernate.validator.ap.testmodel.crossparameters.MethodLevelValidationUsingCrossParameterConstraints; +import org.hibernate.validator.ap.testmodel.crossparameters.ValidCrossParameterAndNormalConstraint; +import org.hibernate.validator.ap.testmodel.crossparameters.ValidCrossParameterConstraint; import org.hibernate.validator.ap.testmodel.customconstraints.CaseMode; import org.hibernate.validator.ap.testmodel.customconstraints.CheckCase; import org.hibernate.validator.ap.testmodel.customconstraints.CheckCaseValidator; @@ -63,10 +72,7 @@ import org.hibernate.validator.ap.testmodel.nouniquevalidatorresolution.SizeValidatorForSet; import org.hibernate.validator.ap.testutil.CompilerTestHelper.Library; import org.hibernate.validator.ap.util.DiagnosticExpectation; - -import static org.hibernate.validator.ap.testutil.CompilerTestHelper.assertThatDiagnosticsMatch; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; +import org.testng.annotations.Test; /** * Miscellaneous tests for {@link ConstraintValidationProcessor}. @@ -542,4 +548,28 @@ public void timeConstraintsAllowedAtJava8DateTime() { assertTrue( compilationResult, "Java 8 date/time API types fails at @Future/@Past." ); } + + @Test + public void crossParameterConstraintsAllowed() { + + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( ValidCrossParameterAndNormalConstraint.class ), + compilerHelper.getSourceFile( ValidCrossParameterConstraint.class ), + compilerHelper.getSourceFile( MethodLevelValidationUsingCrossParameterConstraints.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidatorObjectArray.class ), + compilerHelper.getSourceFile( GenericCrossParameterValidator.class ), + compilerHelper.getSourceFile( GenericNormalValidator.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, false, true, sourceFiles); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 41 ), + new DiagnosticExpectation( Kind.ERROR, 63 ) + ); + + } } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorConstraint.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorConstraint.java new file mode 100644 index 0000000000..49a2114d2d --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorConstraint.java @@ -0,0 +1,33 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { GenericCrossParameterValidator.class, DoubleValidatorDummyValidator.class }) +@Documented +public @interface DoubleValidatorConstraint { + + String message() default "{DoubleValidatorConstraint.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorDummyValidator.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorDummyValidator.java new file mode 100644 index 0000000000..f4bd7bdc38 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/DoubleValidatorDummyValidator.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +@SupportedValidationTarget(ValidationTarget.PARAMETERS) +public class DoubleValidatorDummyValidator implements ConstraintValidator { + + @Override + public void initialize(final DoubleValidatorConstraint constraintAnnotation) { + } + + @Override + public boolean isValid(final Object value, final ConstraintValidatorContext context) { + // some validation logic + return true; + } + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidator.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidator.java new file mode 100644 index 0000000000..2dcac3bccc --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidator.java @@ -0,0 +1,29 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import java.lang.annotation.Annotation; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +@SupportedValidationTarget(ValidationTarget.PARAMETERS) +public class GenericCrossParameterValidator implements ConstraintValidator { + + @Override + public void initialize(final Annotation constraintAnnotation) { + } + + @Override + public boolean isValid(final Object value, final ConstraintValidatorContext context) { + // some validation logic + return true; + } + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidatorObjectArray.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidatorObjectArray.java new file mode 100644 index 0000000000..3e50da10fe --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericCrossParameterValidatorObjectArray.java @@ -0,0 +1,29 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import java.lang.annotation.Annotation; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +@SupportedValidationTarget(ValidationTarget.PARAMETERS) +public class GenericCrossParameterValidatorObjectArray implements ConstraintValidator { + + @Override + public void initialize(final Annotation constraintAnnotation) { + } + + @Override + public boolean isValid(final Object[] value, final ConstraintValidatorContext context) { + // some validation logic + return true; + } + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericNormalValidator.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericNormalValidator.java new file mode 100644 index 0000000000..b97b29f889 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/GenericNormalValidator.java @@ -0,0 +1,26 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import java.lang.annotation.Annotation; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class GenericNormalValidator implements ConstraintValidator { + + @Override + public void initialize(final Annotation constraintAnnotation) { + } + + @Override + public boolean isValid(final Object value, final ConstraintValidatorContext context) { + // some validation logic + return true; + } + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidator.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidator.java new file mode 100644 index 0000000000..b8486b1291 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidator.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +@SupportedValidationTarget(ValidationTarget.PARAMETERS) +public class InvalidValidator implements ConstraintValidator { + + @Override + public void initialize(final InvalidValidatorConstraint constraintAnnotation) { + } + + @Override + public boolean isValid(final String value, final ConstraintValidatorContext context) { + // some validation logic + return true; + } + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidatorConstraint.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidatorConstraint.java new file mode 100644 index 0000000000..5907c2c667 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/InvalidValidatorConstraint.java @@ -0,0 +1,33 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { InvalidValidator.class }) +@Documented +public @interface InvalidValidatorConstraint { + + String message() default "{InvalidValidatorConstraint.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/MethodLevelValidationUsingCrossParameterConstraints.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/MethodLevelValidationUsingCrossParameterConstraints.java new file mode 100644 index 0000000000..88e055184e --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/MethodLevelValidationUsingCrossParameterConstraints.java @@ -0,0 +1,84 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import javax.validation.ConstraintTarget; +import javax.validation.constraints.Size; + +public class MethodLevelValidationUsingCrossParameterConstraints { + + /** + * Allowed: single cross-parameter constraint. + */ + @ValidCrossParameterConstraint + public String returnString(Integer parameter, String anotherParameter) { + return parameter + anotherParameter; + } + + /** + * Allowed: mix of cross-parameter constraint and normal constraint. + */ + @Size(min = 5) + @ValidCrossParameterConstraint + public String getString(Integer parameter, String anotherParameter) { + return parameter + anotherParameter; + } + + /** + * Allowed: cross-parameter constraint on void method. + */ + @ValidCrossParameterConstraint + public void voidMethod(Integer parameter, String anotherParameter) { + } + + /** + * Not Allowed: cross-parameter constraint on void method without arguments. + */ + @ValidCrossParameterConstraint + public void methodWithoutArguments() { + } + + /** + * Allowed: cross-parameter and normal constraint with implicit target. + */ + @ValidCrossParameterAndNormalConstraint + public void constraintWithImplicitTarget(String param) { + } + + /** + * Allowed: cross-parameter and normal constraint with implicit target. + */ + @ValidCrossParameterAndNormalConstraint + public String constraintWithImplicitTarget() { + return null; + } + + /** + * Not Allowed: cross-parameter and normal constraint with implicit target that cannot be resolved. + */ + @ValidCrossParameterAndNormalConstraint + public String constraintWithImplicitTargetNoResolution(String param) { + return param; + } + + /** + * Allowed: cross-parameter and normal constraint with explicit target. + */ + @ValidCrossParameterAndNormalConstraint(validationAppliesTo = ConstraintTarget.PARAMETERS) + public String constraintWithExplicitTarget(String param) { + return param; + } + + /** + * Allowed: cross-parameter and normal constraint with explicit target. + */ + @ValidCrossParameterAndNormalConstraint(validationAppliesTo = ConstraintTarget.RETURN_VALUE) + public String constraintWithExplicitTarget2(String param) { + return param; + } + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterAndNormalConstraint.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterAndNormalConstraint.java new file mode 100644 index 0000000000..c69ae481d6 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterAndNormalConstraint.java @@ -0,0 +1,36 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.ConstraintTarget; +import javax.validation.Payload; + +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { GenericCrossParameterValidatorObjectArray.class, GenericNormalValidator.class }) +@Documented +public @interface ValidCrossParameterAndNormalConstraint { + + String message() default "{ValidCrossParameterConstraintWithObjectArrayValidator.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT; + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraint.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraint.java new file mode 100644 index 0000000000..e37b977156 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraint.java @@ -0,0 +1,33 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { GenericCrossParameterValidator.class }) +@Documented +public @interface ValidCrossParameterConstraint { + + String message() default "{ValidCrossParameterConstraint.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraintWithObjectArrayValidator.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraintWithObjectArrayValidator.java new file mode 100644 index 0000000000..1b3fa8def6 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/ValidCrossParameterConstraintWithObjectArrayValidator.java @@ -0,0 +1,33 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { GenericCrossParameterValidatorObjectArray.class }) +@Documented +public @interface ValidCrossParameterConstraintWithObjectArrayValidator { + + String message() default "{ValidCrossParameterConstraintWithObjectArrayValidator.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidDefault.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidDefault.java new file mode 100644 index 0000000000..e023f27722 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidDefault.java @@ -0,0 +1,37 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.ConstraintTarget; +import javax.validation.Payload; + +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { GenericCrossParameterValidator.class }) +@Documented +public @interface WrongValidationAppliesToConstraintWithInvalidDefault { + + String message() default "{WrongValidationAppliesToConstraintWithInvalidDefault.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + // Default value must be IMPLICIT + ConstraintTarget validationAppliesTo() default ConstraintTarget.PARAMETERS; + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidReturnType.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidReturnType.java new file mode 100644 index 0000000000..c17d882030 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithInvalidReturnType.java @@ -0,0 +1,35 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { GenericCrossParameterValidator.class }) +@Documented +public @interface WrongValidationAppliesToConstraintWithInvalidReturnType { + + String message() default "{WrongValidationAppliesToConstraintWithInvalidReturnType.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + String validationAppliesTo() default "IMPLICIT"; + +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithMissingAttribute.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithMissingAttribute.java new file mode 100644 index 0000000000..0b46d63729 --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/crossparameters/WrongValidationAppliesToConstraintWithMissingAttribute.java @@ -0,0 +1,36 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel.crossparameters; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +/** + * Two different validators are specified, so a 'validationAppliesTo' method is required. + */ +@Target({ ANNOTATION_TYPE, METHOD, CONSTRUCTOR }) +@Retention(RUNTIME) +@Constraint(validatedBy = { GenericCrossParameterValidator.class, GenericNormalValidator.class }) +@Documented +public @interface WrongValidationAppliesToConstraintWithMissingAttribute { + + String message() default "{WrongValidationAppliesToConstraintWithMissingAttribute.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} From 5e5da76d91244b99b99640e6845f4a6eebee3c0f Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 14 Jan 2016 08:43:26 +0100 Subject: [PATCH 029/189] HV-1045 Using WildFly 10.0.0.CR5 for testing; Updating to JodaTime 2.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 213b524776..97e0ed8893 100644 --- a/pom.xml +++ b/pom.xml @@ -86,9 +86,9 @@ 1.1.0.Final 1.1.0 2.5.5 - 2.1 + 2.7 1.8.3 - 10.0.0.CR2 + 10.0.0.CR5 1.1.9.Final 1.7.2 3.3.0.Final From 57e6035135d3ccb98a10f8f499e5920638a8ede7 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 15 Jan 2016 09:41:58 +0100 Subject: [PATCH 030/189] Updating readme.md and changelog.txt prior to 5.3.0.Alpha1 release --- README.md | 6 +++--- changelog.txt | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4148a80f5d..e82e8aba24 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Hibernate Validator -*Version: 5.2.2.Final, 29.09.2015* +*Version: 5.3.0.Alpha1, 15.01.2016* ## What is it? @@ -35,7 +35,7 @@ Logging will delegate any log requests to that provider. org.hibernate hibernate-validator - 5.2.2.Final + 5.3.0.Alpha1 You also need an API and implementation of the Unified Expression Language. These dependencies must be explicitly added in an SE environment. @@ -59,7 +59,7 @@ extension by adding the following dependency: org.hibernate hibernate-validator-cdi - 5.2.2.Final + 5.3.0.Alpha1 * _hibernate-validator-annotation-processor-<version>.jar_ is an optional jar which can be integrated with your build diff --git a/changelog.txt b/changelog.txt index 1ad2c6e843..51b156dbfc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,31 @@ Hibernate Validator Changelog ============================= +5.3.0.Alpha1 (15.01.2016) +------------------------- + +** New Feature + * [HV-872] - Implement optional relaxation of parameter validation consistent with Section 4.5.5 + * [HV-1020] - ConstraintValidatorContext should allow to attach arbitrary context information to a ConstrainViolation + +** Bug + * [HV-864] - Cross-parameter constraint is disallowed by Annotation Processor + * [HV-1019] - Copying PathImpl results in hashCode==0 + * [HV-1022] - Validator.validateValue does not work for JDK-8 TYPE_USE annotations + * [HV-1023] - Validator fails with Google App Engine due to use of restricted class java.util.ResourceBundle.Control + * [HV-1025] - Configuration streams not reusable on IBM JVM + * [HV-1035] - Wrong messages in Korean for NotNull and Null + +** Task + * [HV-1041] - Upgrade to jaxb2-maven-plugin 2.2 + * [HV-1045] - Use WildFly 10.0.0.CR5 for integration tests and update to JodaTime 2.7 + +** Improvement + * [HV-1021] - Monitor contention at ValidatorFactoryImpl + * [HV-1026] - Exceptions during execution of LoadClass don't provide enough context + * [HV-1037] - General code quality improvements + * [HV-1039] - Improve OSGi integration addressing class loading issues + 5.2.2.Final (29.09.2015) ------------------------- From 7945546f16f7c29db222797a4b6f458925319c51 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 15 Jan 2016 10:23:12 +0100 Subject: [PATCH 031/189] [maven-release-plugin] prepare release 5.3.0.Alpha1 --- annotation-processor/pom.xml | 2 +- cdi/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- engine-jdk8-tests/pom.xml | 2 +- engine/pom.xml | 2 +- integration/pom.xml | 2 +- osgi/integrationtest/pom.xml | 2 +- osgi/karaf-features/pom.xml | 2 +- osgi/pom.xml | 2 +- performance/pom.xml | 2 +- pom.xml | 4 ++-- tck-runner/pom.xml | 2 +- test-utils/pom.xml | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml index b57f4b76cf..5dfed4e3c4 100644 --- a/annotation-processor/pom.xml +++ b/annotation-processor/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/cdi/pom.xml b/cdi/pom.xml index 7f3cf0bfd9..926ad8d27f 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 16e2e2ec25..89f534591e 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index dec85332fc..c298bf3bc3 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/engine-jdk8-tests/pom.xml b/engine-jdk8-tests/pom.xml index a04dd88a2d..d7151f5170 100644 --- a/engine-jdk8-tests/pom.xml +++ b/engine-jdk8-tests/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 hibernate-validator-engine-jdk8-tests diff --git a/engine/pom.xml b/engine/pom.xml index 5b9b4be031..0e65abe44b 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/integration/pom.xml b/integration/pom.xml index 23eed0ee65..4aa0caa688 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/osgi/integrationtest/pom.xml b/osgi/integrationtest/pom.xml index 73af4ed1fa..fb6ac2a687 100644 --- a/osgi/integrationtest/pom.xml +++ b/osgi/integrationtest/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-osgi org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/osgi/karaf-features/pom.xml b/osgi/karaf-features/pom.xml index 3fe2274b66..ba84cdbe7e 100644 --- a/osgi/karaf-features/pom.xml +++ b/osgi/karaf-features/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-osgi org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/osgi/pom.xml b/osgi/pom.xml index 8f903a6d56..c596d3ce79 100644 --- a/osgi/pom.xml +++ b/osgi/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/performance/pom.xml b/performance/pom.xml index dd143f3b5d..d51983aa2d 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/pom.xml b/pom.xml index 97e0ed8893..1936305cf0 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 pom Hibernate Validator Aggregator @@ -750,7 +750,7 @@ scm:git:git://github.com/hibernate/hibernate-validator.git scm:git:git@github.com:hibernate/hibernate-validator.git http://github.com/hibernate/hibernate-validator - HEAD + 5.3.0.Alpha1 diff --git a/tck-runner/pom.xml b/tck-runner/pom.xml index 408a9606c3..b1098ae63e 100644 --- a/tck-runner/pom.xml +++ b/tck-runner/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 ../pom.xml diff --git a/test-utils/pom.xml b/test-utils/pom.xml index 5953422ae9..2f765413a7 100644 --- a/test-utils/pom.xml +++ b/test-utils/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.3.0-SNAPSHOT + 5.3.0.Alpha1 hibernate-validator-test-utils From 7a2f1b5c4ab71434868800adfe49ea26a37ef4de Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 15 Jan 2016 10:23:12 +0100 Subject: [PATCH 032/189] [maven-release-plugin] prepare for next development iteration --- annotation-processor/pom.xml | 2 +- cdi/pom.xml | 2 +- distribution/pom.xml | 2 +- documentation/pom.xml | 2 +- engine-jdk8-tests/pom.xml | 2 +- engine/pom.xml | 2 +- integration/pom.xml | 2 +- osgi/integrationtest/pom.xml | 2 +- osgi/karaf-features/pom.xml | 2 +- osgi/pom.xml | 2 +- performance/pom.xml | 2 +- pom.xml | 4 ++-- tck-runner/pom.xml | 2 +- test-utils/pom.xml | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml index 5dfed4e3c4..b57f4b76cf 100644 --- a/annotation-processor/pom.xml +++ b/annotation-processor/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/cdi/pom.xml b/cdi/pom.xml index 926ad8d27f..7f3cf0bfd9 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/distribution/pom.xml b/distribution/pom.xml index 89f534591e..16e2e2ec25 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/documentation/pom.xml b/documentation/pom.xml index c298bf3bc3..dec85332fc 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/engine-jdk8-tests/pom.xml b/engine-jdk8-tests/pom.xml index d7151f5170..a04dd88a2d 100644 --- a/engine-jdk8-tests/pom.xml +++ b/engine-jdk8-tests/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT hibernate-validator-engine-jdk8-tests diff --git a/engine/pom.xml b/engine/pom.xml index 0e65abe44b..5b9b4be031 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/integration/pom.xml b/integration/pom.xml index 4aa0caa688..23eed0ee65 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/osgi/integrationtest/pom.xml b/osgi/integrationtest/pom.xml index fb6ac2a687..73af4ed1fa 100644 --- a/osgi/integrationtest/pom.xml +++ b/osgi/integrationtest/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-osgi org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/osgi/karaf-features/pom.xml b/osgi/karaf-features/pom.xml index ba84cdbe7e..3fe2274b66 100644 --- a/osgi/karaf-features/pom.xml +++ b/osgi/karaf-features/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-osgi org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/osgi/pom.xml b/osgi/pom.xml index c596d3ce79..8f903a6d56 100644 --- a/osgi/pom.xml +++ b/osgi/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/performance/pom.xml b/performance/pom.xml index d51983aa2d..dd143f3b5d 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 1936305cf0..97e0ed8893 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT pom Hibernate Validator Aggregator @@ -750,7 +750,7 @@ scm:git:git://github.com/hibernate/hibernate-validator.git scm:git:git@github.com:hibernate/hibernate-validator.git http://github.com/hibernate/hibernate-validator - 5.3.0.Alpha1 + HEAD diff --git a/tck-runner/pom.xml b/tck-runner/pom.xml index b1098ae63e..408a9606c3 100644 --- a/tck-runner/pom.xml +++ b/tck-runner/pom.xml @@ -11,7 +11,7 @@ hibernate-validator-parent org.hibernate - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT ../pom.xml diff --git a/test-utils/pom.xml b/test-utils/pom.xml index 2f765413a7..5953422ae9 100644 --- a/test-utils/pom.xml +++ b/test-utils/pom.xml @@ -10,7 +10,7 @@ org.hibernate hibernate-validator-parent - 5.3.0.Alpha1 + 5.3.0-SNAPSHOT hibernate-validator-test-utils From 9ae4d3d39980c1abb81f12bd6016b83e50e12e9b Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Fri, 22 Jan 2016 12:10:08 +0100 Subject: [PATCH 033/189] HV-1050 Upgrading gmaven plugin version to fix Windows build issue --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 97e0ed8893..c77b708592 100644 --- a/pom.xml +++ b/pom.xml @@ -599,7 +599,7 @@ org.codehaus.gmaven gmaven-plugin - 1.4 + 1.5 org.apache.servicemix.tooling From 99a6487f0978f5356645498818d64fb8962a7bf6 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 21 Jan 2016 11:07:50 +0100 Subject: [PATCH 034/189] HV-1049 Ignoring annotations from "jdk.internal" package --- .../metadata/provider/AnnotationMetaDataProvider.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java index 04729ca6f4..b50923a76d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + import javax.validation.GroupSequence; import javax.validation.ParameterNameProvider; import javax.validation.Valid; @@ -564,6 +565,13 @@ private List> findClassLevelConstraints(Class bea protected List> findConstraintAnnotations(Member member, A annotation, ElementType type) { + // HV-1049 Methods of java.lang.Object (and potentially other JDK classes) are annotated with annotations such + // as jdk.internal.HotSpotIntrinsicCandidate on JDK 9; They cannot be constraint annotation so skip them right + // here, as for the proper check we'd need package access permission for "jdk.internal" + if ( annotation.annotationType().getPackage().getName().equals( "jdk.internal" ) ) { + return Collections.emptyList(); + } + List> constraintDescriptors = newArrayList(); List constraints = newArrayList(); From 03590270dc4fb69c5d55cdf2039826622852de10 Mon Sep 17 00:00:00 2001 From: Tomaz Cerar Date: Wed, 20 Jan 2016 18:29:48 +0100 Subject: [PATCH 035/189] HV-1048 Fix validator to make to work on JDK9 with project Verona --- .../validator/internal/util/Version.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/Version.java b/engine/src/main/java/org/hibernate/validator/internal/util/Version.java index e244b0c2ff..177cb8609c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/Version.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/Version.java @@ -6,6 +6,9 @@ */ package org.hibernate.validator.internal.util; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.hibernate.validator.internal.util.logging.LoggerFactory; /** @@ -13,6 +16,7 @@ * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2012 SERLI */ public final class Version { + private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile( "^(?:1\\.)?(\\d+)$" ); static { LoggerFactory.make().version( getVersionString() ); } @@ -30,10 +34,18 @@ public static void touch() { * @return the Java release as an integer (e.g. 8 for Java 8) */ public static int getJavaRelease() { - // Will return something like 1.8 - String[] specificationVersion = System.getProperty( "java.specification.version" ).split( "\\." ); + // Will return something like 1.8 or 9 + String vmVersionStr = System.getProperty( "java.specification.version" ); + + Matcher matcher = JAVA_VERSION_PATTERN.matcher( vmVersionStr ); //match 1. or + + if ( matcher.find() ) { + return Integer.valueOf( matcher.group( 1 ) ); + } + else { + throw new RuntimeException("Unknown version of jvm " + vmVersionStr); + } - return Integer.parseInt( specificationVersion[1] ); } // helper class should not have a public constructor From 46a0d85daef278b3f3fddb177ac4a4d46af3cd12 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 21 Jan 2016 09:34:09 +0100 Subject: [PATCH 036/189] HV-1048 Only warn if Java version cannot be determined --- .../validator/internal/util/Version.java | 30 ++++++++++----- .../validator/internal/util/logging/Log.java | 10 +++-- .../test/internal/util/VersionTest.java | 37 +++++++++++++++++++ 3 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/util/VersionTest.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/Version.java b/engine/src/main/java/org/hibernate/validator/internal/util/Version.java index 177cb8609c..41e2d5ae26 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/Version.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/Version.java @@ -9,6 +9,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; /** @@ -17,8 +18,16 @@ */ public final class Version { private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile( "^(?:1\\.)?(\\d+)$" ); + + private static Log LOG = LoggerFactory.make(); + + /** + * "java.specification.version" will have a value like 1.8 or 9 + */ + private static int JAVA_RELEASE = determineJavaRelease( System.getProperty( "java.specification.version" ) ); + static { - LoggerFactory.make().version( getVersionString() ); + LOG.version( getVersionString() ); } public static String getVersionString() { @@ -34,18 +43,21 @@ public static void touch() { * @return the Java release as an integer (e.g. 8 for Java 8) */ public static int getJavaRelease() { - // Will return something like 1.8 or 9 - String vmVersionStr = System.getProperty( "java.specification.version" ); + return JAVA_RELEASE; + } - Matcher matcher = JAVA_VERSION_PATTERN.matcher( vmVersionStr ); //match 1. or + public static int determineJavaRelease(String specificationVersion) { + if ( specificationVersion != null && !specificationVersion.trim().isEmpty() ) { + Matcher matcher = JAVA_VERSION_PATTERN.matcher( specificationVersion ); //match 1. or - if ( matcher.find() ) { - return Integer.valueOf( matcher.group( 1 ) ); - } - else { - throw new RuntimeException("Unknown version of jvm " + vmVersionStr); + if ( matcher.find() ) { + return Integer.valueOf( matcher.group( 1 ) ); + } } + // Cannot determine Java version; Assuming 1.6, not enabling the 1.8-only features + LOG.unknownJvmVersion( specificationVersion ); + return 6; } // helper class should not have a public constructor diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index 15a90248db..ff7cacae0a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Set; import java.util.regex.PatternSyntaxException; + import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintDefinitionException; import javax.validation.ConstraintTarget; @@ -23,15 +24,14 @@ import javax.validation.ValidationException; import javax.xml.stream.XMLStreamException; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; import org.jboss.logging.BasicLogger; import org.jboss.logging.annotations.Cause; import org.jboss.logging.annotations.LogMessage; import org.jboss.logging.annotations.Message; import org.jboss.logging.annotations.MessageLogger; -import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; -import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; - import static org.jboss.logging.Logger.Level.DEBUG; import static org.jboss.logging.Logger.Level.INFO; import static org.jboss.logging.Logger.Level.WARN; @@ -636,4 +636,8 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp @Message(id = 191, value = "Error creating unwrapper: %s") ValidationException validatedValueUnwrapperCannotBeCreated(String className, @Cause Exception e); + + @LogMessage(level = WARN) + @Message(id = 192, value = "Couldn't determine Java version from value %1s; Not enabling features requiring Java 8") + void unknownJvmVersion(String vmVersionStr); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/VersionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/VersionTest.java new file mode 100644 index 0000000000..ed33b7946c --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/VersionTest.java @@ -0,0 +1,37 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.util; + +import org.hibernate.validator.internal.util.Version; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +/** + * @author Gunnar Morling + */ +public class VersionTest { + + @Test + public void java8() { + assertEquals( Version.determineJavaRelease( "1.8" ), 8 ); + } + + @Test + public void java9() { + assertEquals( Version.determineJavaRelease( "9" ), 9 ); + } + + @Test + public void unknown() { + assertEquals( Version.determineJavaRelease( "abc" ), 6 ); + } + + @Test + public void nullValue() { + assertEquals( Version.determineJavaRelease( null ), 6 ); + } +} From e9c9987f3ada1932ebc1d283a80c42e1c032f43b Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 21 Jan 2016 09:19:19 +0100 Subject: [PATCH 037/189] HV-1034 Allow compilation using JDK9 --- .../ap/ConstraintValidationProcessorTest.java | 22 ++++----- .../ap/testmodel/ModelWithJodaTypes.java | 11 +++-- ...ateValidatorProvidedCustomConstraints.java | 1 + .../interceptor/ValidationInterceptor.java | 3 +- engine/pom.xml | 46 +++++++++++++++++++ .../aggregated/ReturnValueMetaData.java | 2 +- .../ValidatableParametersMetaData.java | 5 +- .../metadata/raw/BeanConfiguration.java | 5 +- .../internal/util/CollectionHelper.java | 2 +- .../util/privilegedactions/LoadClass.java | 2 +- .../ParameterScriptAssertValidatorTest.java | 8 ++-- 11 files changed, 76 insertions(+), 31 deletions(-) diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java index a811275095..03cc16fd51 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java @@ -6,10 +6,6 @@ */ package org.hibernate.validator.ap; -import static org.hibernate.validator.ap.testutil.CompilerTestHelper.assertThatDiagnosticsMatch; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - import java.io.File; import java.util.EnumSet; @@ -74,6 +70,10 @@ import org.hibernate.validator.ap.util.DiagnosticExpectation; import org.testng.annotations.Test; +import static org.hibernate.validator.ap.testutil.CompilerTestHelper.assertThatDiagnosticsMatch; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + /** * Miscellaneous tests for {@link ConstraintValidationProcessor}. * @@ -101,7 +101,6 @@ diagnostics, new DiagnosticExpectation( Kind.ERROR, 43 ), new DiagnosticExpectat */ @Test public void hibernateValidatorProvidedCustomConstraints() { - File sourceFile = compilerHelper.getSourceFile( HibernateValidatorProvidedCustomConstraints.class ); boolean compilationResult = @@ -110,7 +109,6 @@ public void hibernateValidatorProvidedCustomConstraints() { assertFalse( compilationResult ); assertThatDiagnosticsMatch( diagnostics, - new DiagnosticExpectation( Kind.ERROR, 54 ), new DiagnosticExpectation( Kind.ERROR, 55 ), new DiagnosticExpectation( Kind.ERROR, 56 ), new DiagnosticExpectation( Kind.ERROR, 57 ), @@ -124,8 +122,8 @@ public void hibernateValidatorProvidedCustomConstraints() { new DiagnosticExpectation( Kind.ERROR, 65 ), new DiagnosticExpectation( Kind.ERROR, 66 ), new DiagnosticExpectation( Kind.ERROR, 67 ), - new DiagnosticExpectation( Kind.ERROR, 68 ) - + new DiagnosticExpectation( Kind.ERROR, 68 ), + new DiagnosticExpectation( Kind.ERROR, 69 ) ); } @@ -532,8 +530,8 @@ public void timeConstraintsAllowedAtJodaTypes() { assertFalse( compilationResult ); assertThatDiagnosticsMatch( diagnostics, - new DiagnosticExpectation( Kind.ERROR, 49 ), - new DiagnosticExpectation( Kind.ERROR, 50 ) + new DiagnosticExpectation( Kind.ERROR, 50 ), + new DiagnosticExpectation( Kind.ERROR, 51 ) ); } @@ -548,7 +546,7 @@ public void timeConstraintsAllowedAtJava8DateTime() { assertTrue( compilationResult, "Java 8 date/time API types fails at @Future/@Past." ); } - + @Test public void crossParameterConstraintsAllowed() { @@ -570,6 +568,6 @@ public void crossParameterConstraintsAllowed() { new DiagnosticExpectation( Kind.ERROR, 41 ), new DiagnosticExpectation( Kind.ERROR, 63 ) ); - + } } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJodaTypes.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJodaTypes.java index 77d312ac96..bd71d2b62a 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJodaTypes.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJodaTypes.java @@ -17,16 +17,17 @@ import org.joda.time.ReadableInstant; import org.joda.time.ReadablePartial; +@SuppressWarnings("deprecation") public class ModelWithJodaTypes { @Past @Future public Date jdkDate; - + @Past @Future public GregorianCalendar jdkCalendar; - + @Past @Future public ReadableInstant jodaInstant; @@ -34,7 +35,7 @@ public class ModelWithJodaTypes { @Past @Future public DateMidnight jodaDateMidnight; - + @Past @Future public ReadablePartial jodaPartial; @@ -42,12 +43,12 @@ public class ModelWithJodaTypes { @Past @Future public LocalDate jodaLocalDate; - + /** * Not allowed. */ @Future @Past public String string; - + } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java index 244cc53b42..6ea84c20f3 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java @@ -26,6 +26,7 @@ import org.hibernate.validator.constraints.br.TituloEleitoral; @ScriptAssert(script = "", lang = "javascript") +@SuppressWarnings("deprecation") public class HibernateValidatorProvidedCustomConstraints { /** diff --git a/cdi/src/main/java/org/hibernate/validator/internal/cdi/interceptor/ValidationInterceptor.java b/cdi/src/main/java/org/hibernate/validator/internal/cdi/interceptor/ValidationInterceptor.java index 9b07134484..7ec951fb02 100644 --- a/cdi/src/main/java/org/hibernate/validator/internal/cdi/interceptor/ValidationInterceptor.java +++ b/cdi/src/main/java/org/hibernate/validator/internal/cdi/interceptor/ValidationInterceptor.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.Set; + import javax.annotation.Priority; import javax.inject.Inject; import javax.interceptor.AroundConstruct; @@ -104,7 +105,7 @@ public Object validateMethodInvocation(InvocationContext ctx) throws Exception { @AroundConstruct public void validateConstructorInvocation(InvocationContext ctx) throws Exception { ExecutableValidator executableValidator = validator.forExecutables(); - Set> violations = executableValidator.validateConstructorParameters( + Set> violations = executableValidator.validateConstructorParameters( ctx.getConstructor(), ctx.getParameters() ); diff --git a/engine/pom.xml b/engine/pom.xml index 5b9b4be031..bc5b0ef55a 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -295,4 +295,50 @@ + + + jdk9 + + 9 + + + + + + + + org.codehaus.mojo + jaxb2-maven-plugin + + + org.glassfish.jaxb + codemodel + 2.2.11 + + + org.glassfish.jaxb + txw2 + 2.2.11 + + + com.sun.xsom + xsom + 20140925 + + + com.sun.istack + 2.21 + istack-commons-runtime + + + com.sun.xml.bind.external + rngom + 2.2.11 + + + + + + + diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java index d9047c1d39..ad0f155d8e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ReturnValueMetaData.java @@ -59,7 +59,7 @@ public ReturnValueMetaData(Type type, ); this.typeArgumentsConstraints = Collections.unmodifiableSet( typeArgumentsConstraints ); - this.cascadables = Collections.unmodifiableList( isCascading ? Arrays.asList( this ) : Collections.emptyList() ); + this.cascadables = Collections.unmodifiableList( isCascading ? Arrays.asList( this ) : Collections.emptyList() ); this.groupConversionHelper = new GroupConversionHelper( groupConversions ); this.groupConversionHelper.validateGroupConversions( isCascading(), this.toString() ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ValidatableParametersMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ValidatableParametersMetaData.java index 9324b0c764..2df1c8421f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ValidatableParametersMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ValidatableParametersMetaData.java @@ -8,8 +8,7 @@ import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.facets.Validatable; - -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import org.hibernate.validator.internal.util.CollectionHelper; /** * Represents the constraint related meta data of the arguments of a method or @@ -22,7 +21,7 @@ public class ValidatableParametersMetaData implements Validatable { private final Iterable cascadables; public ValidatableParametersMetaData(Iterable cascadables) { - this.cascadables = newHashSet( cascadables ); + this.cascadables = CollectionHelper.newHashSet( cascadables ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/BeanConfiguration.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/BeanConfiguration.java index 98b0f6a36f..c126eab0db 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/BeanConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/raw/BeanConfiguration.java @@ -9,10 +9,9 @@ import java.util.List; import java.util.Set; +import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - /** * Represents the complete constraint related configuration of one Java type * originating from one {@link ConfigurationSource}. Contains meta-data on @@ -54,7 +53,7 @@ public BeanConfiguration( this.source = source; this.beanClass = beanClass; - this.constrainedElements = newHashSet( constrainedElements ); + this.constrainedElements = CollectionHelper.newHashSet( constrainedElements ); this.defaultGroupSequence = defaultGroupSequence; this.defaultGroupSequenceProvider = defaultGroupSequenceProvider; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java index f4d739878b..3937106c2f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/CollectionHelper.java @@ -58,7 +58,7 @@ public static HashSet newHashSet(Collection c) { } public static HashSet newHashSet(Collection s1, Collection s2) { - HashSet set = newHashSet( s1 ); + HashSet set = CollectionHelper.newHashSet( s1 ); set.addAll( s2 ); return set; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java index 5ccf24c869..c3662c12b2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/LoadClass.java @@ -68,7 +68,7 @@ public Class run() { // HV-363 - library internal classes are loaded via Class.forName first private Class loadClassInValidatorNameSpace() { final ClassLoader loader = HibernateValidator.class.getClassLoader(); - final Exception exception; + Exception exception; try { return Class.forName( className, true, HibernateValidator.class.getClassLoader() ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ParameterScriptAssertValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ParameterScriptAssertValidatorTest.java index 981cafd26d..7de07435aa 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ParameterScriptAssertValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ParameterScriptAssertValidatorTest.java @@ -10,15 +10,15 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.Set; + import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.executable.ExecutableValidator; -import org.testng.annotations.Test; - import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; import static org.hibernate.validator.testutil.ValidatorUtil.getConfiguration; @@ -78,10 +78,10 @@ public void shouldApplyParameterNameProvider() { public void shouldValidateParameterScriptAssertConstraintOnConstructor() throws Exception { ExecutableValidator executableValidator = getValidator().forExecutables(); - Constructor constructor = CalendarServiceImpl.class.getConstructor( String.class ); + Constructor constructor = CalendarServiceImpl.class.getConstructor( String.class ); Object[] parameterValues = new Object[] { "Foo" }; - Set> violations = executableValidator.validateConstructorParameters( + Set> violations = executableValidator.validateConstructorParameters( constructor, parameterValues ); From 419f46bb939b8cebe87608258a192af7a6aa9c5f Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Mon, 1 Feb 2016 09:30:08 +0100 Subject: [PATCH 038/189] HV-1052 Upgrading to JBoss Logging processor 2.0.1 --- .../hibernate/validator/internal/util/logging/Messages.java | 4 ++-- pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java index 5fa4787693..eafa33aaf6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java @@ -6,8 +6,8 @@ */ package org.hibernate.validator.internal.util.logging; -import org.jboss.logging.Message; -import org.jboss.logging.MessageBundle; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageBundle; /** * @author Hardy Ferentschik diff --git a/pom.xml b/pom.xml index c77b708592..e024d641b1 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ 1.7.2 3.3.0.Final 2.2.4 - 1.2.0.Final + 2.0.1.Final From 26619625f689f1713efa11f7cb56a8521972b19d Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 12 Feb 2016 12:07:39 +0100 Subject: [PATCH 039/189] HV-1055 Avoiding some object allocations --- .../internal/engine/groups/ValidationOrderGenerator.java | 6 +++--- .../internal/metadata/aggregated/BeanMetaDataImpl.java | 9 +++++++-- .../groups/validationorder/ValidationOrderTest.java | 8 ++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java index d9055f4f49..0bc77395ac 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; + import javax.validation.GroupSequence; import javax.validation.groups.Default; @@ -34,7 +35,7 @@ public class ValidationOrderGenerator { public ValidationOrderGenerator() { validationOrderForDefaultGroup = new DefaultValidationOrder(); - validationOrderForDefaultGroup.insertGroup( new Group( Default.class ) ); + validationOrderForDefaultGroup.insertGroup( Group.DEFAULT_GROUP ); } /** @@ -85,8 +86,7 @@ public ValidationOrder getValidationOrder(Collection> groups) { DefaultValidationOrder validationOrder = new DefaultValidationOrder(); for ( Class clazz : groups ) { if ( Default.class.equals( clazz ) ) { // HV-621 - Group group = new Group( clazz ); - validationOrder.insertGroup( group ); + validationOrder.insertGroup( Group.DEFAULT_GROUP ); } else if ( isGroupSequence( clazz ) ) { insertSequence( clazz, validationOrder ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index e296548ee9..d48c064585 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -10,12 +10,12 @@ import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; + import javax.validation.ElementKind; import javax.validation.groups.Default; import javax.validation.metadata.BeanDescriptor; @@ -64,6 +64,11 @@ public final class BeanMetaDataImpl implements BeanMetaData { private static final Log log = LoggerFactory.make(); + /** + * Represents the "sequence" of just Default.class. + */ + private static final List> DEFAULT_GROUP_SEQUENCE = Collections.>singletonList( Default.class ); + /** * The root bean class for this meta data. */ @@ -341,7 +346,7 @@ else if ( defaultGroupSequence != null && !defaultGroupSequence.isEmpty() ) { setDefaultGroupSequence( defaultGroupSequence ); } else { - setDefaultGroupSequence( Arrays.>asList( beanClass ) ); + this.defaultGroupSequence = DEFAULT_GROUP_SEQUENCE; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/validationorder/ValidationOrderTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/validationorder/ValidationOrderTest.java index 69946351a0..4c96652f8a 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/validationorder/ValidationOrderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/validationorder/ValidationOrderTest.java @@ -8,14 +8,14 @@ import java.util.ArrayList; import java.util.List; + import javax.validation.GroupDefinitionException; import javax.validation.groups.Default; -import org.testng.annotations.Test; - import org.hibernate.validator.internal.engine.groups.DefaultValidationOrder; import org.hibernate.validator.internal.engine.groups.Group; import org.hibernate.validator.internal.engine.groups.Sequence; +import org.testng.annotations.Test; import static org.testng.FileAssert.fail; @@ -29,7 +29,7 @@ public void testAssertDefaultGroupSequenceIsExpandableWithDefaultAtEndOfSequence Group a = new Group( GroupA.class ); Group b = new Group( GroupB.class ); Group c = new Group( GroupC.class ); - Group defaultGroup = new Group( Default.class ); + Group defaultGroup = Group.DEFAULT_GROUP; List sequence = new ArrayList(); sequence.add( a ); @@ -87,7 +87,7 @@ public void testAssertDefaultGroupSequenceIsExpandableWithDefaultAtBeginningOfSe Group a = new Group( GroupA.class ); Group b = new Group( GroupB.class ); Group c = new Group( GroupC.class ); - Group defaultGroup = new Group( Default.class ); + Group defaultGroup = Group.DEFAULT_GROUP; List sequence = new ArrayList(); sequence.add( defaultGroup ); From 60d23e7a8136b0a0f5f8315d7ed75275f3c95d12 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Tue, 16 Feb 2016 12:41:48 +0100 Subject: [PATCH 040/189] HV-1057 When validating a sequence of groups which extend other groups, only stop validation after one such "group of groups" has been fully processed --- .../internal/engine/ValidatorImpl.java | 112 ++++++++++-------- .../engine/groups/GroupWithInheritance.java | 30 +++++ .../internal/engine/groups/Sequence.java | 39 ++++-- .../inheritance/GroupInheritanceTest.java | 9 +- 4 files changed, 125 insertions(+), 65 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/groups/GroupWithInheritance.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index ab1bdbf9fe..308107b160 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -38,6 +38,7 @@ import org.hibernate.validator.internal.engine.ValidationContext.ValidationContextBuilder; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.groups.GroupWithInheritance; import org.hibernate.validator.internal.engine.groups.Group; import org.hibernate.validator.internal.engine.groups.Sequence; import org.hibernate.validator.internal.engine.groups.ValidationOrder; @@ -424,20 +425,22 @@ private Set> validateInContext(ValueContext sequenceIterator = validationOrder.getSequenceIterator(); while ( sequenceIterator.hasNext() ) { Sequence sequence = sequenceIterator.next(); - for ( Group group : sequence.getComposingGroups() ) { + for ( GroupWithInheritance groupOfGroups : sequence ) { int numberOfViolations = context.getFailingConstraints().size(); - valueContext.setCurrentGroup( group.getDefiningClass() ); - validateConstraintsForCurrentGroup( context, valueContext ); - if ( shouldFailFast( context ) ) { - return context.getFailingConstraints(); - } + for ( Group group : groupOfGroups ) { + valueContext.setCurrentGroup( group.getDefiningClass() ); - validateCascadedConstraints( context, valueContext ); - if ( shouldFailFast( context ) ) { - return context.getFailingConstraints(); - } + validateConstraintsForCurrentGroup( context, valueContext ); + if ( shouldFailFast( context ) ) { + return context.getFailingConstraints(); + } + validateCascadedConstraints( context, valueContext ); + if ( shouldFailFast( context ) ) { + return context.getFailingConstraints(); + } + } if ( context.getFailingConstraints().size() > numberOfViolations ) { break; } @@ -828,13 +831,18 @@ private Set> validatePropertyInContext(ValidationCont Iterator sequenceIterator = validationOrder.getSequenceIterator(); while ( sequenceIterator.hasNext() ) { Sequence sequence = sequenceIterator.next(); - for ( Group group : sequence.getComposingGroups() ) { - valueContext.setCurrentGroup( group.getDefiningClass() ); - int numberOfConstraintViolations = validatePropertyForCurrentGroup( - valueContext, context, metaConstraints, typeUseConstraints - ); - if ( shouldFailFast( context ) ) { - return context.getFailingConstraints(); + + for ( GroupWithInheritance groupOfGroups : sequence ) { + int numberOfConstraintViolations = 0; + + for ( Group group : groupOfGroups ) { + valueContext.setCurrentGroup( group.getDefiningClass() ); + numberOfConstraintViolations += validatePropertyForCurrentGroup( + valueContext, context, metaConstraints, typeUseConstraints + ); + if ( shouldFailFast( context ) ) { + return context.getFailingConstraints(); + } } if ( numberOfConstraintViolations > 0 ) { break; @@ -884,13 +892,16 @@ private Set> validateValueInContext(ValidationContext Iterator sequenceIterator = validationOrder.getSequenceIterator(); while ( sequenceIterator.hasNext() ) { Sequence sequence = sequenceIterator.next(); - for ( Group group : sequence.getComposingGroups() ) { - valueContext.setCurrentGroup( group.getDefiningClass() ); - int numberOfConstraintViolations = validatePropertyForCurrentGroup( - valueContext, context, metaConstraints, typeArgumentConstraints - ); - if ( shouldFailFast( context ) ) { - return context.getFailingConstraints(); + for ( GroupWithInheritance groupOfGroups : sequence ) { + int numberOfConstraintViolations = 0; + for ( Group group : groupOfGroups ) { + valueContext.setCurrentGroup( group.getDefiningClass() ); + numberOfConstraintViolations += validatePropertyForCurrentGroup( + valueContext, context, metaConstraints, typeArgumentConstraints + ); + if ( shouldFailFast( context ) ) { + return context.getFailingConstraints(); + } } if ( numberOfConstraintViolations > 0 ) { break; @@ -1095,19 +1106,23 @@ private void validateParametersInContext(ValidationContext validationCont Iterator sequenceIterator = validationOrder.getSequenceIterator(); while ( sequenceIterator.hasNext() ) { Sequence sequence = sequenceIterator.next(); - for ( Group group : sequence.getComposingGroups() ) { - int numberOfFailingConstraint = validateParametersForGroup( - validationContext, parameterValues, group - ); - if ( shouldFailFast( validationContext ) ) { - return; - } + for ( GroupWithInheritance groupOfGroups : sequence ) { + int numberOfFailingConstraint = 0; - cascadingValueContext.setCurrentGroup( group.getDefiningClass() ); - validateCascadedConstraints( validationContext, cascadingValueContext ); + for ( Group group : groupOfGroups ) { + numberOfFailingConstraint += validateParametersForGroup( + validationContext, parameterValues, group + ); + if ( shouldFailFast( validationContext ) ) { + return; + } - if ( shouldFailFast( validationContext ) ) { - return; + cascadingValueContext.setCurrentGroup( group.getDefiningClass() ); + validateCascadedConstraints( validationContext, cascadingValueContext ); + + if ( shouldFailFast( validationContext ) ) { + return; + } } if ( numberOfFailingConstraint > 0 ) { @@ -1294,21 +1309,24 @@ private void validateReturnValueInContext(ValidationContext context, T Iterator sequenceIterator = validationOrder.getSequenceIterator(); while ( sequenceIterator.hasNext() ) { Sequence sequence = sequenceIterator.next(); - for ( Group group : sequence.getComposingGroups() ) { - int numberOfFailingConstraint = validateReturnValueForGroup( - context, bean, value, group - ); - if ( shouldFailFast( context ) ) { - return; - } - - if ( value != null ) { - cascadingValueContext.setCurrentGroup( group.getDefiningClass() ); - validateCascadedConstraints( context, cascadingValueContext ); - + for ( GroupWithInheritance groupOfGroups : sequence ) { + int numberOfFailingConstraint = 0; + for ( Group group : groupOfGroups ) { + numberOfFailingConstraint += validateReturnValueForGroup( + context, bean, value, group + ); if ( shouldFailFast( context ) ) { return; } + + if ( value != null ) { + cascadingValueContext.setCurrentGroup( group.getDefiningClass() ); + validateCascadedConstraints( context, cascadingValueContext ); + + if ( shouldFailFast( context ) ) { + return; + } + } } if ( numberOfFailingConstraint > 0 ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/GroupWithInheritance.java b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/GroupWithInheritance.java new file mode 100644 index 0000000000..b82d742051 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/GroupWithInheritance.java @@ -0,0 +1,30 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.engine.groups; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +/** + * Represents a validation group and all the groups it extends ("group inheritance"). + * + * @author Gunnar Morling + */ +public class GroupWithInheritance implements Iterable { + + private final Set groups; + + public GroupWithInheritance(Set groups) { + this.groups = Collections.unmodifiableSet( groups ); + } + + @Override + public Iterator iterator() { + return groups.iterator(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java index 65cb2dbd0b..cc017d2267 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java @@ -6,25 +6,28 @@ */ package org.hibernate.validator.internal.engine.groups; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; + import javax.validation.GroupSequence; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - /** * Models a group sequence. * * @author Hardy Ferentschik */ -public class Sequence { +public class Sequence implements Iterable { private static final Log log = LoggerFactory.make(); private final Class sequence; private List groups; - private boolean inheritedGroupsExpanded = false; + private List expandedGroups; public Sequence(Class sequence, List groups) { this.groups = groups; @@ -40,17 +43,29 @@ public Class getDefiningClass() { } public void expandInheritedGroups() { - if ( inheritedGroupsExpanded ) { + if ( expandedGroups != null ) { return; } - List expandedGroups = newArrayList(); + expandedGroups = new ArrayList(); + ArrayList tmpGroups = new ArrayList(); + for ( Group group : groups ) { - expandedGroups.add( group ); - addInheritedGroups( group, expandedGroups ); + HashSet groupsOfGroup = new HashSet(); + + groupsOfGroup.add( group ); + addInheritedGroups( group, groupsOfGroup ); + + expandedGroups.add( new GroupWithInheritance( groupsOfGroup ) ); + tmpGroups.addAll( groupsOfGroup ); } - groups = expandedGroups; - inheritedGroupsExpanded = true; + + groups = tmpGroups; + } + + @Override + public Iterator iterator() { + return expandedGroups.iterator(); } @Override @@ -97,7 +112,7 @@ public String toString() { * @param group the group for which the inherited groups need to be added to {@code expandedGroups} * @param expandedGroups The list into which to add all groups */ - private void addInheritedGroups(Group group, List expandedGroups) { + private void addInheritedGroups(Group group, Set expandedGroups) { for ( Class inheritedGroup : group.getDefiningClass().getInterfaces() ) { if ( isGroupSequence( inheritedGroup ) ) { throw log.getSequenceDefinitionsNotAllowedException(); @@ -112,5 +127,3 @@ private boolean isGroupSequence(Class clazz) { return clazz.getAnnotation( GroupSequence.class ) != null; } } - - diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/inheritance/GroupInheritanceTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/inheritance/GroupInheritanceTest.java index 28c8b50f95..9114a30882 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/inheritance/GroupInheritanceTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/inheritance/GroupInheritanceTest.java @@ -7,12 +7,12 @@ package org.hibernate.validator.test.internal.engine.groups.inheritance; import java.util.Set; + import javax.validation.ConstraintViolation; import javax.validation.Validator; -import org.testng.annotations.Test; - import org.hibernate.validator.testutil.ValidatorUtil; +import org.testng.annotations.Test; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations; @@ -23,17 +23,16 @@ public class GroupInheritanceTest { /** - * HV-288 + * HV-288, HV-1057. */ @Test public void testGroupInheritanceWithinGroupSequence() { Validator validator = ValidatorUtil.getValidator(); Try tryMe = new Try(); - tryMe.field2 = "foo"; tryMe.field3 = "bar"; Set> violations = validator.validate( tryMe, Try.GlobalCheck.class ); - assertCorrectConstraintViolationMessages( violations, "field1" ); + assertCorrectConstraintViolationMessages( violations, "field1", "field2" ); } /** From 98fe04b58e463cb9b01bcfa45a59c3506b4b805f Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Fri, 12 Feb 2016 14:09:38 +0100 Subject: [PATCH 041/189] HV-1055 Expanding re-defined default group sequences for validate(), validateProperty() and validateValue() --- .../internal/engine/ValidatorImpl.java | 137 +++++++++-------- .../internal/engine/groups/Sequence.java | 16 ++ .../engine/groups/ValidationOrder.java | 32 ++++ .../groups/ValidationOrderGenerator.java | 30 ++-- .../metadata/BeanMetaDataManager.java | 16 +- .../metadata/aggregated/BeanMetaData.java | 14 ++ .../metadata/aggregated/BeanMetaDataImpl.java | 66 +++++++-- .../UnconstrainedEntityMetaDataSingleton.java | 8 + .../groups/defaultgroupwithinheritance/A.java | 23 +++ .../groups/defaultgroupwithinheritance/B.java | 21 +++ .../DefaultGroupWithInheritanceTest.java | 41 +++++ .../defaultgroupwithinheritance/Max.java | 12 ++ .../defaultgroupwithinheritance/Min.java | 12 ++ .../sequence/SequenceOfSequencesTest.java | 140 ++++++++++++++++++ 14 files changed, 472 insertions(+), 96 deletions(-) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/A.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/B.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/DefaultGroupWithInheritanceTest.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Max.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Min.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOfSequencesTest.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 308107b160..da6c1f3988 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -38,8 +38,8 @@ import org.hibernate.validator.internal.engine.ValidationContext.ValidationContextBuilder; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; -import org.hibernate.validator.internal.engine.groups.GroupWithInheritance; import org.hibernate.validator.internal.engine.groups.Group; +import org.hibernate.validator.internal.engine.groups.GroupWithInheritance; import org.hibernate.validator.internal.engine.groups.Sequence; import org.hibernate.validator.internal.engine.groups.ValidationOrder; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; @@ -468,7 +468,8 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat for ( Class clazz : beanMetaData.getClassHierarchy() ) { BeanMetaData hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.defaultGroupSequenceIsRedefined(); - List> defaultGroupSequence = hostingBeanMetaData.getDefaultGroupSequence( valueContext.getCurrentBean() ); + Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); + Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); // if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy. @@ -477,31 +478,37 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat } PathImpl currentPath = valueContext.getPropertyPath(); - for ( Class defaultSequenceMember : defaultGroupSequence ) { - valueContext.setCurrentGroup( defaultSequenceMember ); - boolean validationSuccessful = true; - for ( MetaConstraint metaConstraint : metaConstraints ) { - // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one - // time. An interface can define more than one constraint, we have to check the class we are validating. - final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); - if ( declaringClass.isInterface() ) { - Class validatedForClass = validatedInterfaces.get( declaringClass ); - if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { - continue; + + while ( defaultGroupSequence.hasNext() ) { + for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { + boolean validationSuccessful = true; + + for ( Group defaultSequenceMember : groupOfGroups ) { + valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() ); + for ( MetaConstraint metaConstraint : metaConstraints ) { + // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one + // time. An interface can define more than one constraint, we have to check the class we are validating. + final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); + if ( declaringClass.isInterface() ) { + Class validatedForClass = validatedInterfaces.get( declaringClass ); + if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { + continue; + } + validatedInterfaces.put( declaringClass, clazz ); + } + + boolean tmp = validateConstraint( validationContext, valueContext, false, metaConstraint ); + if ( shouldFailFast( validationContext ) ) { + return; + } + validationSuccessful = validationSuccessful && tmp; + // reset property path + valueContext.setPropertyPath( currentPath ); } - validatedInterfaces.put( declaringClass, clazz ); } - - boolean tmp = validateConstraint( validationContext, valueContext, false, metaConstraint ); - if ( shouldFailFast( validationContext ) ) { - return; + if ( !validationSuccessful ) { + break; } - validationSuccessful = validationSuccessful && tmp; - // reset property path - valueContext.setPropertyPath( currentPath ); - } - if ( !validationSuccessful ) { - break; } } validationContext.markCurrentBeanAsProcessed( valueContext ); @@ -996,55 +1003,61 @@ private int validatePropertyForDefaultGroup(ValueContext valueCon BeanMetaData hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.defaultGroupSequenceIsRedefined(); Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); - List> defaultGroupSequence = hostingBeanMetaData.getDefaultGroupSequence( valueContext.getCurrentBean() ); + Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); if ( defaultGroupSequenceIsRedefined ) { metaConstraints = hostingBeanMetaData.getMetaConstraints(); } - for ( Class groupClass : defaultGroupSequence ) { - boolean validationSuccessful = true; - valueContext.setCurrentGroup( groupClass ); - for ( MetaConstraint metaConstraint : metaConstraints ) { - // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one - // time. An interface can define more than one constraint, we have to check the class we are validating. - final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); - if ( declaringClass.isInterface() ) { - Class validatedForClass = validatedInterfaces.get( declaringClass ); - if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { - continue; + while ( defaultGroupSequence.hasNext() ) { + for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { + boolean validationSuccessful = true; + + for ( Group groupClass : groupOfGroups ) { + valueContext.setCurrentGroup( groupClass.getDefiningClass() ); + for ( MetaConstraint metaConstraint : metaConstraints ) { + // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one + // time. An interface can define more than one constraint, we have to check the class we are validating. + final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); + if ( declaringClass.isInterface() ) { + Class validatedForClass = validatedInterfaces.get( declaringClass ); + if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { + continue; + } + validatedInterfaces.put( declaringClass, clazz ); + } + + if ( constraintList.contains( metaConstraint ) ) { + boolean tmp = validateConstraint( validationContext, valueContext, true, metaConstraint ); + + validationSuccessful = validationSuccessful && tmp; + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints() + .size() - numberOfConstraintViolationsBefore; + } + } + + if ( typeUseConstraints.contains( metaConstraint ) ) { + boolean tmp = validatePropertyTypeConstraint( + validationContext, + valueContext, + true, + metaConstraint + ); + validationSuccessful = validationSuccessful && tmp; + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints() + .size() - numberOfConstraintViolationsBefore; + } + } } - validatedInterfaces.put( declaringClass, clazz ); } - if ( constraintList.contains( metaConstraint ) ) { - boolean tmp = validateConstraint( validationContext, valueContext, true, metaConstraint ); - - validationSuccessful = validationSuccessful && tmp; - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints() - .size() - numberOfConstraintViolationsBefore; - } - } - - if ( typeUseConstraints.contains( metaConstraint ) ) { - boolean tmp = validatePropertyTypeConstraint( - validationContext, - valueContext, - true, - metaConstraint - ); - validationSuccessful = validationSuccessful && tmp; - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints() - .size() - numberOfConstraintViolationsBefore; - } + if ( !validationSuccessful ) { + break; } } - if ( !validationSuccessful ) { - break; - } } // all the hierarchy has been validated, stop validation. if ( defaultGroupSequenceIsRedefined ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java index cc017d2267..704b1863f2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/Sequence.java @@ -7,12 +7,14 @@ package org.hibernate.validator.internal.engine.groups; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.validation.GroupSequence; +import javax.validation.groups.Default; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -23,12 +25,26 @@ * @author Hardy Ferentschik */ public class Sequence implements Iterable { + + /** + * An "anonymous" sequence with just a single contained element, {@code Default.class}. + */ + public static Sequence DEFAULT = new Sequence(); + private static final Log log = LoggerFactory.make(); private final Class sequence; private List groups; private List expandedGroups; + private Sequence() { + this.sequence = Default.class; + this.groups = Collections.singletonList( Group.DEFAULT_GROUP ); + this.expandedGroups = Collections.singletonList( + new GroupWithInheritance( Collections.singleton( Group.DEFAULT_GROUP ) ) + ); + } + public Sequence(Class sequence, List groups) { this.groups = groups; this.sequence = sequence; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrder.java b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrder.java index 36777e2e4e..c0695a344a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrder.java @@ -6,8 +6,10 @@ */ package org.hibernate.validator.internal.engine.groups; +import java.util.Collections; import java.util.Iterator; import java.util.List; + import javax.validation.GroupDefinitionException; /** @@ -32,4 +34,34 @@ public interface ValidationOrder { */ void assertDefaultGroupSequenceIsExpandable(List> defaultGroupSequence) throws GroupDefinitionException; + + /** + * A {@link org.hibernate.validator.internal.engine.groups.ValidationOrder} which contains a single sequence which + * in turn contains a single group, {@code Default}. + */ + ValidationOrder DEFAULT_SEQUENCE = new DefaultValidationOrder(); + + static class DefaultValidationOrder implements ValidationOrder { + + private final List defaultSequences; + + private DefaultValidationOrder() { + defaultSequences = Collections.singletonList( Sequence.DEFAULT ); + } + + @Override + public Iterator getGroupIterator() { + // Not using emptyIterator() to stay on 1.6 language level + return Collections.emptyList().iterator(); + } + + @Override + public Iterator getSequenceIterator() { + return defaultSequences.iterator(); + } + + @Override + public void assertDefaultGroupSequenceIsExpandable(List> defaultGroupSequence) throws GroupDefinitionException { + } + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java index 0bc77395ac..3d63e2202a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/groups/ValidationOrderGenerator.java @@ -89,7 +89,7 @@ public ValidationOrder getValidationOrder(Collection> groups) { validationOrder.insertGroup( Group.DEFAULT_GROUP ); } else if ( isGroupSequence( clazz ) ) { - insertSequence( clazz, validationOrder ); + insertSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), true, validationOrder ); } else { Group group = new Group( clazz ); @@ -101,6 +101,12 @@ else if ( isGroupSequence( clazz ) ) { return validationOrder; } + public ValidationOrder getDefaultValidationOrder(Class clazz, List> defaultGroupSequence) { + DefaultValidationOrder validationOrder = new DefaultValidationOrder(); + insertSequence( clazz, defaultGroupSequence.toArray( new Class[0] ), false, validationOrder ); + return validationOrder; + } + private boolean isGroupSequence(Class clazz) { return clazz.getAnnotation( GroupSequence.class ) != null; } @@ -119,23 +125,25 @@ private void insertInheritedGroups(Class clazz, DefaultValidationOrder chain) } } - private void insertSequence(Class sequenceClass, DefaultValidationOrder validationOrder) { - Sequence sequence = resolvedSequences.get( sequenceClass ); + private void insertSequence(Class sequenceClass, Class[] sequenceElements, boolean cache, DefaultValidationOrder validationOrder) { + Sequence sequence = cache ? resolvedSequences.get( sequenceClass ) : null; if ( sequence == null ) { - sequence = resolveSequence( sequenceClass, new ArrayList>() ); + sequence = resolveSequence( sequenceClass, sequenceElements, new ArrayList>() ); // we expand the inherited groups only after we determined whether the sequence is expandable sequence.expandInheritedGroups(); // cache already resolved sequences - final Sequence cachedResolvedSequence = resolvedSequences.putIfAbsent( sequenceClass, sequence ); - if ( cachedResolvedSequence != null ) { - sequence = cachedResolvedSequence; + if ( cache ) { + final Sequence cachedResolvedSequence = resolvedSequences.putIfAbsent( sequenceClass, sequence ); + if ( cachedResolvedSequence != null ) { + sequence = cachedResolvedSequence; + } } } validationOrder.insertSequence( sequence ); } - private Sequence resolveSequence(Class sequenceClass, List> processedSequences) { + private Sequence resolveSequence(Class sequenceClass, Class[] sequenceElements, List> processedSequences) { if ( processedSequences.contains( sequenceClass ) ) { throw log.getCyclicDependencyInGroupsDefinitionException(); } @@ -143,11 +151,9 @@ private Sequence resolveSequence(Class sequenceClass, List> processe processedSequences.add( sequenceClass ); } List resolvedSequenceGroups = new ArrayList(); - GroupSequence sequenceAnnotation = sequenceClass.getAnnotation( GroupSequence.class ); - Class[] sequenceArray = sequenceAnnotation.value(); - for ( Class clazz : sequenceArray ) { + for ( Class clazz : sequenceElements ) { if ( isGroupSequence( clazz ) ) { - Sequence tmpSequence = resolveSequence( clazz, processedSequences ); + Sequence tmpSequence = resolveSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), processedSequences ); addGroups( resolvedSequenceGroups, tmpSequence.getComposingGroups() ); } else { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java index 723a44f3b7..f910851b09 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManager.java @@ -9,10 +9,12 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; + import javax.validation.ParameterNameProvider; -import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl.BeanMetaDataBuilder; @@ -89,13 +91,15 @@ public class BeanMetaDataManager { */ private final ExecutableHelper executableHelper; + private final ValidationOrderGenerator validationOrderGenerator = new ValidationOrderGenerator(); + /** * the three properties in this field affect the invocation of rules associated to section 4.5.5 * of the V1.1 specification. By default they are all false, if true they allow * for relaxation of the Liskov Substitution Principal. */ private final MethodValidationConfiguration methodValidationConfiguration; - + /** * Creates a new {@code BeanMetaDataManager}. {@link DefaultParameterNameProvider} is used as parameter name * provider, no meta data providers besides the annotation-based providers are used. @@ -125,7 +129,7 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, new MethodValidationConfiguration() ); } - + public BeanMetaDataManager(ConstraintHelper constraintHelper, ExecutableHelper executableHelper, ParameterNameProvider parameterNameProvider, @@ -137,7 +141,7 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, this.executableHelper = executableHelper; this.methodValidationConfiguration = methodValidationConfiguration; - + this.beanMetaDataCache = new ConcurrentReferenceHashMap, BeanMetaData>( DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, @@ -192,8 +196,8 @@ public int numberOfCachedBeanMetaDataInstances() { * @return A bean meta data object for the given type. */ private BeanMetaDataImpl createBeanMetaData(Class clazz) { - BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( - constraintHelper, executableHelper, clazz, methodValidationConfiguration); + BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( + constraintHelper, executableHelper, validationOrderGenerator, clazz, methodValidationConfiguration); for ( MetaDataProvider provider : metaDataProviders ) { for ( BeanConfiguration beanConfiguration : provider.getBeanConfigurationForHierarchy( clazz ) ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java index 3eebe23de2..c597e977ca 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java @@ -6,11 +6,14 @@ */ package org.hibernate.validator.internal.metadata.aggregated; +import java.util.Iterator; import java.util.List; import java.util.Set; + import javax.validation.ConstraintDeclarationException; import javax.validation.metadata.BeanDescriptor; +import org.hibernate.validator.internal.engine.groups.Sequence; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.facets.Validatable; import org.hibernate.validator.internal.metadata.raw.ExecutableElement; @@ -63,6 +66,17 @@ public interface BeanMetaData extends Validatable { */ List> getDefaultGroupSequence(T beanState); + /** + * Returns a {@link org.hibernate.validator.internal.engine.groups.ValidationOrder} representing the default + * validation group sequence as configured through {@code @GroupSequence}/{@code @DefaultGroupSequenceProvider}. If + * this bean type does not re-declare the default validation group sequence {@link org.hibernate.validator.internal.engine.groups.ValidationOrder#DEFAULT_SEQUENCE} + * will be returned. + */ + // TODO: Ideally, a plain Sequence object should be returned here; I am using ValidationOrder for now to keep + // backporting to 4.3 manageable. The expansion of sequences/groups should be moved from ValidationOrder into + // Sequence and Group, respectively. + Iterator getDefaultValidationSequence(T beanState); + /** * @return {@code true} if the entity redefines the default group sequence, {@code false} otherwise. */ diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index d48c064585..adf9ff589f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -11,6 +11,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -24,6 +25,9 @@ import javax.validation.metadata.PropertyDescriptor; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.groups.Sequence; +import org.hibernate.validator.internal.engine.groups.ValidationOrder; +import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.BeanDescriptorImpl; @@ -69,6 +73,8 @@ public final class BeanMetaDataImpl implements BeanMetaData { */ private static final List> DEFAULT_GROUP_SEQUENCE = Collections.>singletonList( Default.class ); + private final ValidationOrderGenerator validationOrderGenerator; + /** * The root bean class for this meta data. */ @@ -124,6 +130,7 @@ public final class BeanMetaDataImpl implements BeanMetaData { */ private DefaultGroupSequenceProvider defaultGroupSequenceProvider; + private ValidationOrder validationOrder; /** * The class hierarchy for this class starting with the class itself going up the inheritance chain. Interfaces * are not included. @@ -141,8 +148,10 @@ public final class BeanMetaDataImpl implements BeanMetaData { public BeanMetaDataImpl(Class beanClass, List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider, - Set constraintMetaDataSet) { + Set constraintMetaDataSet, + ValidationOrderGenerator validationOrderGenerator) { + this.validationOrderGenerator = validationOrderGenerator; this.beanClass = beanClass; this.propertyMetaDataMap = newHashMap(); @@ -182,7 +191,7 @@ public BeanMetaDataImpl(Class beanClass, Filters.excludeInterfaces() ); - setDefaultGroupSequenceOrProvider( defaultGroupSequence, defaultGroupSequenceProvider ); + setDefaultGroupSequenceOrProvider( defaultGroupSequence, defaultGroupSequenceProvider, validationOrderGenerator ); this.directMetaConstraints = getDirectConstraints(); @@ -255,6 +264,21 @@ public List> getDefaultGroupSequence(T beanState) { return Collections.unmodifiableList( defaultGroupSequence ); } + @Override + public Iterator getDefaultValidationSequence(T beanState) { + if ( hasDefaultGroupSequenceProvider() ) { + List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); + return validationOrderGenerator.getDefaultValidationOrder( + beanClass, + getValidDefaultGroupSequence( providerDefaultGroupSequence ) + ) + .getSequenceIterator(); + } + else { + return validationOrder.getSequenceIterator(); + } + } + @Override public boolean defaultGroupSequenceIsRedefined() { return defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider(); @@ -334,19 +358,22 @@ private Map getConstrainedConstructorsAsDescripto return constrainedMethodDescriptors; } - private void setDefaultGroupSequenceOrProvider(List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider) { + private void setDefaultGroupSequenceOrProvider(List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider, ValidationOrderGenerator validationOrderGenerator) { if ( defaultGroupSequence != null && defaultGroupSequenceProvider != null ) { throw log.getInvalidDefaultGroupSequenceDefinitionException(); } if ( defaultGroupSequenceProvider != null ) { this.defaultGroupSequenceProvider = defaultGroupSequenceProvider; + this.validationOrder = null; } else if ( defaultGroupSequence != null && !defaultGroupSequence.isEmpty() ) { setDefaultGroupSequence( defaultGroupSequence ); + this.validationOrder = validationOrderGenerator.getDefaultValidationOrder( beanClass, this.defaultGroupSequence ); } else { this.defaultGroupSequence = DEFAULT_GROUP_SEQUENCE; + this.validationOrder = ValidationOrder.DEFAULT_SEQUENCE; } } @@ -455,6 +482,8 @@ public static class BeanMetaDataBuilder { private final ConstraintHelper constraintHelper; + private final ValidationOrderGenerator validationOrderGenerator; + private final Class beanClass; private final Set builders = newHashSet(); @@ -470,26 +499,30 @@ public static class BeanMetaDataBuilder { private DefaultGroupSequenceProvider defaultGroupSequenceProvider; private final MethodValidationConfiguration methodValidationConfiguration; - + private BeanMetaDataBuilder( - ConstraintHelper constraintHelper, - ExecutableHelper executableHelper, + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + ValidationOrderGenerator validationOrderGenerator, Class beanClass, MethodValidationConfiguration methodValidationConfiguration) { this.beanClass = beanClass; this.constraintHelper = constraintHelper; + this.validationOrderGenerator = validationOrderGenerator; this.executableHelper = executableHelper; this.methodValidationConfiguration = methodValidationConfiguration; } public static BeanMetaDataBuilder getInstance( - ConstraintHelper constraintHelper, - ExecutableHelper executableHelper, + ConstraintHelper constraintHelper, + ExecutableHelper executableHelper, + ValidationOrderGenerator validationOrderGenerator, Class beanClass, MethodValidationConfiguration methodValidationConfiguration) { - return new BeanMetaDataBuilder( - constraintHelper, - executableHelper, + return new BeanMetaDataBuilder( + constraintHelper, + executableHelper, + validationOrderGenerator, beanClass, methodValidationConfiguration); } @@ -549,7 +582,8 @@ public BeanMetaDataImpl build() { beanClass, defaultGroupSequence, defaultGroupSequenceProvider, - aggregatedElements + aggregatedElements, + validationOrderGenerator ); } } @@ -561,12 +595,12 @@ private static class BuilderDelegate { private MetaDataBuilder propertyBuilder; private ExecutableMetaData.Builder methodBuilder; private final MethodValidationConfiguration methodValidationConfiguration; - + public BuilderDelegate( - Class beanClass, - ConstrainedElement constrainedElement, - ConstraintHelper constraintHelper, + Class beanClass, + ConstrainedElement constrainedElement, + ConstraintHelper constraintHelper, ExecutableHelper executableHelper, MethodValidationConfiguration methodValidationConfiguration ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/UnconstrainedEntityMetaDataSingleton.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/UnconstrainedEntityMetaDataSingleton.java index 41050ba0f3..3bd7e31dff 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/UnconstrainedEntityMetaDataSingleton.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/UnconstrainedEntityMetaDataSingleton.java @@ -6,11 +6,14 @@ */ package org.hibernate.validator.internal.metadata.aggregated; +import java.util.Iterator; import java.util.List; import java.util.Set; + import javax.validation.ConstraintDeclarationException; import javax.validation.metadata.BeanDescriptor; +import org.hibernate.validator.internal.engine.groups.Sequence; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.raw.ExecutableElement; @@ -61,6 +64,11 @@ public boolean defaultGroupSequenceIsRedefined() { return false; } + @Override + public Iterator getDefaultValidationSequence(T beanState) { + throw new UnsupportedOperationException(); + } + @Override public Set> getMetaConstraints() { throw new UnsupportedOperationException(); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/A.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/A.java new file mode 100644 index 0000000000..2194ecf005 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/A.java @@ -0,0 +1,23 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.defaultgroupwithinheritance; + +import javax.validation.GroupSequence; +import javax.validation.constraints.NotNull; + +/** + * @author Gunnar Morling + */ +@GroupSequence({ Max.class, A.class }) +public class A { + + @NotNull(groups = Max.class) + public String foo; + + @NotNull(groups = Min.class) + public String bar; +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/B.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/B.java new file mode 100644 index 0000000000..67f714030f --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/B.java @@ -0,0 +1,21 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.defaultgroupwithinheritance; + +import javax.validation.constraints.NotNull; + +/** + * @author Gunnar Morling + */ +public class B { + + @NotNull(groups = Max.class) + public String foo; + + @NotNull(groups = Min.class) + public String bar; +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/DefaultGroupWithInheritanceTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/DefaultGroupWithInheritanceTest.java new file mode 100644 index 0000000000..92d5cc8058 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/DefaultGroupWithInheritanceTest.java @@ -0,0 +1,41 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.defaultgroupwithinheritance; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; + +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidatorUtil; +import org.testng.annotations.Test; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectPropertyPaths; + +/** + * @author Gunnar Morling + */ +public class DefaultGroupWithInheritanceTest { + + @Test + @TestForIssue(jiraKey = "HV-1055") + public void testGroupInheritanceWithinRedeclaredGroupSequence() { + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new A() ); + assertCorrectPropertyPaths( violations, "foo", "bar" ); + } + + @Test + public void testGroupInheritanceWithinPassedGroupSequence() { + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new B(), Max.class, Min.class ); + assertCorrectPropertyPaths( violations, "foo", "bar" ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Max.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Max.java new file mode 100644 index 0000000000..4a184ad87d --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Max.java @@ -0,0 +1,12 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.defaultgroupwithinheritance; + +/** + * @author Gunnar Morling + */ +public interface Max extends Min {} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Min.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Min.java new file mode 100644 index 0000000000..80c4de4816 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupwithinheritance/Min.java @@ -0,0 +1,12 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.defaultgroupwithinheritance; + +/** + * @author Gunnar Morling + */ +public interface Min {} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOfSequencesTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOfSequencesTest.java new file mode 100644 index 0000000000..0c8bde411d --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOfSequencesTest.java @@ -0,0 +1,140 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.sequence; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.GroupSequence; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.test.internal.engine.groups.sequence.SequenceOfSequencesTest.AllConstraints.BasicConstraints; +import org.hibernate.validator.test.internal.engine.groups.sequence.SequenceOfSequencesTest.AllConstraints.BasicConstraints.OverlyBasicConstraints; +import org.hibernate.validator.test.internal.engine.groups.sequence.SequenceOfSequencesTest.AllConstraints.BasicConstraints.SomewhatBasicConstraints; +import org.hibernate.validator.test.internal.engine.groups.sequence.SequenceOfSequencesTest.AllConstraints.ComplexConstraints; +import org.hibernate.validator.test.internal.engine.groups.sequence.SequenceOfSequencesTest.AllConstraints.ComplexConstraints.ImmenselyComplexConstraints; +import org.hibernate.validator.test.internal.engine.groups.sequence.SequenceOfSequencesTest.AllConstraints.ComplexConstraints.SomewhatComplexConstraints; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidatorUtil; +import org.testng.annotations.Test; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectPropertyPaths; + +/** + * Sequences may comprise other sequences. + * + * @author Gunnar Moring + */ +public class SequenceOfSequencesTest { + + /** + * A sequence of sequences is passed to the validate() call. + */ + @Test + public void groupSequenceOfGroupSequences() { + Validator validator = ValidatorUtil.getValidator(); + + PlushAlligator alligator = new PlushAlligator(); + + Set> violations = validator.validate( alligator, AllConstraints.class ); + assertCorrectPropertyPaths( violations, "name" ); + + alligator.name = "Ruben"; + violations = validator.validate( alligator, AllConstraints.class ); + assertCorrectPropertyPaths( violations, "highestEducationalDegree" ); + + alligator.highestEducationalDegree = "PhD"; + violations = validator.validate( alligator, AllConstraints.class ); + assertCorrectPropertyPaths( violations, "length" ); + + alligator.length = 540; + violations = validator.validate( alligator, AllConstraints.class ); + assertCorrectPropertyPaths( violations, "age" ); + } + + /** + * A sequence of sequences is used as the default group sequence. + */ + @Test + @TestForIssue(jiraKey = "HV-1055") + public void defaultGroupSequenceContainsOtherGroupSequences() { + Validator validator = ValidatorUtil.getValidator(); + + PlushCrocodile crocodile = new PlushCrocodile(); + + Set> violations = validator.validate( crocodile ); + assertCorrectPropertyPaths( violations, "name" ); + + crocodile.name = "Ruben"; + violations = validator.validate( crocodile, AllConstraints.class ); + assertCorrectPropertyPaths( violations, "highestEducationalDegree" ); + + crocodile.highestEducationalDegree = "PhD"; + violations = validator.validate( crocodile, AllConstraints.class ); + assertCorrectPropertyPaths( violations, "length" ); + + crocodile.length = 540; + violations = validator.validate( crocodile, AllConstraints.class ); + assertCorrectPropertyPaths( violations, "age" ); + } + + public static class PlushAlligator { + + @NotNull(groups = OverlyBasicConstraints.class) + public String name; + + @NotNull(groups = SomewhatBasicConstraints.class) + public String highestEducationalDegree; + + @NotNull(groups = SomewhatComplexConstraints.class) + public Integer length; + + @NotNull(groups = ImmenselyComplexConstraints.class) + public Integer age; + } + + @GroupSequence({ AllConstraints.class, PlushCrocodile.class }) + public static class PlushCrocodile { + + @NotNull(groups = OverlyBasicConstraints.class) + public String name; + + @NotNull(groups = SomewhatBasicConstraints.class) + public String highestEducationalDegree; + + @NotNull(groups = SomewhatComplexConstraints.class) + public Integer length; + + @NotNull(groups = ImmenselyComplexConstraints.class) + public Integer age; + } + + @GroupSequence({ BasicConstraints.class, ComplexConstraints.class }) + public interface AllConstraints { + + @GroupSequence({ OverlyBasicConstraints.class, SomewhatBasicConstraints.class }) + public interface BasicConstraints { + + public interface OverlyBasicConstraints { + } + + public interface SomewhatBasicConstraints { + } + } + + @GroupSequence({ SomewhatComplexConstraints.class, ImmenselyComplexConstraints.class }) + public interface ComplexConstraints { + + public interface SomewhatComplexConstraints { + } + + public interface ImmenselyComplexConstraints { + } + } + } +} From 94f4f56bc3e887a5df8c9024e4cf457fdd164d0b Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Tue, 16 Feb 2016 15:02:45 +0100 Subject: [PATCH 042/189] HV-1055 Avoiding some loops when validating default group --- .../internal/engine/ValidatorImpl.java | 197 ++++++++++-------- 1 file changed, 114 insertions(+), 83 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index da6c1f3988..0a847e29d1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -468,49 +468,33 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat for ( Class clazz : beanMetaData.getClassHierarchy() ) { BeanMetaData hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.defaultGroupSequenceIsRedefined(); - Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); - - Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); // if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy. if ( defaultGroupSequenceIsRedefined ) { - metaConstraints = hostingBeanMetaData.getMetaConstraints(); - } + Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); + Set> metaConstraints = hostingBeanMetaData.getMetaConstraints(); - PathImpl currentPath = valueContext.getPropertyPath(); - - while ( defaultGroupSequence.hasNext() ) { - for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { - boolean validationSuccessful = true; - - for ( Group defaultSequenceMember : groupOfGroups ) { - valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() ); - for ( MetaConstraint metaConstraint : metaConstraints ) { - // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one - // time. An interface can define more than one constraint, we have to check the class we are validating. - final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); - if ( declaringClass.isInterface() ) { - Class validatedForClass = validatedInterfaces.get( declaringClass ); - if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { - continue; - } - validatedInterfaces.put( declaringClass, clazz ); - } - - boolean tmp = validateConstraint( validationContext, valueContext, false, metaConstraint ); - if ( shouldFailFast( validationContext ) ) { - return; - } - validationSuccessful = validationSuccessful && tmp; - // reset property path - valueContext.setPropertyPath( currentPath ); + while ( defaultGroupSequence.hasNext() ) { + for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { + boolean validationSuccessful = true; + + for ( Group defaultSequenceMember : groupOfGroups ) { + validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, + metaConstraints, defaultSequenceMember ); + } + if ( !validationSuccessful ) { + break; } - } - if ( !validationSuccessful ) { - break; } } } + // fast path in case the default group sequence hasn't been redefined + else { + Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); + validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints, + Group.DEFAULT_GROUP ); + } + validationContext.markCurrentBeanAsProcessed( valueContext ); // all constraints in the hierarchy has been validated, stop validation. @@ -520,6 +504,37 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat } } + private boolean validateConstraintsForSingleDefaultGroupElement(ValidationContext validationContext, ValueContext valueContext, final Map, Class> validatedInterfaces, + Class clazz, Set> metaConstraints, Group defaultSequenceMember) { + boolean validationSuccessful = true; + + valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() ); + PathImpl currentPath = valueContext.getPropertyPath(); + + for ( MetaConstraint metaConstraint : metaConstraints ) { + // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one + // time. An interface can define more than one constraint, we have to check the class we are validating. + final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); + if ( declaringClass.isInterface() ) { + Class validatedForClass = validatedInterfaces.get( declaringClass ); + if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { + continue; + } + validatedInterfaces.put( declaringClass, clazz ); + } + + boolean tmp = validateConstraint( validationContext, valueContext, false, metaConstraint ); + if ( shouldFailFast( validationContext ) ) { + return false; + } + + validationSuccessful = validationSuccessful && tmp; + // reset property path + valueContext.setPropertyPath( currentPath ); + } + return validationSuccessful; + } + private void validateConstraintsForNonDefaultGroup(ValidationContext validationContext, ValueContext valueContext) { BeanMetaData beanMetaData = beanMetaDataManager.getBeanMetaData( valueContext.getCurrentBeanType() ); PathImpl currentPath = valueContext.getPropertyPath(); @@ -1002,63 +1017,34 @@ private int validatePropertyForDefaultGroup(ValueContext valueCon for ( Class clazz : beanMetaData.getClassHierarchy() ) { BeanMetaData hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz ); boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.defaultGroupSequenceIsRedefined(); - Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); - Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); if ( defaultGroupSequenceIsRedefined ) { - metaConstraints = hostingBeanMetaData.getMetaConstraints(); - } + Iterator defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() ); + Set> metaConstraints = hostingBeanMetaData.getMetaConstraints(); + + while ( defaultGroupSequence.hasNext() ) { + for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { + boolean validationSuccessful = true; - while ( defaultGroupSequence.hasNext() ) { - for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) { - boolean validationSuccessful = true; - - for ( Group groupClass : groupOfGroups ) { - valueContext.setCurrentGroup( groupClass.getDefiningClass() ); - for ( MetaConstraint metaConstraint : metaConstraints ) { - // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one - // time. An interface can define more than one constraint, we have to check the class we are validating. - final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); - if ( declaringClass.isInterface() ) { - Class validatedForClass = validatedInterfaces.get( declaringClass ); - if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { - continue; - } - validatedInterfaces.put( declaringClass, clazz ); - } - - if ( constraintList.contains( metaConstraint ) ) { - boolean tmp = validateConstraint( validationContext, valueContext, true, metaConstraint ); - - validationSuccessful = validationSuccessful && tmp; - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints() - .size() - numberOfConstraintViolationsBefore; - } - } - - if ( typeUseConstraints.contains( metaConstraint ) ) { - boolean tmp = validatePropertyTypeConstraint( - validationContext, - valueContext, - true, - metaConstraint - ); - validationSuccessful = validationSuccessful && tmp; - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints() - .size() - numberOfConstraintViolationsBefore; - } - } + for ( Group groupClass : groupOfGroups ) { + validationSuccessful = validatePropertyForSingleDefaultGroupElement( valueContext, validationContext, constraintList, + typeUseConstraints, validatedInterfaces, clazz, metaConstraints, groupClass ); } - } - if ( !validationSuccessful ) { - break; + if ( !validationSuccessful ) { + break; + } } } + } + // fast path in case the default group sequence hasn't been redefined + else { + Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); + validatePropertyForSingleDefaultGroupElement( valueContext, validationContext, constraintList, + typeUseConstraints, validatedInterfaces, clazz, metaConstraints, Group.DEFAULT_GROUP ); } + // all the hierarchy has been validated, stop validation. if ( defaultGroupSequenceIsRedefined ) { break; @@ -1067,6 +1053,51 @@ private int validatePropertyForDefaultGroup(ValueContext valueCon return validationContext.getFailingConstraints().size() - numberOfConstraintViolationsBefore; } + private boolean validatePropertyForSingleDefaultGroupElement(ValueContext valueContext, ValidationContext validationContext, + List> constraintList, List> typeUseConstraints, + final Map, Class> validatedInterfaces, Class clazz, Set> metaConstraints, + Group groupClass) { + valueContext.setCurrentGroup( groupClass.getDefiningClass() ); + boolean validationSuccessful =true; + + for ( MetaConstraint metaConstraint : metaConstraints ) { + // HV-466, an interface implemented more than one time in the hierarchy has to be validated only one + // time. An interface can define more than one constraint, we have to check the class we are validating. + final Class declaringClass = metaConstraint.getLocation().getDeclaringClass(); + if ( declaringClass.isInterface() ) { + Class validatedForClass = validatedInterfaces.get( declaringClass ); + if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) { + continue; + } + validatedInterfaces.put( declaringClass, clazz ); + } + + if ( constraintList.contains( metaConstraint ) ) { + boolean tmp = validateConstraint( validationContext, valueContext, true, metaConstraint ); + + validationSuccessful = validationSuccessful && tmp; + if ( shouldFailFast( validationContext ) ) { + return false; + } + } + + if ( typeUseConstraints.contains( metaConstraint ) ) { + boolean tmp = validatePropertyTypeConstraint( + validationContext, + valueContext, + true, + metaConstraint + ); + validationSuccessful = validationSuccessful && tmp; + if ( shouldFailFast( validationContext ) ) { + return false; + } + } + } + + return validationSuccessful; + } + private void validateParametersInContext(ValidationContext validationContext, Object[] parameterValues, ValidationOrder validationOrder) { From 442ff6298ccdef83435389b546bea4191d5bfa44 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Tue, 16 Feb 2016 12:49:45 +0100 Subject: [PATCH 043/189] HV-1055 Expanding re-defined default group sequences for validateParameters() and validateReturnValue() --- .../internal/engine/ValidatorImpl.java | 224 ++++++++++-------- 1 file changed, 126 insertions(+), 98 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java index 0a847e29d1..9556713fe5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java @@ -1196,89 +1196,103 @@ private int validateParametersForGroup(ValidationContext validationContex // the inheritance tree? // For now a redefined default sequence will only be considered if specified at the bean // hosting the validated itself, but no other default sequence from parent types - List> groupList; if ( group.isDefaultGroup() ) { - groupList = beanMetaData.getDefaultGroupSequence( validationContext.getRootBean() ); + Iterator defaultGroupSequence = beanMetaData.getDefaultValidationSequence( validationContext.getRootBean() ); + + while ( defaultGroupSequence.hasNext() ) { + Sequence sequence = defaultGroupSequence.next(); + for ( GroupWithInheritance expandedGroup : sequence ) { + int numberOfViolationsOfCurrentGroup = 0; + + for ( Group defaultGroupSequenceElement : expandedGroup ) { + numberOfViolationsOfCurrentGroup += validateParametersForSingleGroup( validationContext, parameterValues, executableMetaData, defaultGroupSequenceElement.getDefiningClass() ); + + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } + } + + if ( numberOfViolationsOfCurrentGroup > 0 ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } + } + } } else { - groupList = Arrays.>asList( group.getDefiningClass() ); + validateParametersForSingleGroup( validationContext, parameterValues, executableMetaData, group.getDefiningClass() ); } - //the only case where we can have multiple groups here is a redefined default group sequence - for ( Class currentValidatedGroup : groupList ) { - int numberOfViolationsOfCurrentGroup = 0; + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } - ValueContext valueContext = getExecutableValueContext( - validationContext.getRootBean(), executableMetaData, currentValidatedGroup - ); - valueContext.appendCrossParameterNode(); - valueContext.setCurrentValidatedValue( parameterValues ); + private int validateParametersForSingleGroup(ValidationContext validationContext, Object[] parameterValues, ExecutableMetaData executableMetaData, Class currentValidatedGroup) { + int numberOfViolationsBefore = validationContext.getFailingConstraints().size(); - // 1. validate cross-parameter constraints - numberOfViolationsOfCurrentGroup += validateConstraintsForGroup( - validationContext, valueContext, executableMetaData.getCrossParameterConstraints() - ); - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; - } + ValueContext valueContext = getExecutableValueContext( + validationContext.getRootBean(), executableMetaData, currentValidatedGroup + ); + valueContext.appendCrossParameterNode(); + valueContext.setCurrentValidatedValue( parameterValues ); - valueContext = getExecutableValueContext( - validationContext.getRootBean(), executableMetaData, currentValidatedGroup - ); - valueContext.setCurrentValidatedValue( parameterValues ); + // 1. validate cross-parameter constraints + validateConstraintsForGroup( + validationContext, valueContext, executableMetaData.getCrossParameterConstraints() + ); + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } - // 2. validate parameter constraints - for ( int i = 0; i < parameterValues.length; i++ ) { - PathImpl originalPath = valueContext.getPropertyPath(); + valueContext = getExecutableValueContext( + validationContext.getRootBean(), executableMetaData, currentValidatedGroup + ); + valueContext.setCurrentValidatedValue( parameterValues ); - ParameterMetaData parameterMetaData = executableMetaData.getParameterMetaData( i ); - Object value = parameterValues[i]; + // 2. validate parameter constraints + for ( int i = 0; i < parameterValues.length; i++ ) { + PathImpl originalPath = valueContext.getPropertyPath(); - if ( value != null ) { - Class valueType = value.getClass(); - if ( parameterMetaData.getType() instanceof Class && ( (Class) parameterMetaData.getType() ).isPrimitive() ) { - valueType = ReflectionHelper.unBoxedType( valueType ); - } - if ( !TypeHelper.isAssignable( - TypeHelper.getErasedType( parameterMetaData.getType() ), - valueType - ) ) { - throw log.getParameterTypesDoNotMatchException( - valueType.getName(), - parameterMetaData.getType().toString(), - i, - validationContext.getExecutable().getMember() - ); - } + ParameterMetaData parameterMetaData = executableMetaData.getParameterMetaData( i ); + Object value = parameterValues[i]; + + if ( value != null ) { + Class valueType = value.getClass(); + if ( parameterMetaData.getType() instanceof Class && ( (Class) parameterMetaData.getType() ).isPrimitive() ) { + valueType = ReflectionHelper.unBoxedType( valueType ); + } + if ( !TypeHelper.isAssignable( + TypeHelper.getErasedType( parameterMetaData.getType() ), + valueType + ) ) { + throw log.getParameterTypesDoNotMatchException( + valueType.getName(), + parameterMetaData.getType().toString(), + i, + validationContext.getExecutable().getMember() + ); } + } + + valueContext.appendNode( parameterMetaData ); + valueContext.setUnwrapMode( parameterMetaData.unwrapMode() ); + valueContext.setCurrentValidatedValue( value ); - valueContext.appendNode( parameterMetaData ); - valueContext.setUnwrapMode( parameterMetaData.unwrapMode() ); - valueContext.setCurrentValidatedValue( value ); + validateConstraintsForGroup( + validationContext, valueContext, parameterMetaData + ); + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } - numberOfViolationsOfCurrentGroup += validateConstraintsForGroup( - validationContext, valueContext, parameterMetaData + if ( !parameterMetaData.isCascading() ) { + validateConstraintsForGroup( + validationContext, valueContext, parameterMetaData.getTypeArgumentsConstraints() ); if ( shouldFailFast( validationContext ) ) { return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; } - - if ( !parameterMetaData.isCascading() ) { - numberOfViolationsOfCurrentGroup += validateConstraintsForGroup( - validationContext, valueContext, parameterMetaData.getTypeArgumentsConstraints() - ); - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; - } - } - - valueContext.setPropertyPath( originalPath ); } - //stop processing after first group with errors occurred - if ( numberOfViolationsOfCurrentGroup > 0 ) { - break; - } + valueContext.setPropertyPath( originalPath ); } return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; @@ -1398,55 +1412,69 @@ private int validateReturnValueForGroup(ValidationContext validationConte // For now a redefined default sequence will only be considered if specified at the bean // hosting the validated itself, but no other default sequence from parent types - List> groupList; if ( group.isDefaultGroup() ) { - groupList = beanMetaData.getDefaultGroupSequence( bean ); + Iterator defaultGroupSequence = beanMetaData.getDefaultValidationSequence( bean ); + + while ( defaultGroupSequence.hasNext() ) { + Sequence sequence = defaultGroupSequence.next(); + for ( GroupWithInheritance expandedGroup : sequence ) { + int numberOfViolationsOfCurrentGroup = 0; + + for ( Group defaultGroupSequenceElement : expandedGroup ) { + numberOfViolationsOfCurrentGroup += validateReturnValueForSingleGroup( validationContext, executableMetaData, bean, value, defaultGroupSequenceElement.getDefiningClass() ); + + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } + } + + //stop processing after first group with errors occurred + if ( numberOfViolationsOfCurrentGroup > 0 ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } + } + } } else { - groupList = Arrays.>asList( group.getDefiningClass() ); + validateReturnValueForSingleGroup( validationContext, executableMetaData, bean, value, group.getDefiningClass() ); } - //the only case where we can have multiple groups here is a redefined default group sequence - for ( Class oneGroup : groupList ) { - - int numberOfViolationsOfCurrentGroup = 0; + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } - // validate constraints at return value itself - ValueContext valueContext = getExecutableValueContext( - executableMetaData.getKind() == ElementKind.CONSTRUCTOR ? value : bean, - executableMetaData, - oneGroup - ); + private int validateReturnValueForSingleGroup(ValidationContext validationContext, ExecutableMetaData executableMetaData, T bean, Object value, Class oneGroup) { + int numberOfViolationsBefore = validationContext.getFailingConstraints().size(); - valueContext.setCurrentValidatedValue( value ); - ReturnValueMetaData returnValueMetaData = executableMetaData.getReturnValueMetaData(); - valueContext.appendNode( returnValueMetaData ); - valueContext.setUnwrapMode( returnValueMetaData.unwrapMode() ); + // validate constraints at return value itself + ValueContext valueContext = getExecutableValueContext( + executableMetaData.getKind() == ElementKind.CONSTRUCTOR ? value : bean, + executableMetaData, + oneGroup + ); - numberOfViolationsOfCurrentGroup += - validateConstraintsForGroup( - validationContext, valueContext, returnValueMetaData - ); - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; - } + valueContext.setCurrentValidatedValue( value ); + ReturnValueMetaData returnValueMetaData = executableMetaData.getReturnValueMetaData(); + valueContext.appendNode( returnValueMetaData ); + valueContext.setUnwrapMode( returnValueMetaData.unwrapMode() ); - if ( !returnValueMetaData.isCascading() ) { - numberOfViolationsOfCurrentGroup += validateConstraintsForGroup( - validationContext, valueContext, returnValueMetaData.getTypeArgumentsConstraints() + int numberOfViolationsOfCurrentGroup = + validateConstraintsForGroup( + validationContext, valueContext, returnValueMetaData ); - if ( shouldFailFast( validationContext ) ) { - return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; - } - } + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + } - //stop processing after first group with errors occurred - if ( numberOfViolationsOfCurrentGroup > 0 ) { - break; + if ( !returnValueMetaData.isCascading() ) { + numberOfViolationsOfCurrentGroup += validateConstraintsForGroup( + validationContext, valueContext, returnValueMetaData.getTypeArgumentsConstraints() + ); + if ( shouldFailFast( validationContext ) ) { + return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; } } - return validationContext.getFailingConstraints().size() - numberOfViolationsBefore; + return numberOfViolationsOfCurrentGroup; } private int validateConstraintsForGroup(ValidationContext validationContext, From e83d3ff7bc40988cee8f57519f18fac639eb389d Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 18 Feb 2016 10:30:24 +0100 Subject: [PATCH 044/189] HV-1058 Retrieving default group sequence only once for all metamodel descriptors --- .../metadata/aggregated/BeanMetaDataImpl.java | 52 ++++++++++++++----- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index adf9ff589f..0f35dc0ee4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -197,14 +197,35 @@ public BeanMetaDataImpl(Class beanClass, this.executableMetaDataMap = Collections.unmodifiableMap( bySignature( executableMetaDataSet ) ); + boolean defaultGroupSequenceIsRedefined = defaultGroupSequenceIsRedefined(); + List> resolvedDefaultGroupSequence = getDefaultGroupSequence( null ); + + Map propertyDescriptors = getConstrainedPropertiesAsDescriptors( + propertyMetaDataMap, + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence + ); + + Map methodsDescriptors = getConstrainedMethodsAsDescriptors( + executableMetaDataMap, + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence + ); + + Map constructorsDescriptors = getConstrainedConstructorsAsDescriptors( + executableMetaDataMap, + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence + ); + this.beanDescriptor = new BeanDescriptorImpl( beanClass, getClassLevelConstraintsAsDescriptors(), - getConstrainedPropertiesAsDescriptors(), - getConstrainedMethodsAsDescriptors(), - getConstrainedConstructorsAsDescriptors(), - defaultGroupSequenceIsRedefined(), - getDefaultGroupSequence( null ) + propertyDescriptors, + methodsDescriptors, + constructorsDescriptors, + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence ); } @@ -301,7 +322,8 @@ private Set> getClassLevelConstraintsAsDescriptors() return theValue; } - private Map getConstrainedPropertiesAsDescriptors() { + private static Map getConstrainedPropertiesAsDescriptors(Map propertyMetaDataMap, + boolean defaultGroupSequenceIsRedefined, List> resolvedDefaultGroupSequence) { Map theValue = newHashMap(); for ( Entry entry : propertyMetaDataMap.entrySet() ) { @@ -309,8 +331,8 @@ private Map getConstrainedPropertiesAsDescriptors() theValue.put( entry.getKey(), entry.getValue().asDescriptor( - defaultGroupSequenceIsRedefined(), - getDefaultGroupSequence( null ) + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence ) ); } @@ -319,15 +341,16 @@ private Map getConstrainedPropertiesAsDescriptors() return theValue; } - private Map getConstrainedMethodsAsDescriptors() { + private static Map getConstrainedMethodsAsDescriptors(Map executableMetaDataMap, + boolean defaultGroupSequenceIsRedefined, List> resolvedDefaultGroupSequence) { Map constrainedMethodDescriptors = newHashMap(); for ( ExecutableMetaData executableMetaData : executableMetaDataMap.values() ) { if ( executableMetaData.getKind() == ElementKind.METHOD && executableMetaData.isConstrained() ) { ExecutableDescriptorImpl descriptor = executableMetaData.asDescriptor( - defaultGroupSequenceIsRedefined(), - getDefaultGroupSequence( null ) + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence ); for ( String signature : executableMetaData.getSignatures() ) { @@ -339,7 +362,8 @@ private Map getConstrainedMethodsAsDescriptors return constrainedMethodDescriptors; } - private Map getConstrainedConstructorsAsDescriptors() { + private static Map getConstrainedConstructorsAsDescriptors(Map executableMetaDataMap, + boolean defaultGroupSequenceIsRedefined, List> resolvedDefaultGroupSequence) { Map constrainedMethodDescriptors = newHashMap(); for ( ExecutableMetaData executableMetaData : executableMetaDataMap.values() ) { @@ -348,8 +372,8 @@ private Map getConstrainedConstructorsAsDescripto // constructors never override, so there will be exactly one identifier executableMetaData.getSignatures().iterator().next(), executableMetaData.asDescriptor( - defaultGroupSequenceIsRedefined(), - getDefaultGroupSequence( null ) + defaultGroupSequenceIsRedefined, + resolvedDefaultGroupSequence ) ); } From c8e61d657cdff565435ec95243b71a7e369fc075 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 18 Feb 2016 10:45:37 +0100 Subject: [PATCH 045/189] HV-1058 Making BeanMetaDataImpl immutable --- .../metadata/aggregated/BeanMetaDataImpl.java | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index 0f35dc0ee4..8508a5fcd9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -50,7 +50,6 @@ import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import static org.hibernate.validator.internal.util.CollectionHelper.partition; @@ -120,7 +119,7 @@ public final class BeanMetaDataImpl implements BeanMetaData { /** * The default groups sequence for this bean class. */ - private List> defaultGroupSequence = newArrayList(); + private final List> defaultGroupSequence; /** * The default group sequence provider. @@ -128,9 +127,9 @@ public final class BeanMetaDataImpl implements BeanMetaData { * @see org.hibernate.validator.group.GroupSequenceProvider * @see DefaultGroupSequenceProvider */ - private DefaultGroupSequenceProvider defaultGroupSequenceProvider; + private final DefaultGroupSequenceProvider defaultGroupSequenceProvider; - private ValidationOrder validationOrder; + private final ValidationOrder validationOrder; /** * The class hierarchy for this class starting with the class itself going up the inheritance chain. Interfaces * are not included. @@ -191,7 +190,10 @@ public BeanMetaDataImpl(Class beanClass, Filters.excludeInterfaces() ); - setDefaultGroupSequenceOrProvider( defaultGroupSequence, defaultGroupSequenceProvider, validationOrderGenerator ); + DefaultGroupSequenceContext defaultGroupContext = getDefaultGroupSequenceData( beanClass, defaultGroupSequence, defaultGroupSequenceProvider, validationOrderGenerator ); + this.defaultGroupSequenceProvider = defaultGroupContext.defaultGroupSequenceProvider; + this.defaultGroupSequence = Collections.unmodifiableList( defaultGroupContext.defaultGroupSequence ); + this.validationOrder = defaultGroupContext.validationOrder; this.directMetaConstraints = getDirectConstraints(); @@ -279,10 +281,10 @@ public ExecutableMetaData getMetaDataFor(ExecutableElement executable) { public List> getDefaultGroupSequence(T beanState) { if ( hasDefaultGroupSequenceProvider() ) { List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); - return getValidDefaultGroupSequence( providerDefaultGroupSequence ); + return getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ); } - return Collections.unmodifiableList( defaultGroupSequence ); + return defaultGroupSequence; } @Override @@ -291,7 +293,7 @@ public Iterator getDefaultValidationSequence(T beanState) { List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); return validationOrderGenerator.getDefaultValidationOrder( beanClass, - getValidDefaultGroupSequence( providerDefaultGroupSequence ) + getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ) ) .getSequenceIterator(); } @@ -382,23 +384,28 @@ private static Map getConstrainedConstructorsAsDe return constrainedMethodDescriptors; } - private void setDefaultGroupSequenceOrProvider(List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider, ValidationOrderGenerator validationOrderGenerator) { + private static DefaultGroupSequenceContext getDefaultGroupSequenceData(Class beanClass, List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider, ValidationOrderGenerator validationOrderGenerator) { if ( defaultGroupSequence != null && defaultGroupSequenceProvider != null ) { throw log.getInvalidDefaultGroupSequenceDefinitionException(); } + DefaultGroupSequenceContext context = new DefaultGroupSequenceContext(); + if ( defaultGroupSequenceProvider != null ) { - this.defaultGroupSequenceProvider = defaultGroupSequenceProvider; - this.validationOrder = null; + context.defaultGroupSequenceProvider = defaultGroupSequenceProvider; + context.defaultGroupSequence = Collections.emptyList(); + context.validationOrder = null; } else if ( defaultGroupSequence != null && !defaultGroupSequence.isEmpty() ) { - setDefaultGroupSequence( defaultGroupSequence ); - this.validationOrder = validationOrderGenerator.getDefaultValidationOrder( beanClass, this.defaultGroupSequence ); + context.defaultGroupSequence = getValidDefaultGroupSequence( beanClass, defaultGroupSequence ); + context.validationOrder = validationOrderGenerator.getDefaultValidationOrder( beanClass, context.defaultGroupSequence ); } else { - this.defaultGroupSequence = DEFAULT_GROUP_SEQUENCE; - this.validationOrder = ValidationOrder.DEFAULT_SEQUENCE; + context.defaultGroupSequence = DEFAULT_GROUP_SEQUENCE; + context.validationOrder = ValidationOrder.DEFAULT_SEQUENCE; } + + return context; } private Set> getClassLevelConstraints(Set> constraints) { @@ -444,11 +451,7 @@ private Map bySignature(Set exec return theValue; } - private void setDefaultGroupSequence(List> groupSequence) { - defaultGroupSequence = getValidDefaultGroupSequence( groupSequence ); - } - - private List> getValidDefaultGroupSequence(List> groupSequence) { + private static List> getValidDefaultGroupSequence(Class beanClass, List> groupSequence) { List> validDefaultGroupSequence = new ArrayList>(); boolean groupSequenceContainsDefault = false; @@ -720,4 +723,13 @@ public Set build() { return metaDataSet; } } + + /** + * Tuple for returning default group sequence, provider and validation order at once. + */ + private static class DefaultGroupSequenceContext { + List> defaultGroupSequence; + DefaultGroupSequenceProvider defaultGroupSequenceProvider; + ValidationOrder validationOrder; + } } From db7ae586508add6491f1443c8ab4e770d7cb2c20 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 18 Feb 2016 10:48:08 +0100 Subject: [PATCH 046/189] HV-1053 Using WildFly 10.0.0.Final for integration tests --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e024d641b1..691f5315fd 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ 2.5.5 2.7 1.8.3 - 10.0.0.CR5 + 10.0.0.Final 1.1.9.Final 1.7.2 3.3.0.Final From 09f59e78b1d7550e531091479f3005c459e4f341 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 13 Apr 2016 13:05:48 +0200 Subject: [PATCH 047/189] HV-1063 Update Karaf dependencies to fix an integration test issue --- osgi/integrationtest/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osgi/integrationtest/pom.xml b/osgi/integrationtest/pom.xml index 73af4ed1fa..3400aa2652 100644 --- a/osgi/integrationtest/pom.xml +++ b/osgi/integrationtest/pom.xml @@ -23,9 +23,9 @@ true - 4.7.0 - 2.4.4 - 3.0.3 + 4.8.0 + 2.4.7 + 3.0.6 From ef9e0abdba01deb13a0013ef7254b54b4ad23159 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Fri, 15 Apr 2016 16:45:15 +0200 Subject: [PATCH 048/189] HV-1063 Adding Guillaume Smet to copyright.txt --- copyright.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/copyright.txt b/copyright.txt index b5d34e63e2..440492f4d0 100644 --- a/copyright.txt +++ b/copyright.txt @@ -18,6 +18,7 @@ Federico Mancini Gavin King George Gastaldi Gerhard Petracek +Guillaume Smet Gunnar Morling Hardy Ferentschik Henno Vermeulen From ce9c087827678d45ecbc706da683b6b327a90b6a Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 13 Apr 2016 13:14:17 +0200 Subject: [PATCH 049/189] HV-1064 Add Travis support --- .travis.yml | 27 +++++++++++++++++++++++++++ README.md | 6 ++++++ 2 files changed, 33 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..1729ded0d8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,27 @@ +sudo: required +dist: trusty +language: java +jdk: + - oraclejdk8 +addons: + apt: + packages: + - oracle-java8-installer +# might be useful to push reports to an S3 bucket +# artifacts: +# paths: +# - $(find $HOME -name surefire-reports | tr "\n" ":") +# - $(find $HOME -name failsafe-reports | tr "\n" ":") +# s3_region: 'us-west-2' +cache: + directories: + - $HOME/.m2 +install: + # The Maven install provided by Travis is outdated, use Maven wrapper to get the latest version + - mvn -N io.takari:maven:wrapper + - ./mvnw -v + # first run to download all the Maven dependencies without logging + - travis_wait ./mvnw -B -q -DskipTests=true install +before_script: +script: + - ./mvnw clean verify diff --git a/README.md b/README.md index e82e8aba24..96d49811ea 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,12 @@ You will also need a JDK 8 and Maven 3 (>= 3.0.3). With these prerequisites in p There are more build options available as well. For more information refer to [Contributing to Hibernate Validator](http://hibernate.org/validator/contribute/). +## Continuous Integration + +The [official Continuous Integration service for the project](https://hibernate-validator.ci.cloudbees.com/) is hosted at CloudBees. + +We provide a `.travis.yml` file so that you can enable CI for your GitHub fork by enabling the build in [your Travis CI account](https://travis-ci.org/). + ## Hibernate Validator URLs * [Home Page](http://hibernate.org/validator/) From 8a8c378151286f1d9999b4764978346fd234a7be Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Thu, 2 Jun 2016 11:54:10 +0200 Subject: [PATCH 050/189] HV-1068 Deriving MessageDescriptorFormatException from correct ValidationException type --- .../parser/MessageDescriptorFormatException.java | 4 +--- .../messageinterpolation/AbstractMessageInterpolator.java | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageDescriptorFormatException.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageDescriptorFormatException.java index 22b854d054..76e13bedd3 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageDescriptorFormatException.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageDescriptorFormatException.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.internal.engine.messageinterpolation.parser; -import javax.xml.bind.ValidationException; +import javax.validation.ValidationException; /** * Exception thrown in case the message descriptor is invalid, for example unbalanced braces or escape characters @@ -18,5 +18,3 @@ public MessageDescriptorFormatException(String s) { super( s ); } } - - diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java index bdb61fff7d..42efc16e59 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java @@ -13,8 +13,9 @@ import java.util.ResourceBundle; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.validation.MessageInterpolator; -import javax.xml.bind.ValidationException; +import javax.validation.ValidationException; import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; import org.hibernate.validator.internal.engine.messageinterpolation.LocalizedMessage; From 48a2c8d74de7a586fdbda455d97dde5785d88233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 3 May 2016 23:43:22 +0200 Subject: [PATCH 051/189] HV-501 Introduced an API for constraint definition --- .../validator/cfg/ConstraintMapping.java | 21 +- .../context/ConstraintDefinitionContext.java | 42 +++ .../cfg/context/ConstraintMappingTarget.java | 16 + .../cfg/context/ConstraintTarget.java | 30 ++ ...rossParameterConstraintMappingContext.java | 2 +- .../ParameterConstraintMappingContext.java | 2 +- .../PropertyConstraintMappingContext.java | 2 +- .../ReturnValueConstraintMappingContext.java | 2 +- .../context/TypeConstraintMappingContext.java | 2 +- .../context/ConstraintContextImplBase.java | 36 ++ .../ConstraintDefinitionContextImpl.java | 88 +++++ .../ConstraintMappingContextImplBase.java | 10 +- .../cfg/context/DefaultConstraintMapping.java | 41 ++- .../internal/engine/ValidatorFactoryImpl.java | 25 +- .../metadata/core/ConstraintHelper.java | 9 +- .../validator/internal/util/logging/Log.java | 7 +- .../internal/util/logging/Messages.java | 8 + .../internal/xml/XmlMappingParser.java | 13 +- .../cfg/ValidatorConstraintMappingTest.java | 308 ++++++++++++++++++ 19 files changed, 625 insertions(+), 39 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionContext.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintTarget.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/cfg/ValidatorConstraintMappingTest.java diff --git a/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java index 93ed6dca92..2c8e774dbb 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java @@ -6,6 +6,9 @@ */ package org.hibernate.validator.cfg; +import java.lang.annotation.Annotation; + +import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; import org.hibernate.validator.cfg.context.TypeConstraintMappingContext; /** @@ -14,6 +17,7 @@ * @author Hardy Ferentschik * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + * @author Yoann Rodiere */ public interface ConstraintMapping { /** @@ -22,9 +26,24 @@ public interface ConstraintMapping { * * @param The type to be configured. * @param beanClass The bean class on which to define constraints. All constraints defined after calling this method - * are added to the bean of the type {@code beanClass} until the next call of {@code type}. + * are added to the bean of the type {@code beanClass} until the next call of {@code type} or {@code annotation}. * * @return Instance allowing for defining constraints on the specified class. */ TypeConstraintMappingContext type(Class beanClass); + + /** + * Starts defining {@link javax.validation.ConstraintValidator}s to be executed for the specified constraint (i.e. annotation class). + * Each constraint may only be configured once within all constraint mappings used for configuring one validator + * factory. + * + * @param The annotation type to be configured. + * @param annotationClass The annotation class on which to define the validators. This type must be an + * {@code @interface} annotated with {@code javax.validation.Constraint}. All validators defined after calling + * this method are added to the annotation of the type {@code annotationClass} until the next call + * of {@code type} or {@code annotation}. + * + * @return Instance allowing for defining validators to be executed for the specified constraint. + */ + ConstraintDefinitionContext constraint(Class annotationClass); } diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionContext.java new file mode 100644 index 0000000000..d84128b570 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionContext.java @@ -0,0 +1,42 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.cfg.context; + +import java.lang.annotation.Annotation; + +import javax.validation.ConstraintValidator; + + +/** + * Constraint mapping creational context representing a constraint (i.e. annotation type). Allows to define which + * validators should validate this constraint. + * + * @param The annotation type represented by this context. + * + * @author Yoann Rodiere + */ +public interface ConstraintDefinitionContext extends ConstraintMappingTarget { + + /** + * Specifies whether validators already mapped to this constraint (i.e. defined in the annotation declaration + * through {@link javax.validation.Constraint#validatedBy()} or the validation engine defaults) should + * be included or not. + * + * @param includeExistingValidators Whether or not to use already-mapped validators when validating this constraint. + * @return This context for method chaining. + */ + ConstraintDefinitionContext includeExistingValidators(boolean includeExistingValidators); + + /** + * Adds a new validator to validate this constraint. + * + * @param validator The validator to add. + * + * @return This context for method chaining. + */ + ConstraintDefinitionContext validatedBy(Class> validator); +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java new file mode 100644 index 0000000000..2d65a98c15 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java @@ -0,0 +1,16 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.cfg.context; + +/** + * Facet of a constraint mapping creational context which allows to start a new constraint mapping or definition. + * + * @author Yoann Rodiere + */ +public interface ConstraintMappingTarget extends TypeTarget, ConstraintTarget { + +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintTarget.java new file mode 100644 index 0000000000..45dd08332f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintTarget.java @@ -0,0 +1,30 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.cfg.context; + +import java.lang.annotation.Annotation; + +/** + * Facet of a constraint mapping creational context which allows to the select the constraint (annotation type) to + * which the next operations shall apply. + * + * @author Yoann Rodiere + */ +public interface ConstraintTarget { + + /** + * Selects the constraint (i.e. annotation type) to which the next operations shall apply. A given constraint + * may only be configured once. + * + * @param The annotation type to select. + * @param annotationType The annotation type to select. This type must be an {@code @interface} annotated with + * {@code javax.validation.Constraint}. + * + * @return A creational context representing the selected constraint. + */ + ConstraintDefinitionContext constraint(Class annotationType); +} diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterConstraintMappingContext.java index 74c1cccbdd..0bf79bc5db 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterConstraintMappingContext.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/CrossParameterConstraintMappingContext.java @@ -13,5 +13,5 @@ * @author Gunnar Morling */ public interface CrossParameterConstraintMappingContext - extends TypeTarget, ConstructorTarget, MethodTarget, ParameterTarget, ReturnValueTarget, Constrainable, AnnotationIgnoreOptions { + extends ConstraintMappingTarget, ConstructorTarget, MethodTarget, ParameterTarget, ReturnValueTarget, Constrainable, AnnotationIgnoreOptions { } diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterConstraintMappingContext.java index b6395956a3..ea5754e59a 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterConstraintMappingContext.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ParameterConstraintMappingContext.java @@ -14,7 +14,7 @@ * @author Gunnar Morling * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI */ -public interface ParameterConstraintMappingContext extends TypeTarget, CrossParameterTarget, ParameterTarget, +public interface ParameterConstraintMappingContext extends ConstraintMappingTarget, CrossParameterTarget, ParameterTarget, ReturnValueTarget, ConstructorTarget, MethodTarget, Constrainable, Cascadable, Unwrapable, AnnotationIgnoreOptions { diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java index d4767511cf..cd598939f8 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/PropertyConstraintMappingContext.java @@ -15,7 +15,7 @@ * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI */ public interface PropertyConstraintMappingContext extends Constrainable, - TypeTarget, + ConstraintMappingTarget, PropertyTarget, ConstructorTarget, MethodTarget, diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueConstraintMappingContext.java index c32f5610e9..514410bab3 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueConstraintMappingContext.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ReturnValueConstraintMappingContext.java @@ -15,6 +15,6 @@ * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI */ public interface ReturnValueConstraintMappingContext - extends TypeTarget, ParameterTarget, CrossParameterTarget, ConstructorTarget, MethodTarget, Constrainable, Cascadable, Unwrapable, AnnotationIgnoreOptions { + extends ConstraintMappingTarget, ParameterTarget, CrossParameterTarget, ConstructorTarget, MethodTarget, Constrainable, Cascadable, Unwrapable, AnnotationIgnoreOptions { } diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java b/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java index c99b3d91b2..4655d18855 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/TypeConstraintMappingContext.java @@ -19,7 +19,7 @@ * @author Gunnar Morling */ public interface TypeConstraintMappingContext extends Constrainable>, - TypeTarget, + ConstraintMappingTarget, PropertyTarget, MethodTarget, ConstructorTarget, diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java new file mode 100644 index 0000000000..70ba90ebd8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java @@ -0,0 +1,36 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.cfg.context; + +import java.lang.annotation.Annotation; + +import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; +import org.hibernate.validator.cfg.context.TypeConstraintMappingContext; + +/** + * Base class for implementations of constraint-related context types. + * + * @author Gunnar Morling + * @author Yoann Rodiere + */ +abstract class ConstraintContextImplBase { + + protected final DefaultConstraintMapping mapping; + + public ConstraintContextImplBase(DefaultConstraintMapping mapping) { + this.mapping = mapping; + } + + public TypeConstraintMappingContext type(Class type) { + return mapping.type( type ); + } + + public ConstraintDefinitionContext constraint(Class annotationClass) { + return mapping.constraint( annotationClass ); + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java new file mode 100644 index 0000000000..4a413e8448 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java @@ -0,0 +1,88 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.cfg.context; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.lang.annotation.Annotation; +import java.util.Set; + +import javax.validation.ConstraintValidator; + +import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; +import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; + +/** + * Constraint definition context which allows to configure the validators to be used for a constraint's validation. + * + * @param The constraint (i.e. annotation type) represented by this context. + * + * @author Yoann Rodiere + */ +class ConstraintDefinitionContextImpl + extends ConstraintContextImplBase + implements ConstraintDefinitionContext { + + private final Class annotationType; + + private boolean includeExistingValidators = true; + + private final Set>> validatorTypes = newHashSet(); + + ConstraintDefinitionContextImpl(DefaultConstraintMapping mapping, Class annotationType) { + super( mapping ); + this.annotationType = annotationType; + } + + @Override + public ConstraintDefinitionContext includeExistingValidators(boolean includeExistingValidators) { + this.includeExistingValidators = includeExistingValidators; + return this; + } + + @Override + public ConstraintDefinitionContext validatedBy(Class> validator) { + validatorTypes.add( validator ); + return this; + } + + ConstraintDefinitionContributor build() { + return new ConstraintDefinitionContributorImpl( + annotationType, + includeExistingValidators, + validatorTypes ); + } + + private static final class ConstraintDefinitionContributorImpl + implements ConstraintDefinitionContributor { + + private final Class annotationType; + + private final boolean includeExistingValidators; + + private final Set>> validatorTypes; + + private ConstraintDefinitionContributorImpl(Class annotationType, boolean includeExistingValidators, + Set>> validatorTypes) { + super(); + this.annotationType = annotationType; + this.includeExistingValidators = includeExistingValidators; + this.validatorTypes = newHashSet( validatorTypes ); + } + + @Override + public void collectConstraintDefinitions(ConstraintDefinitionBuilder constraintDefinitionContributionBuilder) { + ConstraintDefinitionBuilderContext context = constraintDefinitionContributionBuilder + .constraint( annotationType ) + .includeExistingValidators( includeExistingValidators ); + + for ( Class> validatorTypes : validatorTypes ) { + context = context.validatedBy( validatorTypes ); + } + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java index 05776aed3b..034099152e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintMappingContextImplBase.java @@ -10,7 +10,6 @@ import java.util.Collections; import java.util.Set; -import org.hibernate.validator.cfg.context.TypeConstraintMappingContext; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; @@ -23,20 +22,15 @@ * * @author Gunnar Morling */ -abstract class ConstraintMappingContextImplBase { +abstract class ConstraintMappingContextImplBase extends ConstraintContextImplBase { - protected final DefaultConstraintMapping mapping; private final Set> constraints; ConstraintMappingContextImplBase(DefaultConstraintMapping mapping) { - this.mapping = mapping; + super( mapping ); this.constraints = newHashSet(); } - public TypeConstraintMappingContext type(Class type) { - return mapping.type( type ); - } - /** * Returns the type of constraints hosted on the element configured by this creational context. * diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java index 8ece7fabbd..8cd494fcaa 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java @@ -6,11 +6,17 @@ */ package org.hibernate.validator.internal.cfg.context; +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; + +import java.lang.annotation.Annotation; import java.util.Set; +import javax.validation.Constraint; import javax.validation.ParameterNameProvider; import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; import org.hibernate.validator.cfg.context.TypeConstraintMappingContext; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -18,9 +24,7 @@ import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; - -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; -import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; +import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; /** * Default implementation of {@link ConstraintMapping}. @@ -36,11 +40,15 @@ public class DefaultConstraintMapping implements ConstraintMapping { private final AnnotationProcessingOptionsImpl annotationProcessingOptions; private final Set> configuredTypes; private final Set> typeContexts; + private final Set> configuredConstraints; + private final Set> constraintContexts; public DefaultConstraintMapping() { this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); this.configuredTypes = newHashSet(); this.typeContexts = newHashSet(); + this.configuredConstraints = newHashSet(); + this.constraintContexts = newHashSet(); } @Override @@ -83,4 +91,31 @@ public Set> getBeanConfigurations(ConstraintHelper constrai return configurations; } + + @Override + public ConstraintDefinitionContext constraint(Class annotationClass) { + Contracts.assertNotNull( annotationClass, MESSAGES.annotationTypeMustNotBeNull() ); + Contracts.assertTrue( annotationClass.isAnnotationPresent( Constraint.class ), + MESSAGES.annotationTypeMustBeAnnotatedWithConstraint() ); + + if ( configuredConstraints.contains( annotationClass ) ) { + throw log.getConstraintHasAlreadyBeenConfiguredViaProgrammaticApiException( annotationClass.getName() ); + } + + ConstraintDefinitionContextImpl constraintContext = new ConstraintDefinitionContextImpl( this, annotationClass ); + constraintContexts.add( constraintContext ); + configuredConstraints.add( annotationClass ); + + return constraintContext; + } + + public Set getConstraintDefinitionContributors() { + Set contributors = newHashSet(); + + for ( ConstraintDefinitionContextImpl constraintContext : constraintContexts ) { + contributors.add( constraintContext.build() ); + } + + return contributors; + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 4a6b9f83ce..69003972a4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -162,6 +162,13 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { ); } + this.constraintMappings = Collections.unmodifiableSet( + getConstraintMappings( + configurationState, + externalClassLoader + ) + ); + Map properties = configurationState.getProperties(); boolean tmpFailFast = false; @@ -191,18 +198,12 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { registerCustomConstraintValidators( hibernateSpecificConfig, + constraintMappings, properties, externalClassLoader, constraintHelper ); } - this.constraintMappings = Collections.unmodifiableSet( - getConstraintMappings( - configurationState, - externalClassLoader - ) - ); - tmpValidatedValueHandlers.addAll( getPropertyConfiguredValidatedValueHandlers( properties, @@ -518,10 +519,18 @@ private static List getPropertyConfiguredConstr } private static void registerCustomConstraintValidators(ConfigurationImpl hibernateSpecificConfig, - Map properties, ClassLoader externalClassLoader, ConstraintHelper constraintHelper) { + Set constraintMappings, + Map properties, ClassLoader externalClassLoader, + ConstraintHelper constraintHelper) { for ( ConstraintDefinitionContributor contributor : hibernateSpecificConfig.getConstraintDefinitionContributors() ) { registerConstraintValidators( contributor, constraintHelper ); } + + for ( DefaultConstraintMapping constraintMapping : constraintMappings ) { + for (ConstraintDefinitionContributor contributor : constraintMapping.getConstraintDefinitionContributors() ) { + registerConstraintValidators( contributor, constraintHelper ); + } + } for ( ConstraintDefinitionContributor contributor : getPropertyConfiguredConstraintDefinitionContributors( properties, externalClassLoader diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index 43581c73fb..a0b30e298d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -139,6 +139,7 @@ public class ConstraintHelper { private final Map, List>> builtinConstraints; private final ValidatorClassMap validatorClasses = new ValidatorClassMap(); + private ConcurrentMap, Object> overridenAnnotations = newConcurrentHashMap(); public ConstraintHelper() { Map, List>> tmpConstraints = newHashMap(); @@ -331,7 +332,13 @@ public void putValidatorClasses(Class annotationType, } } - validatorClasses.put( annotationType, definitionClasses ); + Object previousOverride = overridenAnnotations.putIfAbsent( annotationType, annotationType ); + if ( previousOverride != null ) { + throw log.getOverridingConstraintDefinitionsMultipleTimesException( annotationType.getName() ); + } + else { + validatorClasses.put( annotationType, definitionClasses ); + } } /** diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index ff7cacae0a..28a3102f8a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -546,8 +546,8 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp ValidationException getValidateOnExecutionOnOverriddenOrInterfaceMethodException(Method m); @Message(id = 167, - value = "A given constraint definition can only be overridden in one mapping file. %1$s is overridden in multiple files") - ValidationException getOverridingConstraintDefinitionsInMultipleMappingFilesException(String constraintClass); + value = "A given constraint definition can only be overridden once (either through a XML file or the programmatic API). %1$s is overridden in multiple XML files and/or API calls") + ValidationException getOverridingConstraintDefinitionsMultipleTimesException(String constraintClass); @Message(id = 168, value = "The message descriptor '%1$s' contains an unbalanced meta character '%2$c' parameter.") @@ -640,4 +640,7 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp @LogMessage(level = WARN) @Message(id = 192, value = "Couldn't determine Java version from value %1s; Not enabling features requiring Java 8") void unknownJvmVersion(String vmVersionStr); + + @Message(id = 193, value = "%s is configured more than once via the programmatic constraint definition API.") + ValidationException getConstraintHasAlreadyBeenConfiguredViaProgrammaticApiException(String annotationClassName); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java index eafa33aaf6..f804951a81 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Messages.java @@ -92,6 +92,14 @@ public interface Messages { "ConstraintDefinitionContributors are a Hibernate Validator specific feature. All Bean Validation " + "features work as expected. See also https://hibernate.atlassian.net/browse/HV-1023.") String unableToUseResourceBundleAggregation(); + + @Message(value = "The annotation type must not be null when creating a constraint definition.", + format = Message.Format.NO_FORMAT) + String annotationTypeMustNotBeNull(); + + @Message(value = "The annotation type must be annotated with @javax.validation.Constraint when creating a constraint definition.", + format = Message.Format.NO_FORMAT) + String annotationTypeMustBeAnnotatedWithConstraint(); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java index 072a2cdaf3..bc638531bb 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java @@ -128,7 +128,6 @@ public final void parse(Set mappingStreams) { annotationProcessingOptions ); - Set alreadyProcessedConstraintDefinitions = newHashSet(); for ( InputStream in : mappingStreams ) { // check whether mark is supported, if so we can reset the stream in order to allow reuse of Configuration @@ -150,8 +149,7 @@ public final void parse(Set mappingStreams) { parseConstraintDefinitions( mapping.getConstraintDefinition(), - defaultPackage, - alreadyProcessedConstraintDefinitions + defaultPackage ); for ( BeanType bean : mapping.getBean() ) { @@ -254,16 +252,9 @@ private void processBeanType(ConstrainedTypeBuilder constrainedTypeBuilder, Cons @SuppressWarnings("unchecked") private void parseConstraintDefinitions(List constraintDefinitionList, - String defaultPackage, - Set alreadyProcessedConstraintDefinitions) { + String defaultPackage) { for ( ConstraintDefinitionType constraintDefinition : constraintDefinitionList ) { String annotationClassName = constraintDefinition.getAnnotation(); - if ( alreadyProcessedConstraintDefinitions.contains( annotationClassName ) ) { - throw log.getOverridingConstraintDefinitionsInMultipleMappingFilesException( annotationClassName ); - } - else { - alreadyProcessedConstraintDefinitions.add( annotationClassName ); - } Class clazz = classLoadingHelper.loadClass( annotationClassName, defaultPackage ); if ( !clazz.isAnnotation() ) { diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ValidatorConstraintMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ValidatorConstraintMappingTest.java new file mode 100644 index 0000000000..0fa1d53f54 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ValidatorConstraintMappingTest.java @@ -0,0 +1,308 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.cfg; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintViolation; +import javax.validation.Payload; +import javax.validation.UnexpectedTypeException; +import javax.validation.ValidationException; +import javax.validation.Validator; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidatorUtil; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Unit test for {@link ConstraintMapping#constraint(Class)} et al. + * + * @author Yoann Rodiere + */ +@TestForIssue( jiraKey = "HV-501") +public class ValidatorConstraintMappingTest { + + private HibernateValidatorConfiguration config; + private DefaultConstraintMapping mapping; + + @BeforeMethod + public void setUp() { + config = ValidatorUtil.getConfiguration( HibernateValidator.class ); + mapping = (DefaultConstraintMapping) config.createConstraintMapping(); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "HV[0-9]*: The annotation type must not be null when creating a constraint definition." + ) + public void testNullClass() { + mapping.constraint( null ); + } + + @Test( + expectedExceptions = IllegalArgumentException.class, + expectedExceptionsMessageRegExp = "HV[0-9]*: The annotation type must be annotated with @javax.validation.Constraint when creating a constraint definition." + ) + public void testNonConstraintAnnotation() { + mapping.constraint( NonConstraintAnnotation.class ); + } + + @Test + public void testConstraintMapping() { + mapping.constraint( ConstraintAnnotation.class ) + .validatedBy( NonDefaultLongValidator.class ); + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new ConstrainedLongFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, NonDefaultLongValidator.class ); + } + + @Test + public void testConstraintMappingDefaultsToIncludingExistingValidators() { + mapping.constraint( ConstraintAnnotation.class ) + .validatedBy( NonDefaultLongValidator.class ); + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new ConstrainedStringFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, DefaultStringValidator.class ); + } + + @Test + public void testConstraintMappingIncludingExistingValidators() { + mapping.constraint( ConstraintAnnotation.class ) + .includeExistingValidators( true ) + .validatedBy( NonDefaultLongValidator.class ); + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new ConstrainedStringFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, DefaultStringValidator.class ); + } + + @Test + public void testConstraintMappingExcludingExistingValidators() { + mapping.constraint( ConstraintAnnotation.class ) + .includeExistingValidators( false ) + .validatedBy( NonDefaultIntegerValidator.class ); + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new ConstrainedIntegerFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, NonDefaultIntegerValidator.class ); + } + + @Test + public void testConstraintMappingIncludingExistingValidatorsThenExcludingThem() { + mapping.constraint( ConstraintAnnotation.class ) + .includeExistingValidators( true ) + .validatedBy( NonDefaultLongValidator.class ) + .includeExistingValidators( false ) + .validatedBy( NonDefaultIntegerValidator.class ); + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new ConstrainedLongFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, NonDefaultLongValidator.class ); + + violations = validator.validate( new ConstrainedIntegerFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, NonDefaultIntegerValidator.class ); + } + + @Test( + expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = "HV[0-9]*:" + + " .*\\$ConstraintAnnotation is configured more than once via the programmatic constraint definition API." + ) + public void testMultipleDefinitionForSameConstraintOnSameConstraintMapping() { + mapping.constraint( ConstraintAnnotation.class ) + .validatedBy( NonDefaultLongValidator.class ) + .constraint( ConstraintAnnotation.class ) + .includeExistingValidators( false ) + .validatedBy( NonDefaultIntegerValidator.class ); + } + + @Test( + expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = "HV000167:.*" + ) + public void testMultipleDefinitionForSameConstraintOnDifferentConstraintMappings() { + mapping.constraint( ConstraintAnnotation.class ) + .validatedBy( NonDefaultLongValidator.class ); + + ConstraintMapping otherMapping = config.createConstraintMapping(); + otherMapping.constraint( ConstraintAnnotation.class ) + .includeExistingValidators( false ) + .validatedBy( NonDefaultIntegerValidator.class ); + + config.addMapping( mapping ); + config.addMapping( otherMapping ); + config.buildValidatorFactory().getValidator(); + } + + @Test( + expectedExceptions = UnexpectedTypeException.class, + expectedExceptionsMessageRegExp = "HV000150:.*" + ) + public void testMultipleValidatorsForSameType() { + mapping.constraint( ConstraintAnnotation.class ) + .includeExistingValidators( true ) + .validatedBy( NonDefaultIntegerValidator.class ); + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + validator.validate( new ConstrainedIntegerFieldBean() ); + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + private @interface NonConstraintAnnotation { + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { DefaultIntegerValidator.class, DefaultStringValidator.class }) + private @interface ConstraintAnnotation { + String message() default "Default ConstraintAnnotation violation message"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } + + /* + * Implementation note: we are compiling in Java 6 mode, so {@code @SafeVarargs} is of no use here. Thus, we work + * around the compiler warnings by not restraining the type of classes passed as parameters and just casting them. + */ + @SafeVarargs + @SuppressWarnings("unchecked") + private static void assertCorrectValidatorTypes(Set> violations, + Class... validatorClasses) { + List expectedMessages = new ArrayList(); + for ( Class validatorClass : validatorClasses ) { + String identifyingMessage = StubValidator.getIdentifyingMessage( + (Class>) validatorClass + ); + expectedMessages.add( identifyingMessage ); + } + assertCorrectConstraintViolationMessages( + violations, expectedMessages.toArray( new String[expectedMessages.size()] ) ); + } + + private static class StubValidator + implements ConstraintValidator { + + /** + * Returns a message that will allow to uniquely identify the originating validator from a constraint violation + * message. + *

+ * This is useful for testing purposes. + * + * @param validator The validator class + * @return The uniquely identifying message for this validator + */ + public static String getIdentifyingMessage( + @SuppressWarnings("rawtypes") Class validatorClass) { + return validatorClass.getName(); + } + + @Override + public void initialize(ConstraintAnnotation constraintAnnotation) { + // Nothing to do here + } + + @Override + public boolean isValid(T value, ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate( getIdentifyingMessage( getClass() ) ) + .addConstraintViolation(); + return false; + } + } + + public static class DefaultIntegerValidator extends StubValidator { + /* + * Nothing special here: everything is in the parent class, which uses getClass() to enable derived + * class-specific behavior. + */ + } + + public static class NonDefaultIntegerValidator extends StubValidator { + /* + * Nothing special here: everything is in the parent class, which uses getClass() to enable derived + * class-specific behavior. + */ + } + + public static class DefaultStringValidator extends StubValidator { + /* + * Nothing special here: everything is in the parent class, which uses getClass() to enable derived + * class-specific behavior. + */ + } + + public static class NonDefaultLongValidator extends StubValidator { + /* + * Nothing special here: everything is in the parent class, which uses getClass() to enable derived + * class-specific behavior. + */ + } + + private static class ConstrainedStringFieldBean { + + @ConstraintAnnotation + private String field; + } + + private static class ConstrainedIntegerFieldBean { + + @ConstraintAnnotation + private Integer field; + } + + private static class ConstrainedLongFieldBean { + + @ConstraintAnnotation + private Long field; + } + +} From 95209d46b2559cead17400c4c18c927870d7ba76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 4 May 2016 01:18:12 +0200 Subject: [PATCH 052/189] HV-501 Replaced the concept of ConstraintDefinitionContributor with the programmatic API --- documentation/src/main/asciidoc/ch02.asciidoc | 4 +- documentation/src/main/asciidoc/ch11.asciidoc | 73 ++++----------- .../Car.java => constraintapi/Bus.java} | 6 +- .../constraintapi/ConstraintApiTest.java | 37 ++++++++ .../ValidPassengerCount.java | 2 +- .../ValidPassengerCountValidator.java | 6 +- .../constraintdefinition/CarTest.java | 55 ------------ .../constraintdefinition/Person.java | 5 -- .../HibernateValidatorConfiguration.java | 28 +----- .../hibernate/validator/constraints/URL.java | 24 +++-- .../ConstraintDefinitionContextImpl.java | 41 ++------- .../cfg/context/DefaultConstraintMapping.java | 10 +-- .../internal/engine/ConfigurationImpl.java | 25 ++---- ...derBasedConstraintMappingContributor.java} | 18 ++-- .../internal/engine/ValidatorFactoryImpl.java | 89 ++++--------------- ...onstraintDefinitionBuilderContextImpl.java | 56 ------------ .../ConstraintDefinitionBuilderImpl.java | 44 --------- .../ConstraintDefinitionContributor.java | 59 ------------ .../constraintdefinition/package-info.java | 11 --- .../constraintvalidator/AcmeConstraint.java | 52 ----------- .../AcmeConstraintDefinitionContributor.java | 36 -------- .../AcmeConstraintWithDefaultValidator.java | 51 ----------- .../ConstraintDefinitionContributorTest.java | 88 +----------------- .../hv/URLValidatorTest.java | 19 ++-- 24 files changed, 132 insertions(+), 707 deletions(-) rename documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/{constraintdefinition/Car.java => constraintapi/Bus.java} (88%) rename documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/{constraintdefinition => constraintapi}/ValidPassengerCount.java (98%) rename documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/{constraintdefinition => constraintapi}/ValidPassengerCountValidator.java (80%) delete mode 100644 documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/CarTest.java delete mode 100644 documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/Person.java rename engine/src/main/java/org/hibernate/validator/internal/engine/{ServiceLoaderBasedConstraintDefinitionContributor.java => ServiceLoaderBasedConstraintMappingContributor.java} (80%) delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderContextImpl.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderImpl.java delete mode 100644 engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/ConstraintDefinitionContributor.java delete mode 100644 engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/package-info.java delete mode 100644 engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraint.java delete mode 100644 engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintDefinitionContributor.java delete mode 100644 engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintWithDefaultValidator.java diff --git a/documentation/src/main/asciidoc/ch02.asciidoc b/documentation/src/main/asciidoc/ch02.asciidoc index 91f50311fc..de129d5f56 100644 --- a/documentation/src/main/asciidoc/ch02.asciidoc +++ b/documentation/src/main/asciidoc/ch02.asciidoc @@ -1010,8 +1010,8 @@ regular expression (including regular expression flags) which the URL must match. Per default this constraint used the `java.net.URL` constructor to verify whether a given string represents a valid URL. A regular expression based version is also available - `RegexpURLValidator` - which can be configured via XML -(see <>) or a `ConstraintDefinitionContributor` -(see <>). +(see <>) or the programmatic API +(see <>). |None |=============== diff --git a/documentation/src/main/asciidoc/ch11.asciidoc b/documentation/src/main/asciidoc/ch11.asciidoc index 24e2fa9ae7..d1cc5572b6 100644 --- a/documentation/src/main/asciidoc/ch11.asciidoc +++ b/documentation/src/main/asciidoc/ch11.asciidoc @@ -800,83 +800,40 @@ entries from all the bundles with this name found on the classpath in addition t This mechanism is also helpful when creating large multi-module applications: Instead of putting all the constraint messages into one single bundle, you can have one resource bundle per module containing only those messages of that module. -[[section-constraint-definition-contributor]] -==== Constraint definitions via `ConstraintDefinitionContributor` +[[section-programmatic-constraint-definition]] +==== Constraint definitions via programmatic constraint declaration API While the service loader approach works in many scenarios, but not in all (think for example OSGi where service files are not visible), there is yet another way of contributing constraint -definitions. You can provide one or more implementations of `ConstraintDefinitionContributor` to -`HibernateConfiguration` during bootstrapping of the `ValidatorFactory` - see -<>. +definitions. You can use the programmatic constraint declaration API - see +<>. -[[example-using-constraint-definition-contributor]] -.Using `ConstraintDefinitionContributor` to register constraint definitions -==== -[source, JAVA] +[[example-using-constraint-definition-api]] +.Using the programmatic constraint declaration API to register constraint definitions +======== +[source, JAVA, indent=0] ---- -public class CarTest { - - private static Validator validator; - - public static class MyConstraintDefinitionContributor - implements ConstraintDefinitionContributor { - - @Override - public void collectConstraintDefinitions(ConstraintDefinitionBuilder builder) { - builder.constraint( ValidPassengerCount.class ) - .validatedBy( ValidPassengerCountValidator.class ); - } - } - - @BeforeClass - public static void setUpValidator() { - - HibernateValidatorConfiguration configuration = Validation - .byProvider( HibernateValidator.class ) - .configure(); - - ConstraintDefinitionContributor contributor = new MyConstraintDefinitionContributor(); - configuration.addConstraintDefinitionContributor( contributor ); - - validator = configuration.buildValidatorFactory().getValidator(); - } - - // ... -} +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java[tags=constraintDefinition] ---- ==== -Instead of programmatically registering `ConstraintDefinitionContributor` instances, the -fully-qualified classnames of one or more implementations can be specified via the -property `hibernate.validator.constraint_definition_contributors`. This can be useful when +Instead of directly adding a constraint mapping to the configuration object, you may use a `ConstraintMappingContributor` +as detailed in <>. This can be useful when configuring the default validator factory using _META-INF/validation.xml_ (see <>). [TIP] ==== -One use case for `ConstraintDefinitionContributor` is the ability to specify an alternative +One use case for registering constraint definitions through the programmatic API is the ability to specify an alternative constraint validator for the `@URL` constraint. Historically, Hibernate Validator's default constraint validator for this constraint uses the `java.net.URL` constructor to validate an URL. However, there is also a purely regular expression based version available which can be configured using a `ConstraintDefinitionContributor`: -.Using a `ConstraintDefinitionContributor` to register a regular expression based constraint definition for `@URL` -[source, JAVA] +.Using the programmatic constraint declaration API to register a regular expression based constraint definition for `@URL` +[source, JAVA, indent=0] ---- -HibernateValidatorConfiguration configuration = Validation - .byProvider( HibernateValidator.class ) - .configure(); - -configuration.addConstraintDefinitionContributor( - new ConstraintDefinitionContributor() { - @Override - public void collectConstraintDefinitions(ConstraintDefinitionBuilder builder) { - builder.constraint( URL.class ) - .includeExistingValidators( false ) - .validatedBy( RegexpURLValidator.class ); - } - } -); +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java[tags=urlValidationOverride] ---- ==== diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/Car.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/Bus.java similarity index 88% rename from documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/Car.java rename to documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/Bus.java index a31c469daa..d69d88d58a 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/Car.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/Bus.java @@ -1,14 +1,14 @@ -package org.hibernate.validator.referenceguide.chapter11.constraintdefinition; +package org.hibernate.validator.referenceguide.chapter11.constraintapi; import java.util.ArrayList; import java.util.List; @ValidPassengerCount -public class Car { +public class Bus { private final int seatCount; private final List passengers; - public Car(int seatCount) { + public Bus(int seatCount) { this.seatCount = seatCount; this.passengers = new ArrayList( seatCount ); } diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java index 6d657e998c..f6664ac18d 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java @@ -13,6 +13,8 @@ import org.hibernate.validator.cfg.defs.MaxDef; import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.cfg.defs.SizeDef; +import org.hibernate.validator.constraints.URL; +import org.hibernate.validator.constraintvalidators.RegexpURLValidator; import org.junit.Test; import static java.lang.annotation.ElementType.FIELD; @@ -136,6 +138,41 @@ public void defaultGroupSequence() { //end::defaultGroupSequence[] } + @Test + public void constraintDefinition() { + HibernateValidatorConfiguration configuration = Validation + .byProvider( HibernateValidator.class ) + .configure(); + + //tag::constraintDefinition[] + ConstraintMapping constraintMapping = configuration.createConstraintMapping(); + + constraintMapping + .constraint( ValidPassengerCount.class ) + .validatedBy( ValidPassengerCountValidator.class ); + //end::constraintDefinition[] + + configuration.addMapping( constraintMapping ); + } + + @Test + public void urlValidationOverride() { + HibernateValidatorConfiguration configuration = Validation + .byProvider( HibernateValidator.class ) + .configure(); + + //tag::urlValidationOverride[] + ConstraintMapping constraintMapping = configuration.createConstraintMapping(); + + constraintMapping + .constraint( URL.class ) + .includeExistingValidators( false ) + .validatedBy( RegexpURLValidator.class ); + //end::urlValidationOverride[] + + configuration.addMapping( constraintMapping ); + } + public interface PersonDefault { } } diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/ValidPassengerCount.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ValidPassengerCount.java similarity index 98% rename from documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/ValidPassengerCount.java rename to documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ValidPassengerCount.java index 9d4e9d4878..d61b6b3120 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/ValidPassengerCount.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ValidPassengerCount.java @@ -1,4 +1,4 @@ -package org.hibernate.validator.referenceguide.chapter11.constraintdefinition; +package org.hibernate.validator.referenceguide.chapter11.constraintapi; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/ValidPassengerCountValidator.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ValidPassengerCountValidator.java similarity index 80% rename from documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/ValidPassengerCountValidator.java rename to documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ValidPassengerCountValidator.java index 9fff4af599..349f1b708b 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/ValidPassengerCountValidator.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ValidPassengerCountValidator.java @@ -1,4 +1,4 @@ -package org.hibernate.validator.referenceguide.chapter11.constraintdefinition; +package org.hibernate.validator.referenceguide.chapter11.constraintapi; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; @@ -6,14 +6,14 @@ /** * @author Hardy Ferentschik */ -public class ValidPassengerCountValidator implements ConstraintValidator { +public class ValidPassengerCountValidator implements ConstraintValidator { @Override public void initialize(ValidPassengerCount constraintAnnotation) { } @Override - public boolean isValid(Car car, ConstraintValidatorContext context) { + public boolean isValid(Bus car, ConstraintValidatorContext context) { if ( car == null ) { return true; } diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/CarTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/CarTest.java deleted file mode 100644 index f5c2307a18..0000000000 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/CarTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.hibernate.validator.referenceguide.chapter11.constraintdefinition; - -import java.util.Set; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; - -import org.junit.BeforeClass; -import org.junit.Test; - -import org.hibernate.validator.HibernateValidator; -import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; - -import static org.junit.Assert.assertEquals; - -public class CarTest { - - private static Validator validator; - - public static class MyConstraintDefinitionContributor - implements ConstraintDefinitionContributor { - - @Override - public void collectConstraintDefinitions(ConstraintDefinitionBuilder builder) { - builder.constraint( ValidPassengerCount.class ) - .validatedBy( ValidPassengerCountValidator.class ); - } - } - - @BeforeClass - public static void setUpValidator() { - - HibernateValidatorConfiguration configuration = Validation - .byProvider( HibernateValidator.class ) - .configure(); - - ConstraintDefinitionContributor contributor = new MyConstraintDefinitionContributor(); - configuration.addConstraintDefinitionContributor( contributor ); - - validator = configuration.buildValidatorFactory().getValidator(); - } - - @Test - public void testProgrammaticRegistrationOfConstraintValidator() throws Exception { - Car car = new Car( 2 ); - car.addPassenger( new Person() ); - car.addPassenger( new Person() ); - car.addPassenger( new Person() ); - Set> violations = validator.validate( car ); - - assertEquals( 1, violations.size() ); - } -} - diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/Person.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/Person.java deleted file mode 100644 index 73cc4b26b2..0000000000 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintdefinition/Person.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.hibernate.validator.referenceguide.chapter11.constraintdefinition; - -public class Person { -} - diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index 29df15504d..984fa2d2c7 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -9,7 +9,7 @@ import javax.validation.Configuration; import org.hibernate.validator.cfg.ConstraintMapping; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; +import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.time.TimeProvider; import org.hibernate.validator.spi.valuehandling.ValidatedValueUnwrapper; @@ -58,14 +58,6 @@ public interface HibernateValidatorConfiguration extends Configuration handler); /** - * @return the default {@link org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor}. Never {@code null}. - * - * @since 5.2 - */ - ConstraintDefinitionContributor getDefaultConstraintDefinitionContributor(); - - /** - * Registers the given {@code ConstraintDefinitionContributor} with the bootstrapped validator factory. - * - * @param contributor the {@code ConstraintDefinitionContributor} to register. Cannot be {@code null}. - * - * @return {@code this} following the chaining method pattern + * @return the default {@link org.hibernate.validator.spi.cfg.ConstraintMappingContributor}. Never {@code null}. * - * @hv.experimental This API is considered experimental and may change in future revisions - * @since 5.2 + * @since 5.3 */ - HibernateValidatorConfiguration addConstraintDefinitionContributor(ConstraintDefinitionContributor contributor); + ConstraintMappingContributor getDefaultConstraintMappingContributor(); /** * Sets the class loader to be used for loading user-provided resources: diff --git a/engine/src/main/java/org/hibernate/validator/constraints/URL.java b/engine/src/main/java/org/hibernate/validator/constraints/URL.java index 684bcb34cb..625412e5eb 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/URL.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/URL.java @@ -40,22 +40,18 @@ *

*

* In case URLs with non default protocol handlers need to be validated, Hibernate Validator can be configured to use - * a regular expression based URL validator only. This can be done programmatically via a {@code ConstraintDefinitionContributor}: + * a regular expression based URL validator only. This can be done programmatically via a {@code org.hibernate.validator.cfg.ConstraintMapping}: *

  * {@code
- * HibernateValidatorConfiguration configuration = Validation
- *         .byProvider( HibernateValidator.class )
- *         .configure();
+ * HibernateValidatorConfiguration config = ValidatorUtil.getConfiguration( HibernateValidator.class );
  *
- * configuration.addConstraintDefinitionContributor(
- *     new ConstraintDefinitionContributor() {
- *         public void collectConstraintDefinitions(ConstraintDefinitionBuilder builder) {
- *             builder.constraint( URL.class )
- *                 .includeExistingValidators( false )
- *                 .validatedBy( RegexpURLValidator.class );
- *         }
- *     }
- * );
+ * ConstraintMapping constraintMapping = config.createConstraintMapping();
+ * constraintMapping
+ *     .constraint( URL.class )
+ *     .includeExistingValidators( false )
+ *     .validatedBy( RegexpURLValidator.class );
+ *
+ * config.addMapping( constraintMapping );
  * }
  * 
* or via a constraint mapping configuration: @@ -76,7 +72,7 @@ *
* * @see RFC2396 - * @see org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor + * @see org.hibernate.validator.cfg.ConstraintMapping#constraint(Class) * @author Hardy Ferentschik */ @Documented diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java index 4a413e8448..6892a57d0c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintDefinitionContextImpl.java @@ -14,7 +14,8 @@ import javax.validation.ConstraintValidator; import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; +import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; +import org.hibernate.validator.internal.util.CollectionHelper; /** * Constraint definition context which allows to configure the validators to be used for a constraint's validation. @@ -50,39 +51,11 @@ public ConstraintDefinitionContext validatedBy(Class( + @SuppressWarnings("unchecked") + ConstraintDefinitionContribution build() { + return new ConstraintDefinitionContribution( annotationType, - includeExistingValidators, - validatorTypes ); - } - - private static final class ConstraintDefinitionContributorImpl - implements ConstraintDefinitionContributor { - - private final Class annotationType; - - private final boolean includeExistingValidators; - - private final Set>> validatorTypes; - - private ConstraintDefinitionContributorImpl(Class annotationType, boolean includeExistingValidators, - Set>> validatorTypes) { - super(); - this.annotationType = annotationType; - this.includeExistingValidators = includeExistingValidators; - this.validatorTypes = newHashSet( validatorTypes ); - } - - @Override - public void collectConstraintDefinitions(ConstraintDefinitionBuilder constraintDefinitionContributionBuilder) { - ConstraintDefinitionBuilderContext context = constraintDefinitionContributionBuilder - .constraint( annotationType ) - .includeExistingValidators( includeExistingValidators ); - - for ( Class> validatorTypes : validatorTypes ) { - context = context.validatedBy( validatorTypes ); - } - } + CollectionHelper.newArrayList( validatorTypes ), + includeExistingValidators ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java index 8cd494fcaa..15408b2a58 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java @@ -18,13 +18,13 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; import org.hibernate.validator.cfg.context.TypeConstraintMappingContext; +import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; /** * Default implementation of {@link ConstraintMapping}. @@ -109,13 +109,13 @@ public ConstraintDefinitionContext constraint(Class return constraintContext; } - public Set getConstraintDefinitionContributors() { - Set contributors = newHashSet(); + public Set> getConstraintDefinitionContributions() { + Set> contributions = newHashSet(); for ( ConstraintDefinitionContextImpl constraintContext : constraintContexts ) { - contributors.add( constraintContext.build() ); + contributions.add( constraintContext.build() ); } - return contributors; + return contributions; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java index 7e73a778ab..379cc39858 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + import javax.validation.BootstrapConfiguration; import javax.validation.ConstraintValidatorFactory; import javax.validation.MessageInterpolator; @@ -42,7 +43,7 @@ import org.hibernate.validator.internal.xml.ValidationXmlParser; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; +import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.time.TimeProvider; import org.hibernate.validator.spi.valuehandling.ValidatedValueUnwrapper; @@ -75,7 +76,7 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi private final TraversableResolver defaultTraversableResolver; private final ConstraintValidatorFactory defaultConstraintValidatorFactory; private final ParameterNameProvider defaultParameterNameProvider; - private final ConstraintDefinitionContributor defaultConstraintDefinitionContributor; + private final ConstraintMappingContributor defaultConstraintMappingContributor; private ValidationProviderResolver providerResolver; private final ValidationBootstrapParameters validationBootstrapParameters; @@ -86,7 +87,6 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi // HV-specific options private final Set programmaticMappings = newHashSet(); private boolean failFast; - private final Set constraintDefinitionContributors = newHashSet(); private final List> validatedValueHandlers = newArrayList(); private ClassLoader externalClassLoader; private TimeProvider timeProvider; @@ -127,10 +127,9 @@ private ConfigurationImpl() { this.defaultConstraintValidatorFactory = new ConstraintValidatorFactoryImpl(); this.defaultParameterNameProvider = new DefaultParameterNameProvider(); this.defaultMessageInterpolator = new ResourceBundleMessageInterpolator( defaultResourceBundleLocator ); - this.defaultConstraintDefinitionContributor = new ServiceLoaderBasedConstraintDefinitionContributor( + this.defaultConstraintMappingContributor = new ServiceLoaderBasedConstraintMappingContributor( typeResolutionHelper ); - this.addConstraintDefinitionContributor( defaultConstraintDefinitionContributor ); } private ValidatedValueUnwrapper createJavaFXUnwrapperClass(TypeResolutionHelper typeResolutionHelper) { @@ -278,20 +277,8 @@ public HibernateValidatorConfiguration addValidatedValueHandler(ValidatedValueUn } @Override - public ConstraintDefinitionContributor getDefaultConstraintDefinitionContributor() { - return defaultConstraintDefinitionContributor; - } - - public Set getConstraintDefinitionContributors() { - return constraintDefinitionContributors; - } - - @Override - public final HibernateValidatorConfiguration addConstraintDefinitionContributor(ConstraintDefinitionContributor contributor) { - Contracts.assertNotNull( contributor, MESSAGES.parameterMustNotBeNull( "contributor" ) ); - constraintDefinitionContributors.add( contributor ); - - return this; + public ConstraintMappingContributor getDefaultConstraintMappingContributor() { + return defaultConstraintMappingContributor; } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintDefinitionContributor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java similarity index 80% rename from engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintDefinitionContributor.java rename to engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java index d95501410a..b56fd410da 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintDefinitionContributor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java @@ -12,31 +12,34 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; + import javax.validation.ConstraintValidator; import com.fasterxml.classmate.ResolvedType; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.context.ConstraintDefinitionContext; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.privilegedactions.GetConstraintValidatorList; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; +import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; /** * @author Hardy Ferentschik */ -public class ServiceLoaderBasedConstraintDefinitionContributor implements ConstraintDefinitionContributor { +public class ServiceLoaderBasedConstraintMappingContributor implements ConstraintMappingContributor { /** * Used for resolving type parameters. Thread-safe. */ private final TypeResolutionHelper typeResolutionHelper; - public ServiceLoaderBasedConstraintDefinitionContributor(TypeResolutionHelper typeResolutionHelper) { + public ServiceLoaderBasedConstraintMappingContributor(TypeResolutionHelper typeResolutionHelper) { this.typeResolutionHelper = typeResolutionHelper; } @Override - public void collectConstraintDefinitions(ConstraintDefinitionBuilder constraintDefinitionContributionBuilder) { + public void createConstraintMappings(ConstraintMappingBuilder builder) { Map, List>> customValidators = newHashMap(); // find additional constraint validators via the Java ServiceLoader mechanism @@ -55,16 +58,17 @@ public void collectConstraintDefinitions(ConstraintDefinitionBuilder constraintD validators.add( constraintValidatorClass ); } + ConstraintMapping constraintMapping = builder.addConstraintMapping(); for ( Map.Entry, List>> entry : customValidators.entrySet() ) { - registerConstraintDefinition( constraintDefinitionContributionBuilder, entry.getKey(), entry.getValue() ); + registerConstraintDefinition( constraintMapping, entry.getKey(), entry.getValue() ); } } @SuppressWarnings("unchecked") - private void registerConstraintDefinition(ConstraintDefinitionBuilder builder, + private void registerConstraintDefinition(ConstraintMapping constraintMapping, Class constraintType, List> validatorTypes) { - ConstraintDefinitionBuilderContext context = builder + ConstraintDefinitionContext context = constraintMapping .constraint( (Class) constraintType ) .includeExistingValidators( true ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 69003972a4..000267f69f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -27,7 +27,6 @@ import org.hibernate.validator.HibernateValidatorFactory; import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; -import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionBuilderImpl; import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.time.DefaultTimeProvider; @@ -43,7 +42,6 @@ import org.hibernate.validator.internal.util.privilegedactions.LoadClass; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; import org.hibernate.validator.spi.time.TimeProvider; import org.hibernate.validator.spi.valuehandling.ValidatedValueUnwrapper; @@ -195,14 +193,9 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { .isAllowParallelMethodsDefineParameterConstraints(); tmpValidatedValueHandlers.addAll( hibernateSpecificConfig.getValidatedValueHandlers() ); - - registerCustomConstraintValidators( - hibernateSpecificConfig, - constraintMappings, - properties, - externalClassLoader, constraintHelper - ); } + + registerCustomConstraintValidators( constraintMappings, constraintHelper ); tmpValidatedValueHandlers.addAll( getPropertyConfiguredValidatedValueHandlers( @@ -252,14 +245,20 @@ private static ClassLoader getExternalClassLoader(ConfigurationState configurati } private static Set getConstraintMappings(ConfigurationState configurationState, ClassLoader externalClassLoader) { - Set constraintMappings; + Set constraintMappings = newHashSet(); - // programmatic config + if ( configurationState instanceof ConfigurationImpl ) { - constraintMappings = ( (ConfigurationImpl) configurationState ).getProgrammaticMappings(); - } - else { - constraintMappings = newHashSet(); + ConfigurationImpl hibernateConfiguration = (ConfigurationImpl) configurationState; + + // default config + ConstraintMappingContributor defaultContributor = hibernateConfiguration.getDefaultConstraintMappingContributor(); + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder(); + defaultContributor.createConstraintMappings( builder ); + constraintMappings.addAll( builder.mappings ); + + // programmatic config + constraintMappings.addAll( hibernateConfiguration.getProgrammaticMappings() ); } // XML @@ -485,67 +484,13 @@ private static List> getPropertyConfiguredValidatedVa return handlers; } - /** - * Returns a list with {@link org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor} instances configured via the - * {@link HibernateValidatorConfiguration#CONSTRAINT_DEFINITION_CONTRIBUTORS} property. - * - * @param properties the properties used to bootstrap the factory - * - * @return a list with property-configured {@link org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor}s. May be empty but never {@code null}. - */ - private static List getPropertyConfiguredConstraintDefinitionContributors( - Map properties, ClassLoader externalClassLoader) { - String propertyValue = properties.get( HibernateValidatorConfiguration.CONSTRAINT_DEFINITION_CONTRIBUTORS ); - - if ( propertyValue == null || propertyValue.isEmpty() ) { - return Collections.emptyList(); - } - - String[] constraintDefinitionContributorNames = propertyValue.split( "," ); - List constraintDefinitionContributors = newArrayList( - constraintDefinitionContributorNames.length - ); - - for ( String fqcn : constraintDefinitionContributorNames ) { - @SuppressWarnings("unchecked") - Class contributorType = (Class) - run( LoadClass.action( fqcn, externalClassLoader ) ); - constraintDefinitionContributors.add( - run( NewInstance.action( contributorType, "constraint definition contributor class" ) ) - ); - } - - return constraintDefinitionContributors; - } - - private static void registerCustomConstraintValidators(ConfigurationImpl hibernateSpecificConfig, - Set constraintMappings, - Map properties, ClassLoader externalClassLoader, + private static void registerCustomConstraintValidators(Set constraintMappings, ConstraintHelper constraintHelper) { - for ( ConstraintDefinitionContributor contributor : hibernateSpecificConfig.getConstraintDefinitionContributors() ) { - registerConstraintValidators( contributor, constraintHelper ); - } - for ( DefaultConstraintMapping constraintMapping : constraintMappings ) { - for (ConstraintDefinitionContributor contributor : constraintMapping.getConstraintDefinitionContributors() ) { - registerConstraintValidators( contributor, constraintHelper ); + for (ConstraintDefinitionContribution contribution : constraintMapping.getConstraintDefinitionContributions() ) { + processConstraintDefinitionContribution( contribution, constraintHelper ); } } - - for ( ConstraintDefinitionContributor contributor : getPropertyConfiguredConstraintDefinitionContributors( - properties, externalClassLoader - ) ) { - registerConstraintValidators( contributor, constraintHelper ); - } - } - - private static void registerConstraintValidators(ConstraintDefinitionContributor contributor, ConstraintHelper constraintHelper) { - ConstraintDefinitionBuilderImpl builder = new ConstraintDefinitionBuilderImpl(); - contributor.collectConstraintDefinitions( builder ); - - for ( ConstraintDefinitionContribution constraintDefinitionContribution : builder.getConstraintValidatorContributions() ) { - processConstraintDefinitionContribution( constraintDefinitionContribution, constraintHelper ); - } } private static void processConstraintDefinitionContribution(ConstraintDefinitionContribution constraintDefinitionContribution, ConstraintHelper constraintHelper) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderContextImpl.java deleted file mode 100644 index 621023643a..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderContextImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.internal.engine.constraintdefinition; - -import java.lang.annotation.Annotation; -import java.util.List; -import javax.validation.ConstraintValidator; - -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor.ConstraintDefinitionBuilder; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor.ConstraintDefinitionBuilderContext; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - -/** - * @author Gunnar Morling - */ -class ConstraintDefinitionBuilderContextImpl implements ConstraintDefinitionBuilderContext { - - private final ConstraintDefinitionBuilder builder; - private final Class constraintType; - private boolean includeExistingValidators = true; - private final List>> validatorTypes = newArrayList(); - - public ConstraintDefinitionBuilderContextImpl(ConstraintDefinitionBuilder builder, Class constraintType) { - this.builder = builder; - this.constraintType = constraintType; - } - - @Override - public ConstraintDefinitionBuilderContext includeExistingValidators(boolean include) { - this.includeExistingValidators = include; - return this; - } - - @Override - public ConstraintDefinitionBuilderContext validatedBy(Class> validatorType) { - validatorTypes.add( validatorType ); - return this; - } - - @Override - public ConstraintDefinitionBuilderContext constraint(Class constraintType) { - return builder.constraint( constraintType ); - } - - ConstraintDefinitionContribution build() { - return new ConstraintDefinitionContribution( constraintType, validatorTypes, includeExistingValidators ); - } -} - - - diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderImpl.java deleted file mode 100644 index 27523bb41d..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionBuilderImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.internal.engine.constraintdefinition; - -import java.lang.annotation.Annotation; -import java.util.List; - -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor.ConstraintDefinitionBuilder; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor.ConstraintDefinitionBuilderContext; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - -/** - * @author Gunnar Morling - */ -public class ConstraintDefinitionBuilderImpl implements ConstraintDefinitionBuilder { - - private final List> contexts = newArrayList(); - - @Override - public ConstraintDefinitionBuilderContext constraint(Class constraintType) { - ConstraintDefinitionBuilderContextImpl context = new ConstraintDefinitionBuilderContextImpl( - this, constraintType - ); - contexts.add( context ); - return context; - } - - public List> getConstraintValidatorContributions() { - List> contributions = newArrayList( contexts.size() ); - - for ( ConstraintDefinitionBuilderContextImpl context : contexts ) { - contributions.add( context.build() ); - } - - return contributions; - } -} - - diff --git a/engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/ConstraintDefinitionContributor.java b/engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/ConstraintDefinitionContributor.java deleted file mode 100644 index fae9331120..0000000000 --- a/engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/ConstraintDefinitionContributor.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.spi.constraintdefinition; - -import java.lang.annotation.Annotation; -import javax.validation.ConstraintValidator; - -/** - * A {@code ConstraintDefinitionContributor} allows for the contribution of custom constraint validator instances. - * - * This is a Hibernate Validator specific feature and can be configured via - * {@link org.hibernate.validator.HibernateValidatorConfiguration}. - * - *

- * The default implementation uses Java's {@code ServiceLoader} approach to discover custom constraint validator - * implementations. - *

- * - * @author Hardy Ferentschik - * @hv.experimental This API is considered experimental and may change in future revisions - * - * @since 5.2 - */ -public interface ConstraintDefinitionContributor { - - /** - * Callback for registering additional validators for given constraints. Use the provided builder object to - * configure the validators. - * - * @param constraintDefinitionContributionBuilder Builder to contribute constraint validators using a fluent API. - * - */ - void collectConstraintDefinitions(ConstraintDefinitionBuilder constraintDefinitionContributionBuilder); - - /** - * Allows to register the validators applying for given constraints, following a fluent API pattern. - */ - public interface ConstraintDefinitionBuilder { - -
ConstraintDefinitionBuilderContext constraint(Class constraintType); - } - - /** - * Allows to register the validators applying for one given constraint. - * - * @param the constraint type - */ - public interface ConstraintDefinitionBuilderContext { - ConstraintDefinitionBuilderContext validatedBy(Class> validatorType); - - ConstraintDefinitionBuilderContext includeExistingValidators(boolean include); - - ConstraintDefinitionBuilderContext constraint(Class constraintType); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/package-info.java b/engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/package-info.java deleted file mode 100644 index 2840de2acf..0000000000 --- a/engine/src/main/java/org/hibernate/validator/spi/constraintdefinition/package-info.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ - -/** - * SPI for discovering custom constraint validator implementations. - */ -package org.hibernate.validator.spi.constraintdefinition; diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraint.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraint.java deleted file mode 100644 index ec4bd3b3ec..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraint.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.test.constraintvalidator; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * @author Hardy Ferentschik - */ -@Documented -@Constraint(validatedBy = { }) -@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) -@Retention(RUNTIME) -public @interface AcmeConstraint { - String message() default "acme"; - - Class[] groups() default { }; - - Class[] payload() default { }; - - - public class AcmeConstraintValidator implements ConstraintValidator { - - @Override - public void initialize(AcmeConstraint constraintAnnotation) { - } - - @Override - public boolean isValid(Object value, ConstraintValidatorContext context) { - return false; - } - } -} - - diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintDefinitionContributor.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintDefinitionContributor.java deleted file mode 100644 index 5139873912..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintDefinitionContributor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.test.constraintvalidator; - -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; - -/** - * @author Hardy Ferentschik - */ -public class AcmeConstraintDefinitionContributor implements ConstraintDefinitionContributor { - private final boolean keepDefaults; - - public AcmeConstraintDefinitionContributor() { - this( true ); - } - - public AcmeConstraintDefinitionContributor(boolean keepDefaults) { - this.keepDefaults = keepDefaults; - } - - @Override - public void collectConstraintDefinitions(ConstraintDefinitionBuilder constraintDefinitionContributionBuilder) { - constraintDefinitionContributionBuilder.constraint( AcmeConstraint.class ) - .includeExistingValidators( keepDefaults ) - .validatedBy( AcmeConstraint.AcmeConstraintValidator.class ) - .constraint( AcmeConstraintWithDefaultValidator.class ) - .includeExistingValidators( keepDefaults ) - .validatedBy( AcmeConstraintWithDefaultValidator.DefaultAcmeConstraintValidator.class ); - } -} - - diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintWithDefaultValidator.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintWithDefaultValidator.java deleted file mode 100644 index 83b40505cb..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/AcmeConstraintWithDefaultValidator.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.test.constraintvalidator; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * @author Hardy Ferentschik - */ -@Documented -@Constraint(validatedBy = { AcmeConstraintWithDefaultValidator.DefaultAcmeConstraintValidator.class }) -@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) -@Retention(RUNTIME) -public @interface AcmeConstraintWithDefaultValidator { - String message() default "acme"; - - Class[] groups() default { }; - - Class[] payload() default { }; - - - public class DefaultAcmeConstraintValidator - implements ConstraintValidator { - - @Override - public void initialize(AcmeConstraintWithDefaultValidator constraintAnnotation) { - } - - @Override - public boolean isValid(Object value, ConstraintValidatorContext context) { - return false; - } - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionContributorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionContributorTest.java index 8f2f23043d..fc2921829a 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionContributorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionContributorTest.java @@ -8,14 +8,13 @@ import java.util.Set; import javax.validation.ConstraintViolation; -import javax.validation.UnexpectedTypeException; import javax.validation.Validator; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.internal.engine.ServiceLoaderBasedConstraintDefinitionContributor; +import org.hibernate.validator.internal.engine.ServiceLoaderBasedConstraintMappingContributor; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutil.ValidatorUtil; @@ -48,85 +47,19 @@ public void constraint_definitions_can_be_configured_via_service_loader() { assertCorrectConstraintTypes( constraintViolations, MustMatch.class ); } - @Test(expectedExceptions = UnexpectedTypeException.class, expectedExceptionsMessageRegExp = "HV000030.*") - public void validating_constraint_without_registered_constraint_validator_throws_exception() { - validator.validate( new Bar() ); - } - @Test() public void service_based_constraint_definition_contributor_is_configured_per_default() { HibernateValidatorConfiguration hibernateValidatorConfiguration = ValidatorUtil.getConfiguration(); assertNotNull( - hibernateValidatorConfiguration.getDefaultConstraintDefinitionContributor(), + hibernateValidatorConfiguration.getDefaultConstraintMappingContributor(), "There should be a default contributor" ); assertTrue( - hibernateValidatorConfiguration.getDefaultConstraintDefinitionContributor() instanceof ServiceLoaderBasedConstraintDefinitionContributor + hibernateValidatorConfiguration.getDefaultConstraintMappingContributor() instanceof ServiceLoaderBasedConstraintMappingContributor ); } - @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "HV000116.*") - public void null_cannot_be_passed_to_add_constraint_definition_contributor() { - validator = ValidatorUtil.getConfiguration() - .addConstraintDefinitionContributor( null ) - .buildValidatorFactory() - .getValidator(); - - Set> constraintViolations = validator.validate( new Bar() ); - assertNumberOfViolations( constraintViolations, 1 ); - assertCorrectConstraintTypes( constraintViolations, AcmeConstraint.class ); - } - - @Test - public void constraint_definition_contributor_instance_can_be_programmatically_registered() { - validator = ValidatorUtil.getConfiguration() - .addConstraintDefinitionContributor( new AcmeConstraintDefinitionContributor( true ) ) - .buildValidatorFactory() - .getValidator(); - - Set> constraintViolations = validator.validate( new Bar() ); - assertNumberOfViolations( constraintViolations, 1 ); - assertCorrectConstraintTypes( constraintViolations, AcmeConstraint.class ); - } - - @Test - public void constraint_definition_contributor_can_be_configured_via_property() { - validator = ValidatorUtil.getConfiguration() - .addProperty( - HibernateValidatorConfiguration.CONSTRAINT_DEFINITION_CONTRIBUTORS, - AcmeConstraintDefinitionContributor.class.getName() - ) - .buildValidatorFactory() - .getValidator(); - - Set> constraintViolations = validator.validate( new Bar() ); - assertNumberOfViolations( constraintViolations, 1 ); - assertCorrectConstraintTypes( constraintViolations, AcmeConstraint.class ); - } - - @Test - public void constraint_definition_contributor_can_disable_default_constraint_validators() { - validator = ValidatorUtil.getConfiguration() - .addConstraintDefinitionContributor( new AcmeConstraintDefinitionContributor( false ) ) - .buildValidatorFactory() - .getValidator(); - - Set> constraintViolations = validator.validate( new Baz() ); - assertNumberOfViolations( constraintViolations, 1 ); - assertCorrectConstraintTypes( constraintViolations, AcmeConstraintWithDefaultValidator.class ); - } - - @Test(expectedExceptions = UnexpectedTypeException.class, expectedExceptionsMessageRegExp = "HV000150.*") - public void having_a_default_constraint_validator_and_a_contributed_constraint_validator_is_invalid() { - validator = ValidatorUtil.getConfiguration() - .addConstraintDefinitionContributor( new AcmeConstraintDefinitionContributor( true ) ) - .buildValidatorFactory() - .getValidator(); - - validator.validate( new Baz() ); - } - @Test @TestForIssue( jiraKey = "HV-953") public void constraints_defined_via_constraint_definition_contributor_can_have_default_message() { @@ -156,21 +89,6 @@ String getFoo() { return "Foo"; } } - - - public class Bar { - @AcmeConstraint - String getBaz() { - return "Boom"; - } - } - - public class Baz { - @AcmeConstraintWithDefaultValidator - String getBaz() { - return "Boom"; - } - } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java index 90b02b980e..a9f26bb0ba 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java @@ -27,7 +27,6 @@ import org.hibernate.validator.internal.constraintvalidators.hv.URLValidator; import org.hibernate.validator.internal.util.annotationfactory.AnnotationDescriptor; import org.hibernate.validator.internal.util.annotationfactory.AnnotationFactory; -import org.hibernate.validator.spi.constraintdefinition.ConstraintDefinitionContributor; import org.hibernate.validator.testutil.MyCustomStringImpl; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutil.ValidatorUtil; @@ -212,16 +211,14 @@ public void optional_regular_expression_can_be_refined_with_flags_using_programm public void url_validator_using_regexp_only_can_be_configured_via_constraint_definition_contributor() { HibernateValidatorConfiguration config = ValidatorUtil.getConfiguration( HibernateValidator.class ); - config.addConstraintDefinitionContributor( - new ConstraintDefinitionContributor() { - @Override - public void collectConstraintDefinitions(ConstraintDefinitionBuilder builder) { - builder.constraint( URL.class ) - .includeExistingValidators( false ) - .validatedBy( RegexpURLValidator.class ); - } - } - ); + ConstraintMapping constraintMapping = config.createConstraintMapping(); + + constraintMapping + .constraint( URL.class ) + .includeExistingValidators( false ) + .validatedBy( RegexpURLValidator.class ); + + config.addMapping( constraintMapping ); DelegatingConstraintValidatorFactory constraintValidatorFactory = new DelegatingConstraintValidatorFactory( config.getDefaultConstraintValidatorFactory() From 716dd7992201cf8347886d48137e0756223aa487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Sat, 14 May 2016 15:44:24 +0200 Subject: [PATCH 053/189] HV-501 Renamed .constraint() to .constraintDefinition() --- .../constraintapi/ConstraintApiTest.java | 4 +-- .../validator/cfg/ConstraintMapping.java | 2 +- ...t.java => ConstraintDefinitionTarget.java} | 6 ++-- .../cfg/context/ConstraintMappingTarget.java | 2 +- .../hibernate/validator/constraints/URL.java | 4 +-- .../context/ConstraintContextImplBase.java | 4 +-- .../cfg/context/DefaultConstraintMapping.java | 2 +- ...aderBasedConstraintMappingContributor.java | 2 +- .../metadata/core/ConstraintHelper.java | 10 +++++-- ...est.java => ConstraintDefinitionTest.java} | 28 +++++++++---------- .../hv/URLValidatorTest.java | 2 +- 11 files changed, 36 insertions(+), 30 deletions(-) rename engine/src/main/java/org/hibernate/validator/cfg/context/{ConstraintTarget.java => ConstraintDefinitionTarget.java} (81%) rename engine/src/test/java/org/hibernate/validator/test/cfg/{ValidatorConstraintMappingTest.java => ConstraintDefinitionTest.java} (92%) diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java index f6664ac18d..71cd9942a2 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter11/constraintapi/ConstraintApiTest.java @@ -148,7 +148,7 @@ public void constraintDefinition() { ConstraintMapping constraintMapping = configuration.createConstraintMapping(); constraintMapping - .constraint( ValidPassengerCount.class ) + .constraintDefinition( ValidPassengerCount.class ) .validatedBy( ValidPassengerCountValidator.class ); //end::constraintDefinition[] @@ -165,7 +165,7 @@ public void urlValidationOverride() { ConstraintMapping constraintMapping = configuration.createConstraintMapping(); constraintMapping - .constraint( URL.class ) + .constraintDefinition( URL.class ) .includeExistingValidators( false ) .validatedBy( RegexpURLValidator.class ); //end::urlValidationOverride[] diff --git a/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java index 2c8e774dbb..e8ef9b5a49 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/ConstraintMapping.java @@ -45,5 +45,5 @@ public interface ConstraintMapping { * * @return Instance allowing for defining validators to be executed for the specified constraint. */ - ConstraintDefinitionContext constraint(Class annotationClass); + ConstraintDefinitionContext constraintDefinition(Class annotationClass); } diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionTarget.java similarity index 81% rename from engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintTarget.java rename to engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionTarget.java index 45dd08332f..646cd398ac 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintDefinitionTarget.java @@ -9,12 +9,12 @@ import java.lang.annotation.Annotation; /** - * Facet of a constraint mapping creational context which allows to the select the constraint (annotation type) to + * Facet of a constraint definition creational context which allows to the select the constraint (annotation type) to * which the next operations shall apply. * * @author Yoann Rodiere */ -public interface ConstraintTarget { +public interface ConstraintDefinitionTarget { /** * Selects the constraint (i.e. annotation type) to which the next operations shall apply. A given constraint @@ -26,5 +26,5 @@ public interface ConstraintTarget { * * @return A creational context representing the selected constraint. */ - ConstraintDefinitionContext constraint(Class annotationType); + ConstraintDefinitionContext constraintDefinition(Class annotationType); } diff --git a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java index 2d65a98c15..1bc065a5a0 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/context/ConstraintMappingTarget.java @@ -11,6 +11,6 @@ * * @author Yoann Rodiere */ -public interface ConstraintMappingTarget extends TypeTarget, ConstraintTarget { +public interface ConstraintMappingTarget extends TypeTarget, ConstraintDefinitionTarget { } diff --git a/engine/src/main/java/org/hibernate/validator/constraints/URL.java b/engine/src/main/java/org/hibernate/validator/constraints/URL.java index 625412e5eb..0bea278b82 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/URL.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/URL.java @@ -47,7 +47,7 @@ * * ConstraintMapping constraintMapping = config.createConstraintMapping(); * constraintMapping - * .constraint( URL.class ) + * .constraintDefinition( URL.class ) * .includeExistingValidators( false ) * .validatedBy( RegexpURLValidator.class ); * @@ -72,7 +72,7 @@ *
* * @see RFC2396 - * @see org.hibernate.validator.cfg.ConstraintMapping#constraint(Class) + * @see org.hibernate.validator.cfg.ConstraintMapping#constraintDefinition(Class) * @author Hardy Ferentschik */ @Documented diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java index 70ba90ebd8..f5cf0c7941 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ConstraintContextImplBase.java @@ -29,8 +29,8 @@ public TypeConstraintMappingContext type(Class type) { return mapping.type( type ); } - public ConstraintDefinitionContext constraint(Class annotationClass) { - return mapping.constraint( annotationClass ); + public ConstraintDefinitionContext constraintDefinition(Class annotationClass) { + return mapping.constraintDefinition( annotationClass ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java index 15408b2a58..560579c405 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java @@ -93,7 +93,7 @@ public Set> getBeanConfigurations(ConstraintHelper constrai } @Override - public ConstraintDefinitionContext constraint(Class annotationClass) { + public ConstraintDefinitionContext constraintDefinition(Class annotationClass) { Contracts.assertNotNull( annotationClass, MESSAGES.annotationTypeMustNotBeNull() ); Contracts.assertTrue( annotationClass.isAnnotationPresent( Constraint.class ), MESSAGES.annotationTypeMustBeAnnotatedWithConstraint() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java index b56fd410da..046fc3130f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ServiceLoaderBasedConstraintMappingContributor.java @@ -69,7 +69,7 @@ private void registerConstraintDefinition(ConstraintMappi Class constraintType, List> validatorTypes) { ConstraintDefinitionContext context = constraintMapping - .constraint( (Class) constraintType ) + .constraintDefinition( (Class) constraintType ) .includeExistingValidators( true ); for ( Class validatorType : validatorTypes ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index a0b30e298d..ad0b702694 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -139,7 +139,13 @@ public class ConstraintHelper { private final Map, List>> builtinConstraints; private final ValidatorClassMap validatorClasses = new ValidatorClassMap(); - private ConcurrentMap, Object> overridenAnnotations = newConcurrentHashMap(); + + /** + * Records whether or not the default constraint definition for an an annotation has been overridden by a user. + * This allows to detect multiple overrides in different constraint mappings (which is forbidden), so that we can + * throw an exception when that happens. + */ + private ConcurrentMap, Object> overriddenAnnotations = newConcurrentHashMap(); public ConstraintHelper() { Map, List>> tmpConstraints = newHashMap(); @@ -332,7 +338,7 @@ public void putValidatorClasses(Class annotationType, } } - Object previousOverride = overridenAnnotations.putIfAbsent( annotationType, annotationType ); + Object previousOverride = overriddenAnnotations.putIfAbsent( annotationType, annotationType ); if ( previousOverride != null ) { throw log.getOverridingConstraintDefinitionsMultipleTimesException( annotationType.getName() ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ValidatorConstraintMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintDefinitionTest.java similarity index 92% rename from engine/src/test/java/org/hibernate/validator/test/cfg/ValidatorConstraintMappingTest.java rename to engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintDefinitionTest.java index 0fa1d53f54..c3e093386c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ValidatorConstraintMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintDefinitionTest.java @@ -40,12 +40,12 @@ import org.testng.annotations.Test; /** - * Unit test for {@link ConstraintMapping#constraint(Class)} et al. + * Unit test for {@link ConstraintMapping#constraintDefinition(Class)} et al. * * @author Yoann Rodiere */ @TestForIssue( jiraKey = "HV-501") -public class ValidatorConstraintMappingTest { +public class ConstraintDefinitionTest { private HibernateValidatorConfiguration config; private DefaultConstraintMapping mapping; @@ -61,7 +61,7 @@ public void setUp() { expectedExceptionsMessageRegExp = "HV[0-9]*: The annotation type must not be null when creating a constraint definition." ) public void testNullClass() { - mapping.constraint( null ); + mapping.constraintDefinition( null ); } @Test( @@ -69,12 +69,12 @@ public void testNullClass() { expectedExceptionsMessageRegExp = "HV[0-9]*: The annotation type must be annotated with @javax.validation.Constraint when creating a constraint definition." ) public void testNonConstraintAnnotation() { - mapping.constraint( NonConstraintAnnotation.class ); + mapping.constraintDefinition( NonConstraintAnnotation.class ); } @Test public void testConstraintMapping() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .validatedBy( NonDefaultLongValidator.class ); config.addMapping( mapping ); @@ -87,7 +87,7 @@ public void testConstraintMapping() { @Test public void testConstraintMappingDefaultsToIncludingExistingValidators() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .validatedBy( NonDefaultLongValidator.class ); config.addMapping( mapping ); @@ -100,7 +100,7 @@ public void testConstraintMappingDefaultsToIncludingExistingValidators() { @Test public void testConstraintMappingIncludingExistingValidators() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .includeExistingValidators( true ) .validatedBy( NonDefaultLongValidator.class ); @@ -114,7 +114,7 @@ public void testConstraintMappingIncludingExistingValidators() { @Test public void testConstraintMappingExcludingExistingValidators() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .includeExistingValidators( false ) .validatedBy( NonDefaultIntegerValidator.class ); @@ -128,7 +128,7 @@ public void testConstraintMappingExcludingExistingValidators() { @Test public void testConstraintMappingIncludingExistingValidatorsThenExcludingThem() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .includeExistingValidators( true ) .validatedBy( NonDefaultLongValidator.class ) .includeExistingValidators( false ) @@ -152,9 +152,9 @@ public void testConstraintMappingIncludingExistingValidatorsThenExcludingThem() + " .*\\$ConstraintAnnotation is configured more than once via the programmatic constraint definition API." ) public void testMultipleDefinitionForSameConstraintOnSameConstraintMapping() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .validatedBy( NonDefaultLongValidator.class ) - .constraint( ConstraintAnnotation.class ) + .constraintDefinition( ConstraintAnnotation.class ) .includeExistingValidators( false ) .validatedBy( NonDefaultIntegerValidator.class ); } @@ -164,11 +164,11 @@ public void testMultipleDefinitionForSameConstraintOnSameConstraintMapping() { expectedExceptionsMessageRegExp = "HV000167:.*" ) public void testMultipleDefinitionForSameConstraintOnDifferentConstraintMappings() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .validatedBy( NonDefaultLongValidator.class ); ConstraintMapping otherMapping = config.createConstraintMapping(); - otherMapping.constraint( ConstraintAnnotation.class ) + otherMapping.constraintDefinition( ConstraintAnnotation.class ) .includeExistingValidators( false ) .validatedBy( NonDefaultIntegerValidator.class ); @@ -182,7 +182,7 @@ public void testMultipleDefinitionForSameConstraintOnDifferentConstraintMappings expectedExceptionsMessageRegExp = "HV000150:.*" ) public void testMultipleValidatorsForSameType() { - mapping.constraint( ConstraintAnnotation.class ) + mapping.constraintDefinition( ConstraintAnnotation.class ) .includeExistingValidators( true ) .validatedBy( NonDefaultIntegerValidator.class ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java index a9f26bb0ba..63983a949f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java @@ -214,7 +214,7 @@ public void url_validator_using_regexp_only_can_be_configured_via_constraint_def ConstraintMapping constraintMapping = config.createConstraintMapping(); constraintMapping - .constraint( URL.class ) + .constraintDefinition( URL.class ) .includeExistingValidators( false ) .validatedBy( RegexpURLValidator.class ); From ebb81f8a326ef079b303f8cc8b5c3f39ed0fe44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Sat, 14 May 2016 17:44:11 +0200 Subject: [PATCH 054/189] HV-501 Introduced XML constraint def override in prog. API Now, the XML constraint definitions may be overridden by using the programmatic API. This implies that: * duplicate constraint definition detection is now done separately by XMLParser and ConstraintMapping/ValidatorFactoryImpl * some fields and functions were renamed from "keepDefaults" (previous meaning) to "keepExisting" (new, broader meaning) --- .../cfg/context/DefaultConstraintMapping.java | 11 +-- .../internal/engine/ConfigurationImpl.java | 3 +- .../internal/engine/ValidatorFactoryImpl.java | 45 +++++++---- .../ConstraintDefinitionContribution.java | 16 ++-- .../metadata/core/ConstraintHelper.java | 35 +++----- .../validator/internal/util/logging/Log.java | 4 +- .../internal/xml/XmlMappingParser.java | 13 ++- .../test/cfg/ConstraintDefinitionTest.java | 81 ++++++++++++++++++- .../cfg/ConstraintDefinitionTest_mapping.xml | 12 +++ 9 files changed, 160 insertions(+), 60 deletions(-) create mode 100644 engine/src/test/resources/org/hibernate/validator/test/cfg/ConstraintDefinitionTest_mapping.xml diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java index 560579c405..21118c91c3 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/DefaultConstraintMapping.java @@ -40,14 +40,14 @@ public class DefaultConstraintMapping implements ConstraintMapping { private final AnnotationProcessingOptionsImpl annotationProcessingOptions; private final Set> configuredTypes; private final Set> typeContexts; - private final Set> configuredConstraints; + private final Set> definedConstraints; private final Set> constraintContexts; public DefaultConstraintMapping() { this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); this.configuredTypes = newHashSet(); this.typeContexts = newHashSet(); - this.configuredConstraints = newHashSet(); + this.definedConstraints = newHashSet(); this.constraintContexts = newHashSet(); } @@ -98,17 +98,18 @@ public ConstraintDefinitionContext constraintDefinitio Contracts.assertTrue( annotationClass.isAnnotationPresent( Constraint.class ), MESSAGES.annotationTypeMustBeAnnotatedWithConstraint() ); - if ( configuredConstraints.contains( annotationClass ) ) { + if ( definedConstraints.contains( annotationClass ) ) { + // Fail fast for easy-to-detect definition conflicts; other conflicts are handled in ValidatorFactoryImpl throw log.getConstraintHasAlreadyBeenConfiguredViaProgrammaticApiException( annotationClass.getName() ); } ConstraintDefinitionContextImpl constraintContext = new ConstraintDefinitionContextImpl( this, annotationClass ); constraintContexts.add( constraintContext ); - configuredConstraints.add( annotationClass ); + definedConstraints.add( annotationClass ); return constraintContext; } - + public Set> getConstraintDefinitionContributions() { Set> contributions = newHashSet(); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java index 379cc39858..d7f4540f30 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ConfigurationImpl.java @@ -247,7 +247,7 @@ public MethodValidationConfiguration getMethodValidationConfiguration() { } @Override - public final ConstraintMapping createConstraintMapping() { + public final DefaultConstraintMapping createConstraintMapping() { return new DefaultConstraintMapping(); } @@ -427,6 +427,7 @@ public ParameterNameProvider getDefaultParameterNameProvider() { public final Set getProgrammaticMappings() { return programmaticMappings; } + private boolean isSpecificProvider() { return validationBootstrapParameters.getProvider() != null; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 000267f69f..d1e8637173 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -247,21 +247,22 @@ private static ClassLoader getExternalClassLoader(ConfigurationState configurati private static Set getConstraintMappings(ConfigurationState configurationState, ClassLoader externalClassLoader) { Set constraintMappings = newHashSet(); - if ( configurationState instanceof ConfigurationImpl ) { ConfigurationImpl hibernateConfiguration = (ConfigurationImpl) configurationState; - - // default config - ConstraintMappingContributor defaultContributor = hibernateConfiguration.getDefaultConstraintMappingContributor(); - DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder(); - defaultContributor.createConstraintMappings( builder ); - constraintMappings.addAll( builder.mappings ); - + // programmatic config + /* We add these first so that constraint mapping created through DefaultConstraintMappingBuilder will take + * these programmatically defined mappings into account when checking for constraint definition uniqueness + */ constraintMappings.addAll( hibernateConfiguration.getProgrammaticMappings() ); + + // service loader based config + ConstraintMappingContributor defaultContributor = hibernateConfiguration.getDefaultConstraintMappingContributor(); + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings ); + defaultContributor.createConstraintMappings( builder ); } - // XML + // XML-defined constraint mapping contributor String constraintMappingContributorClassName = configurationState.getProperties() .get( HibernateValidatorConfiguration.CONSTRAINT_MAPPING_CONTRIBUTOR ); @@ -278,10 +279,8 @@ private static Set getConstraintMappings(Configuration "constraint mapping contributor class" ) ); - DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder(); + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( constraintMappings ); contributor.createConstraintMappings( builder ); - - constraintMappings.addAll( builder.mappings ); } return constraintMappings; @@ -486,18 +485,26 @@ private static List> getPropertyConfiguredValidatedVa private static void registerCustomConstraintValidators(Set constraintMappings, ConstraintHelper constraintHelper) { + Set> definedConstraints = newHashSet(); for ( DefaultConstraintMapping constraintMapping : constraintMappings ) { for (ConstraintDefinitionContribution contribution : constraintMapping.getConstraintDefinitionContributions() ) { - processConstraintDefinitionContribution( contribution, constraintHelper ); + processConstraintDefinitionContribution( contribution, constraintHelper, definedConstraints ); } } } - private static void processConstraintDefinitionContribution(ConstraintDefinitionContribution constraintDefinitionContribution, ConstraintHelper constraintHelper) { + private static void processConstraintDefinitionContribution( + ConstraintDefinitionContribution constraintDefinitionContribution, ConstraintHelper constraintHelper, + Set> definedConstraints) { + Class constraintType = constraintDefinitionContribution.getConstraintType(); + if ( definedConstraints.contains( constraintType ) ) { + throw log.getConstraintHasAlreadyBeenConfiguredViaProgrammaticApiException( constraintType.getName() ); + } + definedConstraints.add( constraintType ); constraintHelper.putValidatorClasses( - constraintDefinitionContribution.getConstraintType(), + constraintType, constraintDefinitionContribution.getConstraintValidators(), - constraintDefinitionContribution.keepDefaults() + constraintDefinitionContribution.includeExisting() ); } @@ -516,8 +523,12 @@ private static T run(PrivilegedAction action) { */ private static class DefaultConstraintMappingBuilder implements ConstraintMappingContributor.ConstraintMappingBuilder { + private final Set mappings; - private final Set mappings = newHashSet(); + public DefaultConstraintMappingBuilder(Set mappings) { + super(); + this.mappings = mappings; + } @Override public ConstraintMapping addConstraintMapping() { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionContribution.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionContribution.java index 4fe891a206..39651c152f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionContribution.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintdefinition/ConstraintDefinitionContribution.java @@ -19,14 +19,14 @@ public class ConstraintDefinitionContribution { private final Class constraintType; private final List>> constraintValidators = new ArrayList>>(); - private final boolean keepDefaults; + private final boolean includeExisting; public ConstraintDefinitionContribution(Class constraintType, List>> constraintValidators, - boolean keepDefaults) { + boolean includeExisting) { this.constraintType = constraintType; this.constraintValidators.addAll( constraintValidators ); - this.keepDefaults = keepDefaults; + this.includeExisting = includeExisting; } /** @@ -48,13 +48,13 @@ public Class getConstraintType() { } /** - * Whether or not the default constraint validators should be kept or not. + * Whether or not the existing constraint validators should be kept or not. * - * @return {@code true} if the default constraint validator instances for the constraint type wrapped by this + * @return {@code true} if the existing constraint validators for the constraint type wrapped by this * instance should be kept, {@code false} otherwise. */ - public boolean keepDefaults() { - return keepDefaults; + public boolean includeExisting() { + return includeExisting; } @Override @@ -90,7 +90,7 @@ public String toString() { return "ConstraintDefinitionContribution{" + "constraintType=" + constraintType + ", constraintValidators=" + constraintValidators + - ", keepDefaults=" + keepDefaults + + ", includeExisting=" + includeExisting + '}'; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index ad0b702694..a134b31376 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -139,13 +139,6 @@ public class ConstraintHelper { private final Map, List>> builtinConstraints; private final ValidatorClassMap validatorClasses = new ValidatorClassMap(); - - /** - * Records whether or not the default constraint definition for an an annotation has been overridden by a user. - * This allows to detect multiple overrides in different constraint mappings (which is forbidden), so that we can - * throw an exception when that happens. - */ - private ConcurrentMap, Object> overriddenAnnotations = newConcurrentHashMap(); public ConstraintHelper() { Map, List>> tmpConstraints = newHashMap(); @@ -249,8 +242,10 @@ private boolean isBuiltinConstraint(Class annotationType) * *
    *
  • {@link Constraint#validatedBy()}, - *
  • internally registered validators for built-in constraints and
  • - *
  • XML configuration.
  • + *
  • internally registered validators for built-in constraints
  • + *
  • XML configuration and
  • + *
  • programmatically registered validators (see + * {@link org.hibernate.validator.cfg.ConstraintMapping#constraintDefinition(Class)}).
  • *
* * The result is cached internally. @@ -323,28 +318,20 @@ private boolean supportsValidationTarget(Class the type of the annotation */ public
void putValidatorClasses(Class annotationType, List>> definitionClasses, - boolean keepDefaultClasses) { - if ( keepDefaultClasses ) { - List>> defaultValidators = getDefaultValidatorClasses( - annotationType - ); - for ( Class> defaultValidator : defaultValidators ) { - definitionClasses.add( 0, defaultValidator ); + boolean keepExistingClasses) { + if ( keepExistingClasses ) { + List>> existingClasses = getAllValidatorClasses( annotationType ); + if ( existingClasses != null ) { + definitionClasses.addAll( 0, existingClasses ); } } - Object previousOverride = overriddenAnnotations.putIfAbsent( annotationType, annotationType ); - if ( previousOverride != null ) { - throw log.getOverridingConstraintDefinitionsMultipleTimesException( annotationType.getName() ); - } - else { - validatorClasses.put( annotationType, definitionClasses ); - } + validatorClasses.put( annotationType, definitionClasses ); } /** diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index 28a3102f8a..880e570810 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -546,8 +546,8 @@ UnexpectedTypeException getNoValidatorFoundForTypeException(String constraintTyp ValidationException getValidateOnExecutionOnOverriddenOrInterfaceMethodException(Method m); @Message(id = 167, - value = "A given constraint definition can only be overridden once (either through a XML file or the programmatic API). %1$s is overridden in multiple XML files and/or API calls") - ValidationException getOverridingConstraintDefinitionsMultipleTimesException(String constraintClass); + value = "A given constraint definition can only be overridden in one mapping file. %1$s is overridden in multiple files") + ValidationException getOverridingConstraintDefinitionsInMultipleMappingFilesException(String constraintClass); @Message(id = 168, value = "The message descriptor '%1$s' contains an unbalanced meta character '%2$c' parameter.") diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java index bc638531bb..072a2cdaf3 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlMappingParser.java @@ -128,6 +128,7 @@ public final void parse(Set mappingStreams) { annotationProcessingOptions ); + Set alreadyProcessedConstraintDefinitions = newHashSet(); for ( InputStream in : mappingStreams ) { // check whether mark is supported, if so we can reset the stream in order to allow reuse of Configuration @@ -149,7 +150,8 @@ public final void parse(Set mappingStreams) { parseConstraintDefinitions( mapping.getConstraintDefinition(), - defaultPackage + defaultPackage, + alreadyProcessedConstraintDefinitions ); for ( BeanType bean : mapping.getBean() ) { @@ -252,9 +254,16 @@ private void processBeanType(ConstrainedTypeBuilder constrainedTypeBuilder, Cons @SuppressWarnings("unchecked") private void parseConstraintDefinitions(List constraintDefinitionList, - String defaultPackage) { + String defaultPackage, + Set alreadyProcessedConstraintDefinitions) { for ( ConstraintDefinitionType constraintDefinition : constraintDefinitionList ) { String annotationClassName = constraintDefinition.getAnnotation(); + if ( alreadyProcessedConstraintDefinitions.contains( annotationClassName ) ) { + throw log.getOverridingConstraintDefinitionsInMultipleMappingFilesException( annotationClassName ); + } + else { + alreadyProcessedConstraintDefinitions.add( annotationClassName ); + } Class clazz = classLoadingHelper.loadClass( annotationClassName, defaultPackage ); if ( !clazz.isAnnotation() ) { diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintDefinitionTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintDefinitionTest.java index c3e093386c..41a35d4d60 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintDefinitionTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintDefinitionTest.java @@ -14,6 +14,7 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations; +import java.io.InputStream; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -161,7 +162,8 @@ public void testMultipleDefinitionForSameConstraintOnSameConstraintMapping() { @Test( expectedExceptions = ValidationException.class, - expectedExceptionsMessageRegExp = "HV000167:.*" + expectedExceptionsMessageRegExp = "HV[0-9]*:" + + " .*\\$ConstraintAnnotation is configured more than once via the programmatic constraint definition API." ) public void testMultipleDefinitionForSameConstraintOnDifferentConstraintMappings() { mapping.constraintDefinition( ConstraintAnnotation.class ) @@ -177,6 +179,63 @@ public void testMultipleDefinitionForSameConstraintOnDifferentConstraintMappings config.buildValidatorFactory().getValidator(); } + @Test + public void testXmlConstraintDefinitionMergedWithProgrammaticConfiguration() { + final InputStream xmlMapping = ConstraintDefinitionTest.class.getResourceAsStream( + "ConstraintDefinitionTest_mapping.xml" + ); + + config.addMapping( xmlMapping ); // Adds NonDefaultLongValidator and keeps default validators + + mapping.constraintDefinition( ConstraintAnnotation.class ) + .validatedBy( NonDefaultShortValidator.class ); // Adds this on top of XML configuration + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + // Defaults are untouched + Set> violations = validator.validate( new ConstrainedStringFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, DefaultStringValidator.class ); + + // XML configuration is taken into account + violations = validator.validate( new ConstrainedLongFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, NonDefaultLongValidator.class ); + + // Programmatic configuration is also taken into account + violations = validator.validate( new ConstrainedShortFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, NonDefaultShortValidator.class ); + } + + @Test + public void testXmlConstraintDefinitionOverriddenByProgrammaticConfiguration() { + final InputStream xmlMapping = ConstraintDefinitionTest.class.getResourceAsStream( + "ConstraintDefinitionTest_mapping.xml" + ); + + config.addMapping( xmlMapping ); // Adds NonDefaultLongValidator and keeps default validators + + mapping.constraintDefinition( ConstraintAnnotation.class ) // Overrides XML configuration (and defaults) + .includeExistingValidators( false ) + .validatedBy( NonDefaultIntegerValidator.class ) + .validatedBy( OtherNonDefaultLongValidator.class ); + + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + // Defaults are overridden + Set> violations = validator.validate( new ConstrainedIntegerFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, NonDefaultIntegerValidator.class ); + + // XML configuration is overridden by programmatic configuration + violations = validator.validate( new ConstrainedLongFieldBean() ); + assertNumberOfViolations( violations, 1 ); + assertCorrectValidatorTypes( violations, OtherNonDefaultLongValidator.class ); + } + @Test( expectedExceptions = UnexpectedTypeException.class, expectedExceptionsMessageRegExp = "HV000150:.*" @@ -287,6 +346,20 @@ public static class NonDefaultLongValidator extends StubValidator { */ } + public static class OtherNonDefaultLongValidator extends StubValidator { + /* + * Nothing special here: everything is in the parent class, which uses getClass() to enable derived + * class-specific behavior. + */ + } + + public static class NonDefaultShortValidator extends StubValidator { + /* + * Nothing special here: everything is in the parent class, which uses getClass() to enable derived + * class-specific behavior. + */ + } + private static class ConstrainedStringFieldBean { @ConstraintAnnotation @@ -305,4 +378,10 @@ private static class ConstrainedLongFieldBean { private Long field; } + private static class ConstrainedShortFieldBean { + + @ConstraintAnnotation + private Short field; + } + } diff --git a/engine/src/test/resources/org/hibernate/validator/test/cfg/ConstraintDefinitionTest_mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/cfg/ConstraintDefinitionTest_mapping.xml new file mode 100644 index 0000000000..b8732f73a6 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/cfg/ConstraintDefinitionTest_mapping.xml @@ -0,0 +1,12 @@ + + + + + org.hibernate.validator.test.cfg.ConstraintDefinitionTest$NonDefaultLongValidator + + + From 47cf38d9b12818b0375a5646a73d213e9433556d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Sat, 14 May 2016 19:07:12 +0200 Subject: [PATCH 055/189] HV-501 Removed HibernateConfiguration.getDefaultConstraintMappingContributor from the public API since it serves no purposes to users. --- .../HibernateValidatorConfiguration.java | 8 ----- .../internal/engine/ConfigurationImpl.java | 9 +++--- .../internal/engine/ValidatorFactoryImpl.java | 5 +-- .../ConstraintDefinitionContributorTest.java | 32 ++++--------------- 4 files changed, 14 insertions(+), 40 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index 984fa2d2c7..cee35114ed 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -9,7 +9,6 @@ import javax.validation.Configuration; import org.hibernate.validator.cfg.ConstraintMapping; -import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.time.TimeProvider; import org.hibernate.validator.spi.valuehandling.ValidatedValueUnwrapper; @@ -151,13 +150,6 @@ public interface HibernateValidatorConfiguration extends Configuration handler); - /** - * @return the default {@link org.hibernate.validator.spi.cfg.ConstraintMappingContributor}. Never {@code null}. - * - * @since 5.3 - */ - ConstraintMappingContributor getDefaultConstraintMappingContributor(); - /** * Sets the class loader to be used for loading user-provided resources: *