diff --git a/.travis.yml b/.travis.yml index 1c7f86505d..eb421ac9dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,6 @@ addons: cache: directories: - $HOME/.m2 -before_install: - - BUILD_OPTIONS='-s settings-example.xml' install: # The Maven install provided by Travis is outdated, use Maven wrapper to get the latest version - mvn -N io.takari:maven:wrapper diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..2407bef4ca --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,469 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ + +import groovy.transform.Field + +/* + * See https://github.com/hibernate/hibernate-jenkins-pipeline-helpers + */ +@Library('hibernate-jenkins-pipeline-helpers@1.3') +import org.hibernate.jenkins.pipeline.helpers.job.JobHelper +import org.hibernate.jenkins.pipeline.helpers.alternative.AlternativeMultiMap + +/* + * WARNING: DO NOT IMPORT LOCAL LIBRARIES HERE. + * + * By local, I mean libraries whose files are in the same Git repository. + * + * The Jenkinsfile is protected and will not be executed if modified in pull requests from external users, + * but other local library files are not protected. + * A user could potentially craft a malicious PR by modifying a local library. + * + * See https://blog.grdryn.me/blog/jenkins-pipeline-trust.html for a full explanation, + * and a potential solution if we really need local libraries. + * Alternatively we might be able to host libraries in a separate GitHub repo and configure + * them in the GUI: see https://ci.hibernate.org/job/hibernate-validator/configure, "Pipeline Libraries". + */ + +/* + * See https://github.com/hibernate/hibernate-jenkins-pipeline-helpers for the documentation + * of the helpers library used in this Jenkinsfile, + * and for help writing Jenkinsfiles. + * + * ### Jenkins configuration + * + * #### Jenkins plugins + * + * This file requires the following plugins in particular: + * + * - everything required by the helpers library (see the org.hibernate.(...) imports for a link to its documentation) + * - https://plugins.jenkins.io/pipeline-github for the trigger on pull request comments + * + * #### Script approval + * + * If not already done, you will need to allow the following calls in /scriptApproval/: + * + * - everything required by the helpers library (see the org.hibernate.(...) imports for a link to its documentation) + * + * ### Integrations + * + * #### Nexus deployment + * + * This job is only able to deploy snapshot artifacts, + * for every non-PR build on "primary" branches (main and maintenance branches), + * but the name of a Maven settings file must be provided in the job configuration file + * (see below). + * + * For actual releases, see jenkins/release.groovy. + * + * ### Job configuration + * + * This Jenkinsfile gets its configuration from four sources: + * branch name, environment variables, a configuration file, and credentials. + * All configuration is optional for the default build (and it should stay that way), + * but some features require some configuration. + * + * #### Branch name + * + * See the org.hibernate.(...) imports for a link to the helpers library documentation, + * which explains the basics. + * + * #### Environment variables + * + * No particular environment variables is necessary. + * + * #### Job configuration file + * + * See the org.hibernate.(...) imports for a link to the helpers library documentation, + * which explains the basic structure of this file and how to set it up. + * + * Below is the additional structure specific to this Jenkinsfile: + * + * deployment: + * maven: + * # String containing the ID of a Maven settings file registered using the config-file-provider Jenkins plugin. + * # The settings must provide credentials to the server with ID 'ossrh'. + * settingsId: ... + */ + +@Field final String MAVEN_TOOL = 'Apache Maven 3.6' + +// Default node pattern, to be used for resource-intensive stages. +// Should not include the controller node. +@Field final String NODE_PATTERN_BASE = 'Worker&&Containers' +// Quick-use node pattern, to be used for very light, quick, and environment-independent stages, +// such as sending a notification. May include the controller node in particular. +@Field final String QUICK_USE_NODE_PATTERN = 'Controller||Worker' + +@Field AlternativeMultiMap environments +@Field JobHelper helper + +@Field boolean enableDefaultBuild = false +@Field boolean enableDefaultBuildIT = false +@Field boolean deploySnapshot = false + +this.helper = new JobHelper(this) + +helper.runWithNotification { + +stage('Configure') { + this.environments = AlternativeMultiMap.create([ + jdk: [ + // This should not include every JDK; in particular let's not care too much about EOL'd JDKs like version 9 + // See http://www.oracle.com/technetwork/java/javase/eol-135779.html + new JdkBuildEnvironment(version: '8', buildJdkTool: 'OracleJDK8 Latest', + condition: TestCondition.BEFORE_MERGE, + isDefault: true), + new JdkBuildEnvironment(version: '11', buildJdkTool: 'OpenJDK 11 Latest', + condition: TestCondition.AFTER_MERGE), + new JdkBuildEnvironment(version: '13', buildJdkTool: 'OpenJDK 13 Latest', + condition: TestCondition.AFTER_MERGE), + new JdkBuildEnvironment(version: '17', buildJdkTool: 'OpenJDK 17 Latest', + condition: TestCondition.AFTER_MERGE) + ], + wildflyTck: [ + new WildFlyTckBuildEnvironment(javaVersion: '8', buildJdkTool: 'OracleJDK8 Latest', + condition: TestCondition.AFTER_MERGE), + new WildFlyTckBuildEnvironment(javaVersion: '11', buildJdkTool: 'OpenJDK 11 Latest', + condition: TestCondition.AFTER_MERGE) + ] + ]) + + helper.configure { + configurationNodePattern QUICK_USE_NODE_PATTERN + file 'job-configuration.yaml' + jdk { + defaultTool environments.content.jdk.default.buildJdkTool + } + maven { + defaultTool MAVEN_TOOL + producedArtifactPattern "org/hibernate/validator/*" + // Relocation artifacts + producedArtifactPattern "org/hibernate/hibernate-validator*" + } + } + + properties([ + buildDiscarder( + logRotator(daysToKeepStr: '30', numToKeepStr: '10') + ), + disableConcurrentBuilds(abortPrevious: true), + pipelineTriggers( + // HSEARCH-3417: do not add snapshotDependencies() here, this was known to cause problems. + [ + issueCommentTrigger('.*test this please.*') + ] + + helper.generateUpstreamTriggers() + ), + helper.generateNotificationProperty(), + parameters([ + choice( + name: 'ENVIRONMENT_SET', + choices: """AUTOMATIC +DEFAULT +SUPPORTED +ALL""", + description: """A set of environments that must be checked. +'AUTOMATIC' picks a different set of environments based on the branch name. +'DEFAULT' means a single build with the default environment expected by the Maven configuration, +while other options will trigger multiple Maven executions in different environments.""" + ), + string( + name: 'ENVIRONMENT_FILTER', + defaultValue: '', + trim: true, + description: """A regex filter to apply to the environments that must be checked. +If this parameter is non-empty, ENVIRONMENT_SET will be ignored and environments whose tag matches the given regex will be checked. +Some useful filters: 'default', 'jdk', 'jdk-10', 'eclipse'. +""" + ) + ]) + ]) + + if (helper.scmSource.branch.primary && !helper.scmSource.pullRequest) { + if (helper.configuration.file?.deployment?.maven?.settingsId) { + deploySnapshot = true + } + else { + echo "Missing deployment configuration in job configuration file - snapshot deployment will be skipped." + } + } + + if (params.ENVIRONMENT_FILTER) { + keepOnlyEnvironmentsMatchingFilter(params.ENVIRONMENT_FILTER) + } + else { + keepOnlyEnvironmentsFromSet(params.ENVIRONMENT_SET) + } + + // Determine whether ITs need to be run in the default build + enableDefaultBuildIT = environments.content.any { key, envSet -> + return envSet.enabled.contains(envSet.default) + } + // No need to re-test default environments separately, they will be tested as part of the default build if needed + environments.content.each { key, envSet -> + envSet.enabled.remove(envSet.default) + } + + if ( enableDefaultBuildIT && params.LEGACY_IT ) { + echo "Enabling legacy integration tests in default environment due to explicit request" + enableDefaultBuildLegacyIT = true + } + + enableDefaultBuild = + enableDefaultBuildIT || + environments.content.any { key, envSet -> envSet.enabled.any { buildEnv -> buildEnv.requiresDefaultBuildArtifacts() } } || + deploySnapshot + + echo """Branch: ${helper.scmSource.branch.name} +PR: ${helper.scmSource.pullRequest?.id} +params.ENVIRONMENT_SET: ${params.ENVIRONMENT_SET} +params.ENVIRONMENT_FILTER: ${params.ENVIRONMENT_FILTER} + +Resulting execution plan: + enableDefaultBuild=$enableDefaultBuild + enableDefaultBuildIT=$enableDefaultBuildIT + environments=${environments.enabledAsString} + deploySnapshot=$deploySnapshot +""" +} + +stage('Default build') { + if (!enableDefaultBuild) { + echo 'Skipping default build and integration tests in the default environment' + helper.markStageSkipped() + return + } + runBuildOnNode { + helper.withMavenWorkspace(mavenSettingsConfig: deploySnapshot ? helper.configuration.file.deployment.maven.settingsId : null) { + sh """ \ + mvn clean \ + --fail-at-end \ + ${deploySnapshot ? "\ + deploy -DdeployAtEnd=true \ + " : "\ + install \ + "} \ + -Pdist \ + -Psigtest \ + -Pjqassistant \ + ${enableDefaultBuildIT ? '' : '-DskipITs'} \ + ${toTestJdkArg(environments.content.jdk.default)} \ + """ + + dir(helper.configuration.maven.localRepositoryPath) { + stash name:'default-build-result', includes:"org/hibernate/validator/**" + } + } + } +} + +stage('Non-default environments') { + Map parameters = [:] + + // Test with multiple JDKs + environments.content.jdk.enabled.each { JdkBuildEnvironment buildEnv -> + parameters.put(buildEnv.tag, { + runBuildOnNode { + helper.withMavenWorkspace(jdk: buildEnv.buildJdkTool) { + mavenNonDefaultBuild buildEnv, """ \ + clean install \ + """ + } + } + }) + } + + // Run the TCK with WildFly in multiple environments + environments.content.wildflyTck.enabled.each { WildFlyTckBuildEnvironment buildEnv -> + parameters.put(buildEnv.tag, { + runBuildOnNode { + helper.withMavenWorkspace(jdk: buildEnv.buildJdkTool) { + mavenNonDefaultBuild buildEnv, """ \ + clean install \ + -pl tck-runner \ + -Dincontainer \ + """ + } + } + }) + } + + if (parameters.isEmpty()) { + echo 'Skipping builds in non-default environments' + helper.markStageSkipped() + } + else { + parameters.put('failFast', false) + parallel(parameters) + } +} + +} // End of helper.runWithNotification + +// Job-specific helpers + +enum TestCondition { + // For environments that are expected to work correctly + // before merging into main or maintenance branches. + // Tested on main and maintenance branches, on feature branches, and for PRs. + BEFORE_MERGE, + // For environments that are expected to work correctly, + // but are considered too resource-intensive to test them on pull requests. + // Tested on main and maintenance branches only. + // Not tested on feature branches or PRs. + AFTER_MERGE, + // For environments that may not work correctly. + // Only tested when explicitly requested through job parameters. + ON_DEMAND; + + // Work around JENKINS-33023 + // See https://issues.jenkins-ci.org/browse/JENKINS-33023?focusedCommentId=325738&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-325738 + public TestCondition() {} +} + +abstract class BuildEnvironment { + boolean isDefault = false + TestCondition condition + String toString() { getTag() } + abstract String getTag() + boolean isDefault() { isDefault } + boolean requiresDefaultBuildArtifacts() { true } +} + +class JdkBuildEnvironment extends BuildEnvironment { + String version + String buildJdkTool + String testJdkTool + @Override + String getTag() { "jdk-$version" } + @Override + boolean requiresDefaultBuildArtifacts() { false } +} + +class WildFlyTckBuildEnvironment extends BuildEnvironment { + String javaVersion + String buildJdkTool + @Override + String getTag() { "wildfly-tck-jdk$javaVersion" } + @Override + boolean requiresDefaultBuildArtifacts() { true } +} + +void keepOnlyEnvironmentsMatchingFilter(String regex) { + def pattern = /$regex/ + + boolean enableDefault = ('default' =~ pattern) + + environments.content.each { key, envSet -> + envSet.enabled.removeAll { buildEnv -> + !(buildEnv.tag =~ pattern) && !(envSet.default == buildEnv && enableDefault) + } + } +} + +void keepOnlyEnvironmentsFromSet(String environmentSetName) { + boolean enableDefaultEnv = false + boolean enableBeforeMergeEnvs = false + boolean enableAfterMergeEnvs = false + boolean enableOnDemandEnvs = false + switch (environmentSetName) { + case 'DEFAULT': + enableDefaultEnv = true + break + case 'SUPPORTED': + enableDefaultEnv = true + enableBeforeMergeEnvs = true + enableAfterMergeEnvs = true + break + case 'ALL': + enableDefaultEnv = true + enableBeforeMergeEnvs = true + enableAfterMergeEnvs = true + enableOptional = true + break + case 'AUTOMATIC': + if (helper.scmSource.pullRequest) { + echo "Building pull request '$helper.scmSource.pullRequest.id'" + enableDefaultEnv = true + enableBeforeMergeEnvs = true + } else if (helper.scmSource.branch.primary) { + echo "Building primary branch '$helper.scmSource.branch.name'" + enableDefaultEnv = true + enableBeforeMergeEnvs = true + enableAfterMergeEnvs = true + echo "Legacy integration tests are enabled for the default build environment." + enableDefaultBuildLegacyIT = true + } else { + echo "Building feature branch '$helper.scmSource.branch.name'" + enableDefaultEnv = true + enableBeforeMergeEnvs = true + } + break + default: + throw new IllegalArgumentException( + "Unknown value for param 'ENVIRONMENT_SET': '$environmentSetName'." + ) + } + + // Filter environments + + environments.content.each { key, envSet -> + envSet.enabled.removeAll { buildEnv -> ! ( + enableDefaultEnv && buildEnv.isDefault || + enableBeforeMergeEnvs && buildEnv.condition == TestCondition.BEFORE_MERGE || + enableAfterMergeEnvs && buildEnv.condition == TestCondition.AFTER_MERGE || + enableOnDemandEnvs && buildEnv.condition == TestCondition.ON_DEMAND ) } + } +} + +void runBuildOnNode(Closure body) { + runBuildOnNode( NODE_PATTERN_BASE, body ) +} + +void runBuildOnNode(String label, Closure body) { + node( label ) { + timeout( [time: 1, unit: 'HOURS'], body ) + } +} + +void mavenNonDefaultBuild(BuildEnvironment buildEnv, String args, String projectPath = '.') { + if ( buildEnv.requiresDefaultBuildArtifacts() ) { + dir(helper.configuration.maven.localRepositoryPath) { + unstash name:'default-build-result' + } + } + + // Add a suffix to tests to distinguish between different executions + // of the same test in different environments in reports + def testSuffix = buildEnv.tag.replaceAll('[^a-zA-Z0-9_\\-+]+', '_') + + dir(projectPath) { + sh """ \ + mvn -Dsurefire.environment=$testSuffix \ + ${toTestJdkArg(buildEnv)} \ + --fail-at-end \ + $args \ + """ + } +} + +String toTestJdkArg(BuildEnvironment buildEnv) { + String args = '' + + if ( ! (buildEnv instanceof JdkBuildEnvironment) ) { + return args; + } + + String testJdkTool = buildEnv.testJdkTool + if ( testJdkTool ) { + def testJdkToolPath = tool(name: testJdkTool, type: 'jdk') + args += " -Dsurefire.jvm.java_executable=$testJdkToolPath/bin/java" + } + + return args +} diff --git a/README.md b/README.md index ec1fe6f9a1..066efa717e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Hibernate Validator -*Version: 6.0.9.Final - 27-03-2018* +*Version: 6.0.23.Final - 2022-02-09* ## What is it? @@ -35,7 +35,7 @@ Logging will delegate any log requests to that provider. org.hibernate.validator hibernate-validator - 6.0.9.Final + 6.0.23.SP2 You also need an API and implementation of the Unified Expression Language. These dependencies must be explicitly added in an SE environment. @@ -54,7 +54,7 @@ extension by adding the following dependency: org.hibernate.validator hibernate-validator-cdi - 6.0.9.Final + 6.0.23.Final * _hibernate-validator-annotation-processor-<version>.jar_ is an optional jar which can be integrated with your build @@ -71,7 +71,7 @@ the Apache Software License 2.0. Refer to license.txt for more information. You can build Hibernate Validator from source by cloning the git repository git://github.com/hibernate/hibernate-validator.git. You will also need a JDK 8 and Maven 3 (>= 3.3.1). With these prerequisites in place you can compile the source via: - mvn -s settings-example.xml clean install + mvn clean install There are more build options available as well. For more information refer to [Contributing to Hibernate Validator](http://hibernate.org/validator/contribute/). @@ -83,7 +83,7 @@ To build Hibernate Validator with JDK 9, export the following environment variab Then the build can be started like this: - mvn -s settings-example.xml clean install + mvn clean install Here are the reasons why we added the various build options: diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml index 3687fd8c44..6ebb01d661 100644 --- a/annotation-processor/pom.xml +++ b/annotation-processor/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java index b093800b2a..db9cb070a4 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/AbstractElementVisitor.java @@ -10,7 +10,7 @@ import java.util.Set; import javax.lang.model.element.ElementVisitor; -import javax.lang.model.util.ElementKindVisitor6; +import javax.lang.model.util.ElementKindVisitor8; import org.hibernate.validator.ap.internal.checks.ConstraintCheckIssue; import org.hibernate.validator.ap.internal.util.CollectionHelper; @@ -25,7 +25,7 @@ * * @author Marko Bekhta */ -public class AbstractElementVisitor extends ElementKindVisitor6 { +public class AbstractElementVisitor extends ElementKindVisitor8 { protected final MessagerAdapter messager; diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java index b323fef90c..f4196445be 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/AnnotationTypeMemberCheck.java @@ -17,8 +17,8 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; -import javax.lang.model.util.TypeKindVisitor6; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; +import javax.lang.model.util.TypeKindVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.AnnotationApiHelper; @@ -225,13 +225,13 @@ private ExecutableElement getMethod(TypeElement element, String name) { private DeclaredType getComponentTypeOfArrayReturnType(ExecutableElement method) { return method.getReturnType().accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public DeclaredType visitArray(ArrayType t, Void p) { return t.getComponentType().accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { @@ -259,7 +259,7 @@ public DeclaredType visitDeclared(DeclaredType t, Void p) { private boolean validateWildcardBounds(TypeMirror type, final TypeMirror expectedExtendsBound, final TypeMirror expectedSuperBound) { Boolean theValue = type.accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public Boolean visitWildcard(WildcardType t, Void p) { @@ -289,7 +289,7 @@ private boolean isEmptyArray(AnnotationValue annotationValue) { return annotationValue != null && Boolean.TRUE.equals( annotationValue.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Boolean visitArray(List values, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java index 0bf9e88250..2c24579ef6 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/CrossParameterConstraintCheck.java @@ -15,9 +15,9 @@ 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.ElementKindVisitor8; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; +import javax.lang.model.util.TypeKindVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.AnnotationApiHelper; @@ -61,7 +61,7 @@ public Set checkAnnotationType(TypeElement element, Annota return Collections.emptySet(); } - DeclaredType elementType = element.asType().accept( new TypeKindVisitor6() { + DeclaredType elementType = element.asType().accept( new TypeKindVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { @@ -135,7 +135,7 @@ private boolean checkValidationAppliesToReturnType(ExecutableElement validationA final DeclaredType constraintTargetType = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); // Check the return type - return validationAppliesToMethod.getReturnType().accept( new TypeKindVisitor6() { + return validationAppliesToMethod.getReturnType().accept( new TypeKindVisitor8() { @Override public Boolean visitDeclared(DeclaredType t, Void p) { @@ -153,7 +153,7 @@ private boolean checkValidationAppliesToDefaultValue(ExecutableElement validatio final DeclaredType constraintTargetType = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); // Check the return type - return validationAppliesToMethod.getDefaultValue().accept( new SimpleAnnotationValueVisitor6() { + return validationAppliesToMethod.getDefaultValue().accept( new SimpleAnnotationValueVisitor8() { @Override public Boolean visitEnumConstant(VariableElement c, Void p) { @@ -167,7 +167,7 @@ public Boolean visitEnumConstant(VariableElement c, Void p) { private ExecutableElement getValidationAppliesToMethod(Element annotation) { for ( Element e : annotation.getEnclosedElements() ) { - ExecutableElement method = e.accept( new ElementKindVisitor6() { + ExecutableElement method = e.accept( new ElementKindVisitor8() { @Override public ExecutableElement visitExecutableAsMethod(ExecutableElement e, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java index 3cadaf801b..9a420a18f5 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/checks/GroupSequenceProviderCheck.java @@ -17,8 +17,8 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.ElementKindVisitor6; -import javax.lang.model.util.SimpleTypeVisitor6; +import javax.lang.model.util.ElementKindVisitor8; +import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.AnnotationApiHelper; @@ -150,7 +150,7 @@ private Set checkAnnotationValue(TypeElement element, Anno */ private boolean hasPublicDefaultConstructor(TypeElement element) { return element.accept( - new ElementKindVisitor6( Boolean.FALSE ) { + new ElementKindVisitor8( Boolean.FALSE ) { @Override public Boolean visitTypeAsClass(TypeElement typeElement, Void aVoid) { @@ -186,7 +186,7 @@ public Boolean visitExecutableAsConstructor(ExecutableElement constructorElement */ private TypeMirror retrieveGenericProviderType(TypeMirror typeMirror) { return typeMirror.accept( - new SimpleTypeVisitor6() { + new SimpleTypeVisitor8() { @Override public TypeMirror visitDeclared(DeclaredType declaredType, Void aVoid) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java index 5040642b8a..b5d8c3ee98 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/AnnotationApiHelper.java @@ -22,7 +22,7 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; -import javax.lang.model.util.SimpleAnnotationValueVisitor6; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.lang.model.util.Types; /** @@ -268,7 +268,7 @@ public List getAnnotationArrayValue(AnnotationMirror } List theValue = annotationValue.accept( - new SimpleAnnotationValueVisitor6, Void>() { + new SimpleAnnotationValueVisitor8, Void>() { @Override public List visitArray(List values, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java index f13c3c6ffc..feae0d53f8 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/Configuration.java @@ -6,8 +6,9 @@ */ package org.hibernate.validator.ap.internal.util; -import java.text.MessageFormat; +import java.util.Locale; import java.util.Map; + import javax.annotation.processing.Messager; import javax.tools.Diagnostic.Kind; @@ -97,11 +98,12 @@ private Kind getDiagnosticKindOption(Map options, Messager messa } catch (IllegalArgumentException e) { messager.printMessage( - Kind.WARNING, MessageFormat.format( - "The given value {0} is no valid diagnostic kind. {1} will be used.", - diagnosticKindFromOptions, - DEFAULT_DIAGNOSTIC_KIND - ) + Kind.WARNING, String.format( + Locale.ROOT, + "The given value %1$s is no valid diagnostic kind. %2$s will be used.", + diagnosticKindFromOptions, + DEFAULT_DIAGNOSTIC_KIND + ) ); } } @@ -118,10 +120,11 @@ private boolean getVerboseOption(Map options, Messager messager) if ( theValue ) { messager.printMessage( - Kind.NOTE, MessageFormat.format( - "Verbose reporting is activated. Some processing information will be displayed using diagnostic kind {0}.", - Kind.NOTE - ) + Kind.NOTE, String.format( + Locale.ROOT, + "Verbose reporting is activated. Some processing information will be displayed using diagnostic kind %1$s.", + Kind.NOTE + ) ); } diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java index 0cc022c357..c390a8f43a 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/ConstraintHelper.java @@ -44,9 +44,9 @@ 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.ElementKindVisitor8; +import javax.lang.model.util.SimpleAnnotationValueVisitor8; +import javax.lang.model.util.TypeKindVisitor8; import javax.lang.model.util.Types; import org.hibernate.validator.ap.internal.util.TypeNames.BeanValidationTypes; @@ -394,7 +394,7 @@ public List getPartsOfMultiValuedConstraint( .getAnnotationArrayValue( annotationMirror, "value" ) ) { oneValuePart.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Void visitAnnotation(AnnotationMirror a, Void p) { @@ -454,7 +454,7 @@ public boolean isComposedConstraint(TypeElement element) { return Boolean.TRUE.equals( element.asType().accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public Boolean visitDeclared(DeclaredType constraintValidatorImplementation, Void p) { @@ -558,7 +558,7 @@ public ConstraintCheckResult checkCrossParameterTypes(DeclaredType constraintAnn final TypeMirror objectMirror = annotationApiHelper.getMirrorForType( Object.class ); TypeMirror type = determineSupportedType( crossParameterValidator ); - Boolean supported = type.accept( new TypeKindVisitor6() { + Boolean supported = type.accept( new TypeKindVisitor8() { @Override public Boolean visitArray(ArrayType t, Void p) { return typeUtils.isSameType( t.getComponentType(), objectMirror ); @@ -647,7 +647,7 @@ private boolean isMultiValuedConstraint(AnnotationMirror annotationMirror) { for ( AnnotationValue oneAnnotationValue : annotationArrayValue ) { Boolean isConstraintAnnotation = oneAnnotationValue.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Boolean visitAnnotation( @@ -797,7 +797,7 @@ private AnnotationProcessorConstraintTarget getConstraintTarget(AnnotationMirror for ( Element e : annotation.getAnnotationType().asElement().getEnclosedElements() ) { - Boolean isValidationAppliesToMethod = e.accept( new ElementKindVisitor6() { + Boolean isValidationAppliesToMethod = e.accept( new ElementKindVisitor8() { @Override public Boolean visitExecutableAsMethod(ExecutableElement e, Void p) { if ( e.getSimpleName().contentEquals( "validationAppliesTo" ) ) { @@ -818,7 +818,7 @@ public Boolean visitExecutableAsMethod(ExecutableElement e, Void p) { } return validationAppliesTo.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { private final TypeMirror constraintTargetMirror = annotationApiHelper.getDeclaredTypeByName( BeanValidationTypes.CONSTRAINT_TARGET ); @@ -870,7 +870,7 @@ private TypeMirror determineSupportedType(AnnotationValue validatorClassReferenc TypeMirror constraintValidatorImplementation = getConstraintValidatorSuperType( validatorClassReference ); return constraintValidatorImplementation.accept( - new TypeKindVisitor6() { + new TypeKindVisitor8() { @Override public TypeMirror visitDeclared(DeclaredType constraintValidatorImplementation, Void p) { @@ -889,7 +889,7 @@ private boolean isValidationTargetSupported(AnnotationValue oneValidatorClassRef private Set getSupportedValidationTargets(AnnotationValue oneValidatorClassReference) { // determine the class that could contain the @SupportedValidationTarget annotation. TypeMirror validatorClass = oneValidatorClassReference.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public TypeMirror visitType(TypeMirror t, Void p) { @@ -898,7 +898,7 @@ public TypeMirror visitType(TypeMirror t, Void p) { }, null ); - DeclaredType validatorType = validatorClass.accept( new TypeKindVisitor6() { + DeclaredType validatorType = validatorClass.accept( new TypeKindVisitor8() { @Override public DeclaredType visitDeclared(DeclaredType t, Void p) { return t; @@ -925,7 +925,7 @@ public DeclaredType visitDeclared(DeclaredType t, Void p) { else { List values = annotationApiHelper.getAnnotationArrayValue( supportedTargetDecl, "value" ); for ( AnnotationValue val : values ) { - AnnotationProcessorValidationTarget target = val.accept( new SimpleAnnotationValueVisitor6() { + AnnotationProcessorValidationTarget target = val.accept( new SimpleAnnotationValueVisitor8() { @Override public AnnotationProcessorValidationTarget visitEnumConstant(VariableElement c, Void p) { return AnnotationProcessorValidationTarget.valueOf( c.getSimpleName().toString() ); @@ -944,7 +944,7 @@ public AnnotationProcessorValidationTarget visitEnumConstant(VariableElement c, private TypeMirror getConstraintValidatorSuperType(AnnotationValue oneValidatorClassReference) { TypeMirror type = oneValidatorClassReference.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public TypeMirror visitType(TypeMirror t, Void p) { @@ -1009,7 +1009,7 @@ private List getValidatorClassesFromConstraintMetaAnn AnnotationValue validatedBy = annotationApiHelper.getAnnotationValue( constraintMetaAnnotation, "validatedBy" ); return validatedBy.accept( - new SimpleAnnotationValueVisitor6, Void>() { + new SimpleAnnotationValueVisitor8, Void>() { @Override public List visitArray(List values, Void p) { diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java index 5c6e26b8f0..23a3849d2b 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/MessagerAdapter.java @@ -8,6 +8,7 @@ import java.text.MessageFormat; import java.util.Collection; +import java.util.Locale; import java.util.ResourceBundle; import javax.annotation.processing.Messager; @@ -45,7 +46,7 @@ public MessagerAdapter(Messager messager, Kind diagnosticKind) { this.messager = messager; this.diagnosticKind = diagnosticKind; - errorMessages = ResourceBundle.getBundle( "org.hibernate.validator.ap.ValidationProcessorMessages" ); + errorMessages = ResourceBundle.getBundle( "org.hibernate.validator.ap.ValidationProcessorMessages", Locale.getDefault() ); } /** @@ -114,7 +115,8 @@ private void report(ConstraintCheckIssue issue, Kind kind) { String message = errorMessages.getString( issue.getMessageKey() ); if ( issue.getMessageParameters() != null ) { - message = MessageFormat.format( message, issue.getMessageParameters() ); + MessageFormat messageFormat = new MessageFormat( message, Locale.getDefault() ); + message = messageFormat.format( issue.getMessageParameters() ); } messager.printMessage( diff --git a/build-config/pom.xml b/build-config/pom.xml index 6e3d9a3cff..51e19c0771 100644 --- a/build-config/pom.xml +++ b/build-config/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -35,6 +35,12 @@ true + + maven-gpg-plugin + + true + + diff --git a/build-config/src/main/resources/forbidden-common.txt b/build-config/src/main/resources/forbidden-common.txt index 3f02a90e8f..47ae8513d0 100644 --- a/build-config/src/main/resources/forbidden-common.txt +++ b/build-config/src/main/resources/forbidden-common.txt @@ -26,3 +26,7 @@ org.assertj.core.api.Assertions#fail() java.lang.StringBuffer org.jboss.logging.processor.util.Objects + +################################################################################################################ +# JAXB shouldn't be used anymore as it is targeted to be removed from the JDK +javax.xml.bind.** diff --git a/cdi/pom.xml b/cdi/pom.xml index 08d7f43ccb..4dbfd25ba7 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -67,8 +67,8 @@ test - log4j - log4j + org.apache.logging.log4j + log4j-core test diff --git a/cdi/src/main/java/org/hibernate/validator/cdi/internal/ValidatorFactoryBean.java b/cdi/src/main/java/org/hibernate/validator/cdi/internal/ValidatorFactoryBean.java index 342342f282..eec74f1042 100644 --- a/cdi/src/main/java/org/hibernate/validator/cdi/internal/ValidatorFactoryBean.java +++ b/cdi/src/main/java/org/hibernate/validator/cdi/internal/ValidatorFactoryBean.java @@ -22,6 +22,8 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.literal.NamedLiteral; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.InjectionPoint; @@ -37,12 +39,15 @@ import javax.validation.ValidatorFactory; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cdi.spi.BeanNames; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; import org.hibernate.validator.internal.util.privilegedactions.GetInstancesFromServiceLoader; import org.hibernate.validator.internal.util.privilegedactions.LoadClass; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; /** * A {@link Bean} representing a {@link ValidatorFactory}. There is one instance of this type representing the default @@ -126,6 +131,22 @@ public ValidatorFactory create(CreationalContext ctx) { config.parameterNameProvider( createParameterNameProvider( config ) ); config.clockProvider( createClockProvider( config ) ); + if ( config instanceof HibernateValidatorConfiguration ) { + HibernateValidatorConfiguration hvConfig = (HibernateValidatorConfiguration) config; + Instance beanMetaDataClassNormalizerInstance = + beanManager.createInstance() + .select( + BeanMetaDataClassNormalizer.class, + NamedLiteral.of( BeanNames.BEAN_META_DATA_CLASS_NORMALIZER ) + ); + if ( beanMetaDataClassNormalizerInstance.isResolvable() ) { + BeanMetaDataClassNormalizer normalizer = beanMetaDataClassNormalizerInstance.get(); + destructibleResources.add( new DestructibleBeanInstance<>( beanManager, normalizer ) ); + + hvConfig.beanMetaDataClassNormalizer( normalizer ); + } + } + addValueExtractorBeans( config ); return config.buildValidatorFactory(); diff --git a/cdi/src/main/java/org/hibernate/validator/cdi/spi/BeanNames.java b/cdi/src/main/java/org/hibernate/validator/cdi/spi/BeanNames.java new file mode 100644 index 0000000000..cb9f1da7f5 --- /dev/null +++ b/cdi/src/main/java/org/hibernate/validator/cdi/spi/BeanNames.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.cdi.spi; + +public final class BeanNames { + + private BeanNames() { + } + + public static final String BEAN_META_DATA_CLASS_NORMALIZER = "hibernate-validator-bean-meta-data-class-normalizer"; + +} diff --git a/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxy.java b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxy.java new file mode 100644 index 0000000000..5bb6b09212 --- /dev/null +++ b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxy.java @@ -0,0 +1,10 @@ +/* + * 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.cdi.internal.beanmetadataclassnormalizer; + +public interface CustomProxy { +} diff --git a/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxyBeanMetaDataClassNormalizer.java b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxyBeanMetaDataClassNormalizer.java new file mode 100644 index 0000000000..2e59a3819e --- /dev/null +++ b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxyBeanMetaDataClassNormalizer.java @@ -0,0 +1,89 @@ +/* + * 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.cdi.internal.beanmetadataclassnormalizer; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Set; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.literal.NamedLiteral; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.InjectionPoint; + +import org.hibernate.validator.cdi.spi.BeanNames; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; + + +public class CustomProxyBeanMetaDataClassNormalizer + implements BeanMetaDataClassNormalizer, Bean { + + @Override + public Class normalize(Class clazz) { + if ( CustomProxy.class.isAssignableFrom( clazz ) ) { + return clazz.getSuperclass(); + } + return clazz; + } + + @Override + public Class getBeanClass() { + return BeanMetaDataClassNormalizer.class; + } + + @Override + public Set getInjectionPoints() { + return Collections.emptySet(); + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public BeanMetaDataClassNormalizer create(CreationalContext creationalContext) { + return new CustomProxyBeanMetaDataClassNormalizer(); + } + + @Override + public void destroy(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer, + CreationalContext creationalContext) { + // Nothing to do + } + + @Override + public Set getTypes() { + return Collections.singleton( BeanMetaDataClassNormalizer.class ); + } + + @Override + public Set getQualifiers() { + return Collections.singleton( NamedLiteral.of( BeanNames.BEAN_META_DATA_CLASS_NORMALIZER ) ); + } + + @Override + public Class getScope() { + return ApplicationScoped.class; + } + + @Override + public String getName() { + return BeanNames.BEAN_META_DATA_CLASS_NORMALIZER; + } + + @Override + public Set> getStereotypes() { + return Collections.emptySet(); + } + + @Override + public boolean isAlternative() { + return false; + } +} diff --git a/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxyBeanMetadataClassNormalizerCdiExtension.java b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxyBeanMetadataClassNormalizerCdiExtension.java new file mode 100644 index 0000000000..7ea42a77f8 --- /dev/null +++ b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/CustomProxyBeanMetadataClassNormalizerCdiExtension.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 org.hibernate.validator.test.cdi.internal.beanmetadataclassnormalizer; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.Extension; + +public class CustomProxyBeanMetadataClassNormalizerCdiExtension implements Extension { + + public void addEjbProxyNormalizer(@Observes AfterBeanDiscovery afterBeanDiscovery) { + afterBeanDiscovery.addBean( new CustomProxyBeanMetaDataClassNormalizer() ); + } +} diff --git a/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/ExtensionProvidedBeanMetadataClassNormalizerTest.java b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/ExtensionProvidedBeanMetadataClassNormalizerTest.java new file mode 100644 index 0000000000..e7b7778506 --- /dev/null +++ b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/beanmetadataclassnormalizer/ExtensionProvidedBeanMetadataClassNormalizerTest.java @@ -0,0 +1,102 @@ +/* + * 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.cdi.internal.beanmetadataclassnormalizer; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.inject.Inject; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.cdi.HibernateValidator; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.testng.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; + +import org.testng.annotations.Test; + +public class ExtensionProvidedBeanMetadataClassNormalizerTest extends Arquillian { + + @Deployment + public static JavaArchive createDeployment() { + return ShrinkWrap.create( JavaArchive.class ) + .addAsManifestResource( EmptyAsset.INSTANCE, "beans.xml" ) + // Register the CDI extension that provides the normalizer bean + .addAsManifestResource( + new StringAsset( CustomProxyBeanMetadataClassNormalizerCdiExtension.class.getName() ), + "services/javax.enterprise.inject.spi.Extension" + ); + } + + @HibernateValidator + @Inject + ValidatorFactory validatorFactory; + + @HibernateValidator + @Inject + Validator validator; + + @Inject + ValidatorFactory defaultValidatorFactory; + + @Inject + Validator defaultValidator; + + @Test + public void testProxyMetadataIgnoredWithQualifiedValidator() throws Exception { + assertThat( validator ).isNotNull(); + doTest( validator ); + } + + @Test + public void testProxyMetadataIgnoredWithDefaultValidator() throws Exception { + assertThat( defaultValidator ).isNotNull(); + doTest( defaultValidator ); + } + + @Test + public void testProxyMetadataIgnoredWithQualifiedValidatorFactory() throws Exception { + assertThat( validatorFactory ).isNotNull(); + doTest( validatorFactory.getValidator() ); + } + + @Test + public void testProxyMetadataIgnoredWithDefaultValidatorFactory() throws Exception { + assertThat( defaultValidatorFactory ).isNotNull(); + doTest( defaultValidatorFactory.getValidator() ); + } + + private void doTest(Validator validator) { + assertThat( validator ).isNotNull(); + /* + * Even though we pass an instance of the proxy class that has invalid annotations, + * we expect those to be ignored + * because of the class normalizer we defined. + */ + assertThat( validator.validate( new TestEntityProxy() ) ).hasSize( 1 ); + } + + public static class TestEntity { + @NotNull + private String foo; + } + + public static class TestEntityProxy extends TestEntity implements CustomProxy { + /* + * This is invalid, but should be ignored because it's defined in a proxy class which gets ignored + * because of the class normalizer we defined. + */ + @DecimalMax(value = "foo") + private String foo; + } +} diff --git a/changelog.txt b/changelog.txt index 9693c913f7..d441fa1013 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,199 @@ Hibernate Validator Changelog ============================= +6.0.23.SP2 (2025-06-13) +------------------------- + +** Improvement + * HV-1816 Disable Expression Language by default for custom constraint violations (Partial backport of internal changes only) + +6.0.23.Final (2022-02-09) +------------------------- + +** Bug + * HV-1878 java.sql.Date is not supported anymore + * HV-1833 DomainNameUtil issues java.lang.StackOverflowError on very large error (@Email validation) + +** Task + * HV-1881 Switch to Log4j 2 for 6.0 testing + * HV-1879 Backport Jenkinsfiles to 6.0 + * HV-1877 Move the release job to a separate Jenkinsfile + * HV-1871 Sign published artifacts + * HV-1858 Switch to OSSRH for deployment to Maven Central + +6.0.22.Final (16-12-2020) +------------------------- + +** Bug + * HV-1821 - engine - HV-1755 introduces NPE in org.hibernate.validator.internal.engine.ValidatorFactoryImpl constructor + +6.0.21.Final (29-09-2020) +------------------------- + +** Bug + * HV-1804 - translations - Fix Dutch translation for @Size constraint + * HV-1761 - engine - Interpolation of primitive arrays causes a ClassCastException + +** New Feature + * HV-1657 - engine - Make the “propertyPath” available via the “HibernateMessageInterpolatorContext” + +** Task + * HV-1795 - build - Remove link to JavaMoney javadoc + +6.0.20.Final (06-05-2020) +------------------------- + +** Bug + * HV-1774 - engine - Invalid parsing of EL expression can lead to invalid EL expressions considered valid + * HV-1772 - engine - Building multiple ValidatorFactory instances from a single Configuration violates specification for MessageInterpolator + +** Improvement + * HV-1773 - documentation - Be more explicit about issues with EL injection and how to avoid them + +6.0.19.Final (27-03-2020) +------------------------- + +** Bug + * HV-1758 - translations - Extra dollar sign in validation messages for ModCheck + * HV-1756 - translations - Incorrect variables in the newly added translations + +** New Feature + * HV-1755 - engine - Introduce the notion of BeanMetaDataClassNormalizer in the standard ValidatorFactory + +** Task + * HV-1753 - tests - Force Pax-Exam and Karaf to use Maven Central repository with SSL enabled + +6.0.18.Final (25-10-2019) +------------------------- + +** Bug + * HV-1722 - engine - Remove settings-example.xml reference from .travis.yml + * HV-1720 - engine - Support bounded wildcard types in container value unwrapping + * HV-1715 - engine - Validation can sometimes proceed to the next group in sequence even after one of the constraints generated a violation + +** Improvement + * HV-1729 - performance - Skip allocation of an action for each need to access the context classloader + +** Task + * HV-1740 - engine - Deprecate @SafeHtml + * HV-1739 - engine - CVE-2019-10219 Security issue with @SafeHtml + * HV-1731 - tck-runner - Move TCK signature check to tck-runner module + * HV-1728 - build - Upgrade to WildFly 17.0.1.Final + * HV-1724 - build - Update to OpenJFX 11.0.2 + +6.0.17.Final (13-06-2019) +------------------------- + +** Bug + * HV-1713 - engine - Missing violation when a bean is validated with different groups + * HV-1709 - validators - Polish Identification numbers are not considering length of the value + * HV-1706 - validators - ISBN-13 algorithm does not handle checksum 10 + +** Task + * HV-1717 - build - Update test dependencies + * HV-1716 - build - Update WildFly secondary version to 17.0.0.Beta1 + * HV-1711 - build - Fix aggregated javadoc build with recent JDK 11 + * HV-1710 - build - Remove settings-example.xml + +6.0.16.Final (21-03-2019) +------------------------- + +** Bug + * HV-1704 - build - Build fails on Windows + * HV-1699 - validators - Rounding error when having a BigDecimal at runtime with @Max/@Min annotation set on a Number field + +** Improvement + * HV-1696 - engine - Avoid using computeIfAbsent for the common case when getting bean metadata + * HV-1695 - engine - Avoid creating an empty map for group conversions + * HV-1694 - engine - Reduce memory allocation for unconstrained beans + +** Task + * HV-1705 - build - Upgrade WildFly to 16.0.0.Final + * HV-1693 - build - Test compatibility with JDK 12 + +6.0.15.Final (18-02-2019) +------------------------- + +** Bug + * HV-1692 - engine - Custom group sequence might cause StackOverflowError on objects with cycles + +6.0.14.Final (04-01-2019) +------------------------- + +** Bug + * HV-1684 - validators - StackOverflowError with Hibernate Validator 6.0.13.Final + +** Improvement + * HV-1686 - translations - Fix a few typos in the Dutch translation + +** Task + * HV-1685 - integration - Upgrade the WildFly versions we generate patches for to 14.0.1.Final and 15.0.0.Final + * HV-1659 - integration - Upgrade WildFly to 14.0.1 + +6.0.13.Final (22-08-2018) +------------------------- + +** Bug + * HV-1652 - engine - Fix a few theoretical null pointer dereference issues + * HV-1650 - validators - French translations are badly encoded + +6.0.12.Final (10-08-2018) +------------------------- + +** Bug + * HV-1645 - extensions - Revert HV-1609 due to increased CDI startup caused by ValidateableBeanFilter + * HV-1644 - build - Using Hibernate Validator with Java 11 brings JavaFX on the classpath + +** Improvement + * HV-1643 - translations - Fix Russian translation for @Null constraint + +** Task + * HV-1649 - tck-runner - Upgrade to Bean Validation TCK 2.0.4.Final + * HV-1648 - build, integration - Reenable WildFly integration tests for JDK 11 + * HV-1647 - tck-runner - Allow running TCK tests in container mode with JDK 11 + * HV-1646 - build, integration, tck-runner - Upgrade WildFly to 14.0.0.Beta1 + * HV-1627 - build - Upgrade our JPA test dependency to 2.2 + +6.0.11.Final (18-07-2018) +------------------------- + +** Bug + * HV-1637 - translations - PropertNotFoundException for @DecimalMax when using the German translation + +** Improvement + * HV-1628 - annotation-processor, engine, tests - Configure a stricter forbidden-apis policy and remove calls deprecated in Java 10 + * HV-1615 - translations - Improvements on the dutch translations + +** Remove Feature + * HV-1624 - engine - Remove the StaticFieldELResolver + +** Task + * HV-1641 - build - Use the OSS snapshot repository to download the JavaFX dependencies when building with JDK 11 + * HV-1640 - build - Add compatibility with the latest JDK 11 build 22 + * HV-1610 - integration - Reenable OSGi tests for JDK 10 + * HV-1608 - build - Have the build work with JDK 11 + * HV-1577 - engine - Use Stax instead of JAXB to parse the XML descriptors + +6.0.10.Final (15-05-2018) +------------------------- + +** Bug + * HV-1614 - engine - Unable to specify constraints at more than 1 nested parameter of a typed container + * HV-1609 - integration - CDI extension should not rely on @WithAnnotations filtering + * HV-1604 - engine - Initializing JPATraversableResolver fails with IllegalAccessException + * HV-1598 - engine - Fix the behavior of XML default-validated-executable-types + +** Improvement + * HV-1612 - translations - Add Dutch translation of the validation messages + * HV-1611 - translations - Be consistent in the case of the validation messages + * HV-1592 - engine - Make ConstraintValidator declaration stricter + * HV-1534 - engine - Allow getter constraints to be specified for subclasses in XML configuration + +** Task + * HV-1607 - build - Have the build work with JDK 10 + * HV-1606 - tck-runner - Update TCK to 2.0.3.Final + * HV-1605 - build - Update Surefire to 2.21.0 for JDK 10 support + 6.0.9.Final (27-03-2018) ------------------------- diff --git a/copyright.txt b/copyright.txt index 3c78287f8e..a386c100bc 100644 --- a/copyright.txt +++ b/copyright.txt @@ -51,6 +51,7 @@ Nicola Ferraro Nicolas François Paolo Perrotta Pete Muir +Rob Dickinson Sanne Grinovero Sebastian Bayerl Shahram Goodarzi diff --git a/distribution/pom.xml b/distribution/pom.xml index a8d1f5afdd..289e3163b1 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -22,6 +22,7 @@ true + true false .. @@ -50,12 +51,12 @@ - log4j - log4j + org.apache.logging.log4j + log4j-core - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + javax.persistence + javax.persistence-api joda-time @@ -119,7 +120,7 @@ ${java.api-docs.base-url} ${javaee.api-docs.base-url} ${bv.api-docs.base-url} - ${javamoney.api-docs.base-url} + @@ -207,8 +208,25 @@ [9,) - -html5 --add-modules=${maven-javadoc-plugin.jigsaw.modules} + -html5 + + jdk11+ + + [11,) + + + -html5 -source 8 + + + + org.openjfx + javafx-base + ${version.org.openjfx} + provided + + + diff --git a/distribution/src/main/assembly/dist.xml b/distribution/src/main/assembly/dist.xml index 686cb34420..8e57f99dd9 100644 --- a/distribution/src/main/assembly/dist.xml +++ b/distribution/src/main/assembly/dist.xml @@ -42,9 +42,9 @@ dist/lib/optional - log4j:log4j + org.apache.logging.log4j:log4j-core joda-time:joda-time - org.hibernate.javax.persistence:hibernate-jpa-2.1-api + javax.persistence:javax.persistence-api org.jsoup:jsoup com.thoughtworks.paranamer:paranamer diff --git a/documentation/pom.xml b/documentation/pom.xml index 4cfd462026..26ffde6329 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -40,6 +40,7 @@ true + true -Duser.language=en forbidden-allow-junit.txt @@ -98,6 +99,11 @@ assertj-core test + + javax.annotation + javax.annotation-api + test + @@ -334,5 +340,19 @@ + + jdk11+ + + [11,) + + + + org.openjfx + javafx-base + ${version.org.openjfx} + test + + + diff --git a/documentation/src/main/asciidoc/ch01.asciidoc b/documentation/src/main/asciidoc/ch01.asciidoc index 9463a8e47a..2465cbc765 100644 --- a/documentation/src/main/asciidoc/ch01.asciidoc +++ b/documentation/src/main/asciidoc/ch01.asciidoc @@ -206,9 +206,6 @@ These are the module names as declared using the `Automatic-Module-Name` header: These module names are preliminary and may be changed when providing real module descriptors in a future release. -When using Bean Validation XML descriptors (_META-INF/validation.xml_ and/or constraint mapping files), the `java.xml.bind` module must be enabled. -Do so by appending `--add-modules java.xml.bind` to your _java_ invocation. - [WARNING] ==== When using Hibernate Validator with CDI, be careful to not enable the `java.xml.ws.annotation` module of the JDK. diff --git a/documentation/src/main/asciidoc/ch06.asciidoc b/documentation/src/main/asciidoc/ch06.asciidoc index f32bb7964a..18d93ff26f 100644 --- a/documentation/src/main/asciidoc/ch06.asciidoc +++ b/documentation/src/main/asciidoc/ch06.asciidoc @@ -173,6 +173,36 @@ It is important to add each configured constraint violation by calling `addConst Only after that the new constraint violation will be created. ==== +[[el-injection-caution]] +[CAUTION] +==== +**Be aware that the custom message template is passed directly to the Expression Language engine.** + +Thus, you should be very careful when integrating user input in a custom message template as it will be interpreted +by the Expression Language engine, which is usually not the behavior you want and **could allow malicious users to leak +sensitive data or even execute arbitrary code**. + +If you need to integrate user input in your message, you must <> +by unwrapping the context to `HibernateConstraintValidatorContext`. + +The following validator is very unsafe as it includes user input in the violation message. +If the validated `value` contains EL expressions, they will be executed by the EL engine. + +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/elinjection/UnsafeValidator.java[tags=include] +---- + +The following pattern must be used instead: + +[source] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter06/elinjection/SafeValidator.java[tags=include] +---- + +By using expression variables, Hibernate Validator properly handles escaping and EL expressions won't be executed. +==== + Refer to <> to learn how to use the `ConstraintValidatorContext` API to control the property path of constraint violations for class-level constraints. diff --git a/documentation/src/main/asciidoc/ch12.asciidoc b/documentation/src/main/asciidoc/ch12.asciidoc index 22ad182f53..143b8f9544 100644 --- a/documentation/src/main/asciidoc/ch12.asciidoc +++ b/documentation/src/main/asciidoc/ch12.asciidoc @@ -456,10 +456,11 @@ 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 using `HibernateConstraintValidatorContext#addExpressionVariable(String, Object)`. +facility using `HibernateConstraintValidatorContext#addExpressionVariable(String, Object)` +or `HibernateConstraintValidatorContext#addMessageParameter(String, Object)`. + -[[example-custom-message-parameter]] -.Custom `@Future` validator with message parameters +[[example-custom-expression-variable]] +.Custom `@Future` validator injecting an expression variable ==== [source, JAVA, indent=0] ---- @@ -467,12 +468,29 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/context/My ---- ==== + +[[example-custom-message-parameter]] +.Custom `@Future` validator injecting a message parameter +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java[tags=include] +---- +==== ++ +[NOTE] +==== +Apart from the syntax, the main difference between message parameters and expression variables is that message parameters +are simply interpolated whereas expression variables are interpreted using the expression language engine. +In practice, it should not change anything. +==== ++ [NOTE] ==== -Note that the parameters specified via `addExpressionVariable(String, Object)` are global and apply -to 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 +Note that the parameters specified via `addExpressionVariable(String, Object)` and +`addMessageParameter(String, Object)` are global and apply to 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()`. ==== * set an arbitrary dynamic payload - see <> diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter06/elinjection/SafeValidator.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter06/elinjection/SafeValidator.java new file mode 100644 index 0000000000..182ab328da --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter06/elinjection/SafeValidator.java @@ -0,0 +1,39 @@ +package org.hibernate.validator.referenceguide.chapter06.elinjection; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; +import org.hibernate.validator.referenceguide.chapter06.constraintvalidatorpayload.ZipCode; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +//tag::include[] +public class SafeValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if ( value == null ) { + return true; + } + + HibernateConstraintValidatorContext hibernateContext = context.unwrap( + HibernateConstraintValidatorContext.class ); + hibernateContext.disableDefaultConstraintViolation(); + + if ( isInvalid( value ) ) { + hibernateContext + .addExpressionVariable( "validatedValue", value ) + .buildConstraintViolationWithTemplate( "${validatedValue} is not a valid ZIP code" ) + .addConstraintViolation(); + + return false; + } + + return true; + } + + private boolean isInvalid(String value) { + // ... + return false; + } +} +// end::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter06/elinjection/UnsafeValidator.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter06/elinjection/UnsafeValidator.java new file mode 100644 index 0000000000..6adf4632a1 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter06/elinjection/UnsafeValidator.java @@ -0,0 +1,35 @@ +package org.hibernate.validator.referenceguide.chapter06.elinjection; + +import org.hibernate.validator.referenceguide.chapter06.constraintvalidatorpayload.ZipCode; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +//tag::include[] +public class UnsafeValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if ( value == null ) { + return true; + } + + context.disableDefaultConstraintViolation(); + + if ( isInvalid( value ) ) { + context + .buildConstraintViolationWithTemplate( value + " is not a valid ZIP code" ) + .addConstraintViolation(); + + return false; + } + + return true; + } + + private boolean isInvalid(String value) { + // ... + return false; + } +} +// end::include[] diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java index 9cfad74d1f..1f5736a44e 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidator.java @@ -30,7 +30,8 @@ public boolean isValid(Instant value, ConstraintValidatorContext context) { if ( !value.isAfter( now ) ) { hibernateContext.disableDefaultConstraintViolation(); - hibernateContext.addExpressionVariable( "now", now ) + hibernateContext + .addExpressionVariable( "now", now ) .buildConstraintViolationWithTemplate( "Must be after ${now}" ) .addConstraintViolation(); diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java new file mode 100644 index 0000000000..7a6c4c681e --- /dev/null +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/context/MyFutureValidatorMessageParameter.java @@ -0,0 +1,44 @@ +//tag::include[] +package org.hibernate.validator.referenceguide.chapter12.context; + +import java.time.Instant; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraints.Future; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; + +//tag::include[] +public class MyFutureValidatorMessageParameter implements ConstraintValidator { + + @Override + public void initialize(Future constraintAnnotation) { + } + + @Override + public boolean isValid(Instant value, ConstraintValidatorContext context) { + if ( value == null ) { + return true; + } + + HibernateConstraintValidatorContext hibernateContext = context.unwrap( + HibernateConstraintValidatorContext.class + ); + + Instant now = Instant.now( context.getClockProvider().getClock() ); + + if ( !value.isAfter( now ) ) { + hibernateContext.disableDefaultConstraintViolation(); + hibernateContext + .addMessageParameter( "now", now ) + .buildConstraintViolationWithTemplate( "Must be after {now}" ) + .addConstraintViolation(); + + return false; + } + + return true; + } +} +//end::include[] diff --git a/engine/pom.xml b/engine/pom.xml index e615c850cd..47f51e4fc6 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -79,8 +79,8 @@ Optional dependencies --> - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + javax.persistence + javax.persistence-api true @@ -118,10 +118,16 @@ test - log4j - log4j + org.apache.logging.log4j + log4j-core test + + org.apache.logging.log4j + log4j-core + test + test-jar + org.easymock easymock @@ -274,168 +280,27 @@ - pre-jdk9 - - 1.8 - - - - - org.codehaus.mojo - jaxb2-maven-plugin - - - - xjc - - - - - org.hibernate.validator.internal.xml.binding - true - - true - - 2.1 - - src/main/xsd/validation-configuration-2.0.xsd - src/main/xsd/validation-mapping-2.0.xsd - - - - - - - - jdk9 + jdk9+ [9,) --illegal-access=deny - - - - org.apache.maven.plugins - maven-antrun-plugin - - - generate-sources - - - - - - - - run - - - - - - org.codehaus.mojo - exec-maven-plugin - - - generate schema types - generate-sources - - exec - - - - - xjc - - -enableIntrospection - -p - org.hibernate.validator.internal.xml.binding - -extension - -target - 2.1 - -d - target/generated-sources/jaxb - src/main/xsd/validation-configuration-2.0.xsd - src/main/xsd/validation-mapping-2.0.xsd - -b - src/main/xjb/binding-customization.xjb - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-source - generate-sources - - add-source - - - - target/generated-sources/jaxb - - - - - - - - sigtest - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-tck-bv-api-signature-file - generate-test-sources - - unpack - - - - - org.hibernate.beanvalidation.tck - beanvalidation-tck-tests - ${tck.version} - jar - true - - - - **/*.sig - ${project.build.directory}/api-signature - - - - - - org.netbeans.tools - sigtest-maven-plugin - - - - check - - - - - javax.validation,javax.validation.bootstrap,javax.validation.constraints, - javax.validation.constraintvalidation,javax.validation.executable,javax.validation.groups, - javax.validation.metadata,javax.validation.spi,javax.validation.valueextraction - - ${project.build.directory}/api-signature/validation-api-java8.sig - - - - + jdk11+ + + [11,) + + + + org.openjfx + javafx-base + ${version.org.openjfx} + provided + + diff --git a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java index 45773d3949..8d2e6d1ed5 100644 --- a/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java @@ -20,6 +20,7 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.scripting.ScriptEvaluator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -311,4 +312,16 @@ public interface HibernateValidatorConfiguration extends Configuration { public SafeHtmlDef() { diff --git a/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java b/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java index fa08e2fa81..86d7848cec 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/SafeHtml.java @@ -34,12 +34,14 @@ * {@code body} tags to the used whitelist as required. * * @author George Gastaldi + * @deprecated {@code @SafeHtml} support will be removed in a future version */ @Documented @Constraint(validatedBy = { }) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Repeatable(List.class) +@Deprecated public @interface SafeHtml { String message() default "{org.hibernate.validator.constraints.SafeHtml.message}"; diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java index 323ee74c7a..a8f40b8944 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java @@ -130,11 +130,14 @@ public ContainerElementConstraintMappingContext containerElement(ContainerElemen ); } - ContainerElementConstraintMappingContextImpl containerElementContext = new ContainerElementConstraintMappingContextImpl( - typeContext, parent, location, index - ); - - containerElementContexts.put( index, containerElementContext ); + // As we already checked that the specific path was not yet configured we should not worry about returning the same context here, + // as it means that there are some nested indexes which make a difference, And at the end a new context will be returned by call + // to containerElementContext#nestedContainerElement(). + ContainerElementConstraintMappingContextImpl containerElementContext = containerElementContexts.get( index ); + if ( containerElementContext == null ) { + containerElementContext = new ContainerElementConstraintMappingContextImpl( typeContext, parent, location, index ); + containerElementContexts.put( index, containerElementContext ); + } if ( nestedIndexes.length > 0 ) { return containerElementContext.nestedContainerElement( nestedIndexes ); 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 6478ac3611..b49a0b8213 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 @@ -47,7 +47,7 @@ public ConstraintDefinitionContext includeExistingValidators(boolean includeE @Override public ConstraintDefinitionContext validatedBy(Class> validator) { - validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validator ) ); + validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validator, this.annotationType ) ); return this; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java index ca7d0f7514..cf33e40d86 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java @@ -168,14 +168,16 @@ ContainerElementConstraintMappingContext nestedContainerElement(int[] nestedInde throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( configuredType ); } - ContainerElementConstraintMappingContextImpl nestedContext = new ContainerElementConstraintMappingContextImpl( - typeContext, - parentContainerElementTarget, - ConstraintLocation.forTypeArgument( parentLocation, typeParameter, getContainerElementType() ), - nestedIndexes[0] - ); - - nestedContainerElementContexts.put( nestedIndexes[0], nestedContext ); + ContainerElementConstraintMappingContextImpl nestedContext = nestedContainerElementContexts.get( nestedIndexes[0] ); + if ( nestedContext == null ) { + nestedContext = new ContainerElementConstraintMappingContextImpl( + typeContext, + parentContainerElementTarget, + ConstraintLocation.forTypeArgument( parentLocation, typeParameter, getContainerElementType() ), + nestedIndexes[0] + ); + nestedContainerElementContexts.put( nestedIndexes[0], nestedContext ); + } if ( nestedIndexes.length > 1 ) { return nestedContext.nestedContainerElement( Arrays.copyOfRange( nestedIndexes, 1, nestedIndexes.length ) ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java index 82a5403b26..63275fc6aa 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/TypeConstraintMappingContextImpl.java @@ -18,6 +18,7 @@ import java.security.PrivilegedAction; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Set; import org.hibernate.validator.cfg.ConstraintDef; @@ -144,7 +145,7 @@ public MethodConstraintMappingContext method(String name, Class... parameterT Method method = run( GetDeclaredMethod.action( beanClass, name, parameterTypes ) ); if ( method == null || method.getDeclaringClass() != beanClass ) { - throw LOG.getBeanDoesNotContainMethodException( beanClass, name, Arrays.asList( parameterTypes ) ); + throw LOG.getBeanDoesNotContainMethodException( beanClass, name, parameterTypes ); } if ( configuredMembers.contains( method ) ) { @@ -168,7 +169,7 @@ public ConstructorConstraintMappingContext constructor(Class... parameterType if ( constructor == null || constructor.getDeclaringClass() != beanClass ) { throw LOG.getBeanDoesNotContainConstructorException( beanClass, - Arrays.asList( parameterTypes ) + parameterTypes ); } @@ -269,7 +270,7 @@ private Member getMember(Class clazz, String property, ElementType elementTyp member = run( GetDeclaredField.action( clazz, property ) ); } else { - String methodName = property.substring( 0, 1 ).toUpperCase() + property.substring( 1 ); + String methodName = property.substring( 0, 1 ).toUpperCase( Locale.ROOT ) + property.substring( 1 ); for ( String prefix : ReflectionHelper.PROPERTY_ACCESSOR_PREFIXES ) { member = run( GetMethod.action( clazz, prefix + methodName ) ); if ( member != null ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/AbstractEmailValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/AbstractEmailValidator.java index ec183977b9..2e349e1de9 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/AbstractEmailValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/AbstractEmailValidator.java @@ -37,13 +37,13 @@ public class AbstractEmailValidator implements ConstraintV private static final int MAX_LOCAL_PART_LENGTH = 64; private static final String LOCAL_PART_ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~\u0080-\uFFFF-]"; - private static final String LOCAL_PART_INSIDE_QUOTES_ATOM = "([a-z0-9!#$%&'*.(),<>\\[\\]:; @+/=?^_`{|}~\u0080-\uFFFF-]|\\\\\\\\|\\\\\\\")"; + private static final String LOCAL_PART_INSIDE_QUOTES_ATOM = "(?:[a-z0-9!#$%&'*.(),<>\\[\\]:; @+/=?^_`{|}~\u0080-\uFFFF-]|\\\\\\\\|\\\\\\\")"; /** * Regular expression for the local part of an email address (everything before '@') */ private static final Pattern LOCAL_PART_PATTERN = Pattern.compile( - "(" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" + - "(\\." + "(" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" + ")*", CASE_INSENSITIVE + "(?:" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" + + "(?:\\." + "(?:" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" + ")*", CASE_INSENSITIVE ); @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java index 6d37014fae..63faf7e075 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.internal.constraintvalidators.bv.number.bound; +import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper; + /** * Check that the number being validated is less than or equal to the maximum * value specified. @@ -16,6 +18,6 @@ public class MaxValidatorForNumber extends AbstractMaxValidator { @Override protected int compare(Number number) { - return NumberComparatorHelper.compare( number, maxValue ); + return NumberComparatorHelper.compare( number, maxValue, InfinityNumberComparatorHelper.GREATER_THAN ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java index 7b2974df76..cd56e942af 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.internal.constraintvalidators.bv.number.bound; +import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper; + /** * Check that the number being validated is greater than or equal to the minimum * value specified. @@ -16,6 +18,6 @@ public class MinValidatorForNumber extends AbstractMinValidator { @Override protected int compare(Number number) { - return NumberComparatorHelper.compare( number, minValue ); + return NumberComparatorHelper.compare( number, minValue, InfinityNumberComparatorHelper.LESS_THAN ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java index c70256ff95..65ad5a0c1d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java @@ -32,7 +32,24 @@ public static int compare(Long number, long value) { return number.compareTo( value ); } - public static int compare(Number number, long value) { + public static int compare(Number number, long value, OptionalInt treatNanAs) { + // In case of comparing numbers before we compare them as two longs: + // 1. We need to check for floating point number as it should be treated differently in such case: + if ( number instanceof Double ) { + return compare( (Double) number, value, treatNanAs ); + } + if ( number instanceof Float ) { + return compare( (Float) number, value, treatNanAs ); + } + + // 2. We need to check for big numbers so that we don't lose data when converting them to long: + if ( number instanceof BigDecimal ) { + return compare( (BigDecimal) number, value ); + } + if ( number instanceof BigInteger ) { + return compare( (BigInteger) number, value ); + } + return Long.compare( number.longValue(), value ); } @@ -41,7 +58,7 @@ public static int compare(Double number, long value, OptionalInt treatNanAs) { if ( infinity.isPresent() ) { return infinity.getAsInt(); } - return Long.compare( number.longValue(), value ); + return Double.compare( number, value ); } public static int compare(Float number, long value, OptionalInt treatNanAs) { @@ -49,6 +66,6 @@ public static int compare(Float number, long value, OptionalInt treatNanAs) { if ( infinity.isPresent() ) { return infinity.getAsInt(); } - return Long.compare( number.longValue(), value ); + return Float.compare( number, value ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/future/FutureValidatorForDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/future/FutureValidatorForDate.java index 7a56506edb..0f8537eadb 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/future/FutureValidatorForDate.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/future/FutureValidatorForDate.java @@ -20,7 +20,8 @@ public class FutureValidatorForDate extends AbstractFutureInstantBasedValidator< @Override protected Instant getInstant(Date value) { - return value.toInstant(); + // we don't use Date.toInstant() as it's not supported by java.sql.Date + return Instant.ofEpochMilli( value.getTime() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/futureorpresent/FutureOrPresentValidatorForDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/futureorpresent/FutureOrPresentValidatorForDate.java index b682aa991b..29d47357c1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/futureorpresent/FutureOrPresentValidatorForDate.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/futureorpresent/FutureOrPresentValidatorForDate.java @@ -20,7 +20,8 @@ public class FutureOrPresentValidatorForDate extends AbstractFutureOrPresentInst @Override protected Instant getInstant(Date value) { - return value.toInstant(); + // we don't use Date.toInstant() as it's not supported by java.sql.Date + return Instant.ofEpochMilli( value.getTime() ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/past/PastValidatorForDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/past/PastValidatorForDate.java index 43066f6341..c97662b83b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/past/PastValidatorForDate.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/time/past/PastValidatorForDate.java @@ -20,7 +20,8 @@ public class PastValidatorForDate extends AbstractPastInstantBasedValidator { - private Whitelist whitelist; + private Safelist safelist; private String baseURI; @@ -37,35 +39,35 @@ public class SafeHtmlValidator implements ConstraintValidator 0 ) { - whitelist.addAttributes( tag.name(), tag.attributes() ); + safelist.addAttributes( tag.name(), tag.attributes() ); } for ( SafeHtml.Attribute attribute : tag.attributesWithProtocols() ) { - whitelist.addAttributes( tag.name(), attribute.name() ); + safelist.addAttributes( tag.name(), attribute.name() ); if ( attribute.protocols().length > 0 ) { - whitelist.addProtocols( tag.name(), attribute.name(), attribute.protocols() ); + safelist.addProtocols( tag.name(), attribute.name(), attribute.protocols() ); } } } @@ -77,7 +79,7 @@ public boolean isValid(CharSequence value, ConstraintValidatorContext context) { return true; } - return new Cleaner( whitelist ).isValid( getFragmentAsDocument( value ) ); + return new Cleaner( safelist ).isValid( getFragmentAsDocument( value ) ); } /** @@ -91,9 +93,9 @@ private Document getFragmentAsDocument(CharSequence value) { Document document = Document.createShell( baseURI ); // add the fragment's nodes to the body of resulting document - Iterator nodes = fragment.children().iterator(); - while ( nodes.hasNext() ) { - document.body().appendChild( nodes.next() ); + List childNodes = fragment.childNodes(); + for ( Node node : childNodes ) { + document.body().appendChild( node.clone() ); } return document; diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java index 4b33031e2c..326e558e8c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PESELValidator.java @@ -30,7 +30,7 @@ public void initialize(PESEL constraintAnnotation) { 0, Integer.MAX_VALUE, -1, - true + false ); } @@ -38,6 +38,11 @@ public void initialize(PESEL constraintAnnotation) { public boolean isCheckDigitValid(List digits, char checkDigit) { Collections.reverse( digits ); + // if the length of the number is incorrect we can return fast + if ( digits.size() != WEIGHTS_PESEL.length ) { + return false; + } + int modResult = ModUtil.calculateModXCheckWithWeights( digits, 10, Integer.MAX_VALUE, WEIGHTS_PESEL ); switch ( modResult ) { case 10: diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java index f41cdb780a..1ab9289d7e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/pl/PolishNumberValidator.java @@ -27,8 +27,15 @@ public abstract class PolishNumberValidator extends ModChe public boolean isCheckDigitValid(List digits, char checkDigit) { Collections.reverse( digits ); + int[] weights = getWeights( digits ); + + // if the length of the number is incorrect we can return fast + if ( weights.length != digits.size() ) { + return false; + } + // as we need sum % 11 rather than 11 - (sum % 11) returned by Mod11 algorithm: - int modResult = 11 - ModUtil.calculateModXCheckWithWeights( digits, 11, Integer.MAX_VALUE, getWeights( digits ) ); + int modResult = 11 - ModUtil.calculateModXCheckWithWeights( digits, 11, Integer.MAX_VALUE, weights ); switch ( modResult ) { case 10: case 11: 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 5fe13d90f0..74661b286d 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 @@ -49,9 +49,10 @@ import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; import org.hibernate.validator.internal.util.privilegedactions.GetInstancesFromServiceLoader; import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; -import org.hibernate.validator.internal.xml.ValidationBootstrapParameters; -import org.hibernate.validator.internal.xml.ValidationXmlParser; +import org.hibernate.validator.internal.xml.config.ValidationBootstrapParameters; +import org.hibernate.validator.internal.xml.config.ValidationXmlParser; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -103,6 +104,7 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi private ScriptEvaluatorFactory scriptEvaluatorFactory; private Duration temporalValidationTolerance; private Object constraintValidatorPayload; + private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer; public ConfigurationImpl(BootstrapState state) { this(); @@ -328,9 +330,29 @@ public HibernateValidatorConfiguration externalClassLoader(ClassLoader externalC Contracts.assertNotNull( externalClassLoader, MESSAGES.parameterMustNotBeNull( "externalClassLoader" ) ); this.externalClassLoader = externalClassLoader; + // we need to reset the messageInterpolator field as it might vary depending on the class loader + this.messageInterpolator = null; + return this; } + @Override + public HibernateValidatorConfiguration beanMetaDataClassNormalizer( + BeanMetaDataClassNormalizer beanMetaDataClassNormalizer) { + if ( LOG.isDebugEnabled() ) { + if ( beanMetaDataClassNormalizer != null ) { + LOG.debug( "Setting custom BeanMetaDataClassNormalizer of type " + beanMetaDataClassNormalizer.getClass() + .getName() ); + } + } + this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer; + return this; + } + + public BeanMetaDataClassNormalizer getBeanMetaDataClassNormalizer() { + return beanMetaDataClassNormalizer; + } + @Override public final ValidatorFactory buildValidatorFactory() { loadValueExtractorsFromServiceLoader(); @@ -387,15 +409,13 @@ public final boolean isIgnoreXmlConfiguration() { @Override public final MessageInterpolator getMessageInterpolator() { + // apply explicitly given MI, otherwise use default one + MessageInterpolator selectedInterpolator = validationBootstrapParameters.getMessageInterpolator(); + if ( selectedInterpolator != null ) { + return selectedInterpolator; + } if ( messageInterpolator == null ) { - // apply explicitly given MI, otherwise use default one - MessageInterpolator interpolator = validationBootstrapParameters.getMessageInterpolator(); - if ( interpolator != null ) { - messageInterpolator = interpolator; - } - else { - messageInterpolator = getDefaultMessageInterpolatorConfiguredWithClassLoader(); - } + messageInterpolator = getDefaultMessageInterpolatorConfiguredWithClassLoader(); } return messageInterpolator; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java index 2c951be52b..e5e1aba518 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/MessageInterpolatorContext.java @@ -11,6 +11,7 @@ import java.lang.invoke.MethodHandles; import java.util.Map; +import javax.validation.Path; import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.internal.util.logging.Log; @@ -33,21 +34,27 @@ public class MessageInterpolatorContext implements HibernateMessageInterpolatorC private final ConstraintDescriptor constraintDescriptor; private final Object validatedValue; private final Class rootBeanType; + private final Path propertyPath; @Immutable private final Map messageParameters; @Immutable private final Map expressionVariables; + private final boolean expressionLanguageEnabled; public MessageInterpolatorContext(ConstraintDescriptor constraintDescriptor, - Object validatedValue, - Class rootBeanType, - Map messageParameters, - Map expressionVariables) { + Object validatedValue, + Class rootBeanType, + Path propertyPath, + Map messageParameters, + Map expressionVariables, + boolean expressionLanguageEnabled) { this.constraintDescriptor = constraintDescriptor; this.validatedValue = validatedValue; this.rootBeanType = rootBeanType; + this.propertyPath = propertyPath; this.messageParameters = toImmutableMap( messageParameters ); this.expressionVariables = toImmutableMap( expressionVariables ); + this.expressionLanguageEnabled = expressionLanguageEnabled; } @Override @@ -70,11 +77,20 @@ public Map getMessageParameters() { return messageParameters; } + public boolean isExpressionLanguageEnabled() { + return expressionLanguageEnabled; + } + @Override public Map getExpressionVariables() { return expressionVariables; } + @Override + public Path getPropertyPath() { + return propertyPath; + } + @Override public T unwrap(Class type) { //allow unwrapping into public super types @@ -122,8 +138,11 @@ public String toString() { sb.append( "MessageInterpolatorContext" ); sb.append( "{constraintDescriptor=" ).append( constraintDescriptor ); sb.append( ", validatedValue=" ).append( validatedValue ); + sb.append( ", rootBeanType=" ).append( rootBeanType.getName() ); + sb.append( ", propertyPath=" ).append( propertyPath ); sb.append( ", messageParameters=" ).append( messageParameters ); sb.append( ", expressionVariables=" ).append( expressionVariables ); + sb.append( ", expressionLanguageEnabled=" ).append( expressionLanguageEnabled ); sb.append( '}' ); return sb.toString(); } 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 a4ebb12d7d..2d4b4a4444 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 @@ -60,7 +60,7 @@ public void createConstraintMappings(ConstraintMappingBuilder builder) { ConstraintValidator.class ) ); for ( ConstraintValidator constraintValidator : discoveredConstraintValidators ) { - Class constraintValidatorClass = constraintValidator.getClass(); + Class constraintValidatorClass = constraintValidator.getClass(); Class annotationType = determineAnnotationType( constraintValidatorClass ); List> validators = customValidators.get( annotationType ); @@ -90,7 +90,8 @@ private void registerConstraintDefinition(ConstraintMappi } } - private Class determineAnnotationType(Class constraintValidatorClass) { + @SuppressWarnings("rawtypes") + private Class determineAnnotationType(Class constraintValidatorClass) { ResolvedType resolvedType = typeResolutionHelper.getTypeResolver() .resolve( constraintValidatorClass ); 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 640217e794..e5468d9eb8 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 @@ -37,7 +37,6 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; import org.hibernate.validator.internal.engine.path.PathImpl; -import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.core.MetaConstraint; @@ -196,7 +195,6 @@ private ValidationContext(ValidationOperation validationOperation, } public static ValidationContextBuilder getValidationContextBuilder( - BeanMetaDataManager beanMetaDataManager, ConstraintValidatorManager constraintValidatorManager, ConstraintValidatorFactory constraintValidatorFactory, ValidatorScopedContext validatorScopedContext, @@ -204,7 +202,6 @@ public static ValidationContextBuilder getValidationContextBuilder( HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { return new ValidationContextBuilder( - beanMetaDataManager, constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, @@ -321,8 +318,10 @@ public ConstraintViolation createConstraintViolation(ValueContext local String messageTemplate = constraintViolationCreationContext.getMessage(); String interpolatedMessage = interpolate( messageTemplate, + constraintViolationCreationContext.isExpressionLanguageEnabled(), localContext.getCurrentValidatedValue(), descriptor, + constraintViolationCreationContext.getPath(), constraintViolationCreationContext.getMessageParameters(), constraintViolationCreationContext.getExpressionVariables() ); @@ -409,6 +408,10 @@ public void setValidatedProperty(String validatedProperty) { this.validatedProperty = validatedProperty; } + public boolean isExpressionLanguageEnabled() { + return validatorScopedContext.isExpressionLanguageEnabled(); + } + @Override public String toString() { final StringBuilder sb = new StringBuilder(); @@ -452,16 +455,20 @@ private static boolean buildDisableAlreadyValidatedBeanTracking(ValidationOperat } private String interpolate(String messageTemplate, + boolean expressionLanguageEnabled, Object validatedValue, ConstraintDescriptor descriptor, + Path path, Map messageParameters, Map expressionVariables) { MessageInterpolatorContext context = new MessageInterpolatorContext( descriptor, validatedValue, getRootBeanClass(), + path, messageParameters, - expressionVariables + expressionVariables, + expressionLanguageEnabled ); try { @@ -529,7 +536,6 @@ private void markCurrentBeanAsProcessedForCurrentGroup(Object bean, Class gro * @author Gunnar Morling */ public static class ValidationContextBuilder { - private final BeanMetaDataManager beanMetaDataManager; private final ConstraintValidatorManager constraintValidatorManager; private final ConstraintValidatorFactory constraintValidatorFactory; private final TraversableResolver traversableResolver; @@ -537,13 +543,11 @@ public static class ValidationContextBuilder { private final ValidatorScopedContext validatorScopedContext; private ValidationContextBuilder( - BeanMetaDataManager beanMetaDataManager, ConstraintValidatorManager constraintValidatorManager, ConstraintValidatorFactory constraintValidatorFactory, ValidatorScopedContext validatorScopedContext, TraversableResolver traversableResolver, HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) { - this.beanMetaDataManager = beanMetaDataManager; this.constraintValidatorManager = constraintValidatorManager; this.constraintValidatorFactory = constraintValidatorFactory; this.traversableResolver = traversableResolver; @@ -551,9 +555,7 @@ private ValidationContextBuilder( this.validatorScopedContext = validatorScopedContext; } - public ValidationContext forValidate(T rootBean) { - @SuppressWarnings("unchecked") - Class rootBeanClass = (Class) rootBean.getClass(); + public ValidationContext forValidate(BeanMetaData rootBeanMetaData, T rootBean) { return new ValidationContext<>( ValidationOperation.BEAN_VALIDATION, constraintValidatorManager, @@ -562,8 +564,8 @@ public ValidationContext forValidate(T rootBean) { traversableResolver, constraintValidatorInitializationContext, rootBean, - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), + rootBeanMetaData.getBeanClass(), + rootBeanMetaData, null, //executable null, //executable parameters null, //executable return value @@ -571,9 +573,7 @@ public ValidationContext forValidate(T rootBean) { ); } - public ValidationContext forValidateProperty(T rootBean) { - @SuppressWarnings("unchecked") - Class rootBeanClass = (Class) rootBean.getClass(); + public ValidationContext forValidateProperty(BeanMetaData rootBeanMetaData, T rootBean) { return new ValidationContext<>( ValidationOperation.PROPERTY_VALIDATION, constraintValidatorManager, @@ -582,8 +582,8 @@ public ValidationContext forValidateProperty(T rootBean) { traversableResolver, constraintValidatorInitializationContext, rootBean, - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), + rootBeanMetaData.getBeanClass(), + rootBeanMetaData, null, //executable null, //executable parameters null, //executable return value @@ -591,7 +591,7 @@ public ValidationContext forValidateProperty(T rootBean) { ); } - public ValidationContext forValidateValue(Class rootBeanClass) { + public ValidationContext forValidateValue(BeanMetaData rootBeanMetaData) { return new ValidationContext<>( ValidationOperation.VALUE_VALIDATION, constraintValidatorManager, @@ -600,8 +600,8 @@ public ValidationContext forValidateValue(Class rootBeanClass) { traversableResolver, constraintValidatorInitializationContext, null, //root bean - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), + rootBeanMetaData.getBeanClass(), + rootBeanMetaData, null, //executable null, //executable parameters null, //executable return value @@ -611,13 +611,10 @@ public ValidationContext forValidateValue(Class rootBeanClass) { public ValidationContext forValidateParameters( ExecutableParameterNameProvider parameterNameProvider, + BeanMetaData rootBeanMetaData, T rootBean, Executable executable, Object[] executableParameters) { - @SuppressWarnings("unchecked") - Class rootBeanClass = rootBean != null ? (Class) rootBean.getClass() : (Class) executable.getDeclaringClass(); - BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); - return new ValidationContext<>( ValidationOperation.PARAMETER_VALIDATION, constraintValidatorManager, @@ -626,7 +623,7 @@ public ValidationContext forValidateParameters( traversableResolver, constraintValidatorInitializationContext, rootBean, - rootBeanClass, + rootBeanMetaData.getBeanClass(), rootBeanMetaData, executable, executableParameters, @@ -636,12 +633,10 @@ public ValidationContext forValidateParameters( } public ValidationContext forValidateReturnValue( + BeanMetaData rootBeanMetaData, T rootBean, Executable executable, Object executableReturnValue) { - @SuppressWarnings("unchecked") - Class rootBeanClass = rootBean != null ? (Class) rootBean.getClass() : (Class) executable.getDeclaringClass(); - BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); return new ValidationContext<>( ValidationOperation.RETURN_VALUE_VALIDATION, constraintValidatorManager, @@ -650,8 +645,8 @@ public ValidationContext forValidateReturnValue( traversableResolver, constraintValidatorInitializationContext, rootBean, - rootBeanClass, - beanMetaDataManager.getBeanMetaData( rootBeanClass ), + rootBeanMetaData.getBeanClass(), + rootBeanMetaData, executable, null, //executable parameters executableReturnValue, @@ -798,6 +793,11 @@ static class ValidatorScopedContext { */ private final boolean traversableResolverResultCacheEnabled; + /** + * Hibernate Validator specific flag to enable Expression Language message interpolation. + */ + private final boolean expressionLanguageEnabled; + /** * Hibernate Validator specific payload passed to the constraint validators. */ @@ -811,6 +811,7 @@ static class ValidatorScopedContext { this.scriptEvaluatorFactory = validatorFactoryScopedContext.getScriptEvaluatorFactory(); this.failFast = validatorFactoryScopedContext.isFailFast(); this.traversableResolverResultCacheEnabled = validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled(); + this.expressionLanguageEnabled = validatorFactoryScopedContext.isExpressionLanguageEnabled(); this.constraintValidatorPayload = validatorFactoryScopedContext.getConstraintValidatorPayload(); } @@ -842,6 +843,10 @@ public boolean isTraversableResolverResultCacheEnabled() { return this.traversableResolverResultCacheEnabled; } + public boolean isExpressionLanguageEnabled() { + return expressionLanguageEnabled; + } + public Object getConstraintValidatorPayload() { return this.constraintValidatorPayload; } 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 a50baef0fb..5de96e33bf 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 @@ -43,6 +43,7 @@ import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider; @@ -55,10 +56,12 @@ import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; +import org.hibernate.validator.internal.util.privilegedactions.GetEnvOrSystemVariableValue; import org.hibernate.validator.internal.util.privilegedactions.LoadClass; import org.hibernate.validator.internal.util.privilegedactions.NewInstance; import org.hibernate.validator.internal.util.stereotypes.Immutable; import org.hibernate.validator.internal.util.stereotypes.ThreadSafe; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -78,6 +81,9 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + private static final String EXPRESSION_LANGUAGE_ENABLED_ENV = "ORG_HIBERNATE_VALIDATOR_EXPRESSION_LANGUAGE_ENABLED"; + private static final String EXPRESSION_LANGUAGE_ENABLED_SYSTEM = "org.hibernate.validator.expressionLanguageEnabled"; + /** * Context containing all {@link ValidatorFactory} level helpers and configuration properties. */ @@ -132,6 +138,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private final ValueExtractorManager valueExtractorManager; + private final BeanMetaDataClassNormalizer beanMetadataClassNormalizer; + private final ValidationOrderGenerator validationOrderGenerator; public ValidatorFactoryImpl(ConfigurationState configurationState) { @@ -188,6 +196,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { getScriptEvaluatorFactory( configurationState, properties, externalClassLoader ), getFailFast( hibernateSpecificConfig, properties ), getTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), + getExpressionLanguageEnabled(), getConstraintValidatorPayload( hibernateSpecificConfig ) ); @@ -198,6 +207,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { this.validationOrderGenerator = new ValidationOrderGenerator(); + this.beanMetadataClassNormalizer = ( hibernateSpecificConfig != null && hibernateSpecificConfig.getBeanMetaDataClassNormalizer() != null ) + ? hibernateSpecificConfig.getBeanMetaDataClassNormalizer() + : new DefaultBeanMetaDataClassNormalizer(); + if ( LOG.isDebugEnabled() ) { logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext ); } @@ -347,6 +360,7 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, typeResolutionHelper, validatorFactoryScopedContext.getParameterNameProvider(), valueExtractorManager, + beanMetadataClassNormalizer, validationOrderGenerator, buildDataProviders(), methodValidationConfiguration @@ -467,6 +481,15 @@ private static boolean getTraversableResolverResultCacheEnabled(ConfigurationImp ); } + private static boolean getExpressionLanguageEnabled() { + String enable = run( GetEnvOrSystemVariableValue.action( EXPRESSION_LANGUAGE_ENABLED_ENV, EXPRESSION_LANGUAGE_ENABLED_SYSTEM ) ); + if ( enable == null ) { + LOG.expressionLanguageFeaturesNoExplicitSetting( EXPRESSION_LANGUAGE_ENABLED_ENV, EXPRESSION_LANGUAGE_ENABLED_SYSTEM ); + enable = "false"; + } + return "true".equalsIgnoreCase( enable ); + } + private static boolean getFailFast(ConfigurationImpl configuration, Map properties) { // check whether fail fast is programmatically enabled boolean tmpFailFast = configuration != null ? configuration.getFailFast() : false; @@ -707,6 +730,11 @@ static class ValidatorFactoryScopedContext { */ private final boolean traversableResolverResultCacheEnabled; + /** + * Hibernate Validator specific flag to enable Expression Language message interpolation. + */ + private final boolean expressionLanguageEnabled; + /** * The constraint validator payload. */ @@ -725,9 +753,10 @@ private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, ScriptEvaluatorFactory scriptEvaluatorFactory, boolean failFast, boolean traversableResolverResultCacheEnabled, + boolean expressionLanguageEnabled, Object constraintValidatorPayload) { this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast, - traversableResolverResultCacheEnabled, constraintValidatorPayload, + traversableResolverResultCacheEnabled, expressionLanguageEnabled, constraintValidatorPayload, new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider, temporalValidationTolerance ) ); } @@ -740,6 +769,7 @@ private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, ScriptEvaluatorFactory scriptEvaluatorFactory, boolean failFast, boolean traversableResolverResultCacheEnabled, + boolean expressionLanguageEnabled, Object constraintValidatorPayload, HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext) { this.messageInterpolator = messageInterpolator; @@ -750,6 +780,7 @@ private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, this.scriptEvaluatorFactory = scriptEvaluatorFactory; this.failFast = failFast; this.traversableResolverResultCacheEnabled = traversableResolverResultCacheEnabled; + this.expressionLanguageEnabled = expressionLanguageEnabled; this.constraintValidatorPayload = constraintValidatorPayload; this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; } @@ -786,6 +817,10 @@ public boolean isTraversableResolverResultCacheEnabled() { return this.traversableResolverResultCacheEnabled; } + public boolean isExpressionLanguageEnabled() { + return expressionLanguageEnabled; + } + public Object getConstraintValidatorPayload() { return this.constraintValidatorPayload; } @@ -905,6 +940,7 @@ public ValidatorFactoryScopedContext build() { scriptEvaluatorFactory, failFast, traversableResolverResultCacheEnabled, + defaultContext.expressionLanguageEnabled, constraintValidatorPayload, HibernateConstraintValidatorInitializationContextImpl.of( constraintValidatorInitializationContext, 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 7b9319c61e..65c8640c4f 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 @@ -152,12 +152,16 @@ public final Set> validate(T object, Class... grou Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() ); sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidate( object ); + @SuppressWarnings("unchecked") + Class rootBeanClass = (Class) object.getClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { + if ( !rootBeanMetaData.hasConstraints() ) { return Collections.emptySet(); } + ValidationContext validationContext = getValidationContextBuilder().forValidate( rootBeanMetaData, object ); + ValidationOrder validationOrder = determineGroupValidationOrder( groups ); ValueContext valueContext = ValueContext.getLocalExecutionContext( validatorScopedContext.getParameterNameProvider(), @@ -175,13 +179,16 @@ public final Set> validateProperty(T object, String p sanityCheckPropertyPath( propertyName ); sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidateProperty( object ); + @SuppressWarnings("unchecked") + Class rootBeanClass = (Class) object.getClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { + if ( !rootBeanMetaData.hasConstraints() ) { return Collections.emptySet(); } PathImpl propertyPath = PathImpl.createPathFromString( propertyName ); + ValidationContext validationContext = getValidationContextBuilder().forValidateProperty( rootBeanMetaData, object ); ValueContext valueContext = getValueContextForPropertyValidation( validationContext, propertyPath ); if ( valueContext.getCurrentBean() == null ) { @@ -199,12 +206,14 @@ public final Set> validateValue(Class beanType, St sanityCheckPropertyPath( propertyName ); sanityCheckGroups( groups ); - ValidationContext validationContext = getValidationContextBuilder().forValidateValue( beanType ); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( beanType ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { + if ( !rootBeanMetaData.hasConstraints() ) { return Collections.emptySet(); } + ValidationContext validationContext = getValidationContextBuilder().forValidateValue( rootBeanMetaData ); + ValidationOrder validationOrder = determineGroupValidationOrder( groups ); return validateValueInContext( @@ -251,17 +260,22 @@ public Set> validateReturnValue(T object, Method meth private Set> validateParameters(T object, Executable executable, Object[] parameterValues, Class... groups) { sanityCheckGroups( groups ); + @SuppressWarnings("unchecked") + Class rootBeanClass = object != null ? (Class) object.getClass() : (Class) executable.getDeclaringClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); + + if ( !rootBeanMetaData.hasConstraints() ) { + return Collections.emptySet(); + } + ValidationContext validationContext = getValidationContextBuilder().forValidateParameters( validatorScopedContext.getParameterNameProvider(), + rootBeanMetaData, object, executable, parameterValues ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { - return Collections.emptySet(); - } - ValidationOrder validationOrder = determineGroupValidationOrder( groups ); validateParametersInContext( validationContext, parameterValues, validationOrder ); @@ -272,16 +286,21 @@ private Set> validateParameters(T object, Executable private Set> validateReturnValue(T object, Executable executable, Object returnValue, Class... groups) { sanityCheckGroups( groups ); + @SuppressWarnings("unchecked") + Class rootBeanClass = object != null ? (Class) object.getClass() : (Class) executable.getDeclaringClass(); + BeanMetaData rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass ); + + if ( !rootBeanMetaData.hasConstraints() ) { + return Collections.emptySet(); + } + ValidationContext validationContext = getValidationContextBuilder().forValidateReturnValue( + rootBeanMetaData, object, executable, returnValue ); - if ( !validationContext.getRootBeanMetaData().hasConstraints() ) { - return Collections.emptySet(); - } - ValidationOrder validationOrder = determineGroupValidationOrder( groups ); validateReturnValueInContext( validationContext, object, returnValue, validationOrder ); @@ -313,13 +332,11 @@ public ExecutableValidator forExecutables() { private ValidationContextBuilder getValidationContextBuilder() { return ValidationContext.getValidationContextBuilder( - beanMetaDataManager, constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, TraversableResolvers.wrapWithCachingForSingleValidation( traversableResolver, validatorScopedContext.isTraversableResolverResultCacheEnabled() ), constraintValidatorInitializationContext - ); } @@ -451,8 +468,11 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat for ( Group defaultSequenceMember : groupOfGroups ) { validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, - metaConstraints, defaultSequenceMember ); + metaConstraints, defaultSequenceMember ) && validationSuccessful; } + + validationContext.markCurrentBeanAsProcessed( valueContext ); + if ( !validationSuccessful ) { break; } @@ -464,10 +484,9 @@ private void validateConstraintsForDefaultGroup(ValidationContext validat Set> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints(); validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints, Group.DEFAULT_GROUP ); + validationContext.markCurrentBeanAsProcessed( valueContext ); } - validationContext.markCurrentBeanAsProcessed( valueContext ); - // all constraints in the hierarchy has been validated, stop validation. if ( defaultGroupSequenceIsRedefined ) { break; @@ -587,14 +606,16 @@ private void validateCascadedConstraints(ValidationContext validationContext, private void validateCascadedAnnotatedObjectForCurrentGroup(Object value, ValidationContext validationContext, ValueContext valueContext, CascadingMetaData cascadingMetaData) { - if ( validationContext.isBeanAlreadyValidated( value, valueContext.getCurrentGroup(), valueContext.getPropertyPath() ) || + // We need to convert the group before checking if the bean was processed or not + // as group defines the processed status. + Class originalGroup = valueContext.getCurrentGroup(); + Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); + + if ( validationContext.isBeanAlreadyValidated( value, currentGroup, valueContext.getPropertyPath() ) || shouldFailFast( validationContext ) ) { return; } - Class originalGroup = valueContext.getCurrentGroup(); - Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); - // expand the group only if was created by group conversion; // otherwise we're looping through the right validation order // already and need only to pass the current element @@ -664,15 +685,17 @@ public void keyedValue(String nodeName, Object key, Object value) { } private void doValidate(Object value, String nodeName) { + // We need to convert the group before checking if the bean was processed or not + // as group defines the processed status. + Class originalGroup = valueContext.getCurrentGroup(); + Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); + if ( value == null || - validationContext.isBeanAlreadyValidated( value, valueContext.getCurrentGroup(), valueContext.getPropertyPath() ) || + validationContext.isBeanAlreadyValidated( value, currentGroup, valueContext.getPropertyPath() ) || shouldFailFast( validationContext ) ) { return; } - Class originalGroup = valueContext.getCurrentGroup(); - Class currentGroup = cascadingMetaData.convertGroup( originalGroup ); - // expand the group only if was created by group conversion; // otherwise we're looping through the right validation order // already and need only to pass the current element diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java index ab1bc49ece..fd5f8c96ce 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ClassBasedValidatorDescriptor.java @@ -25,28 +25,38 @@ * Represents an implementation of {@link ConstraintValidator}. * * @author Gunnar Morling + * @author Guillaume Smet */ class ClassBasedValidatorDescriptor implements ConstraintValidatorDescriptor { - private static final long serialVersionUID = -8207687559460098548L; private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); private final Class> validatorClass; private final Type validatedType; private final EnumSet validationTargets; - public ClassBasedValidatorDescriptor(Class> validatorClass) { + private ClassBasedValidatorDescriptor(Class> validatorClass) { this.validatorClass = validatorClass; - this.validatedType = TypeHelper.extractType( validatorClass ); + this.validatedType = TypeHelper.extractValidatedType( validatorClass ); this.validationTargets = determineValidationTargets( validatorClass ); } + public static ClassBasedValidatorDescriptor of(Class> validatorClass, + Class registeredConstraintAnnotationType) { + Type definedConstraintAnnotationType = TypeHelper.extractConstraintType( validatorClass ); + if ( !registeredConstraintAnnotationType.equals( definedConstraintAnnotationType ) ) { + throw LOG.getConstraintValidatorDefinitionConstraintMismatchException( validatorClass, registeredConstraintAnnotationType, + definedConstraintAnnotationType ); + } + + return new ClassBasedValidatorDescriptor( validatorClass ); + } + private static EnumSet determineValidationTargets(Class> validatorClass) { SupportedValidationTarget supportedTargetAnnotation = validatorClass.getAnnotation( - SupportedValidationTarget.class - ); + SupportedValidationTarget.class ); - //by default constraints target the annotated element + // by default constraints target the annotated element if ( supportedTargetAnnotation == null ) { return EnumSet.of( ValidationTarget.ANNOTATED_ELEMENT ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java index 4d72ef1c10..692b75815c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ComposingConstraintTree.java @@ -94,7 +94,8 @@ protected void validateConstraints(ValidationContext validationContext, validationContext.getClockProvider(), valueContext.getPropertyPath(), descriptor, - validationContext.getConstraintValidatorPayload() + validationContext.getConstraintValidatorPayload(), + validationContext.isExpressionLanguageEnabled() ); // validate 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 ff5c9c14d8..75041de0dd 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,6 +6,7 @@ */ package org.hibernate.validator.internal.engine.constraintvalidation; +import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collections; @@ -43,6 +44,7 @@ public class ConstraintValidatorContextImpl implements HibernateConstraintValida private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + private final boolean expressionLanguageEnabled; private Map messageParameters; private Map expressionVariables; private final List methodParameterNames; @@ -55,7 +57,8 @@ public class ConstraintValidatorContextImpl implements HibernateConstraintValida private final Object constraintValidatorPayload; public ConstraintValidatorContextImpl(List methodParameterNames, ClockProvider clockProvider, - PathImpl propertyPath, ConstraintDescriptor constraintDescriptor, Object constraintValidatorPayload) { + PathImpl propertyPath, ConstraintDescriptor constraintDescriptor, Object constraintValidatorPayload, boolean expressionLanguageEnabled) { + this.expressionLanguageEnabled = expressionLanguageEnabled; this.methodParameterNames = methodParameterNames; this.clockProvider = clockProvider; this.basePath = propertyPath; @@ -164,6 +167,7 @@ public final List getConstraintViolationCrea private ConstraintViolationCreationContext getDefaultConstraintViolationCreationContext() { return new ConstraintViolationCreationContext( getDefaultConstraintMessageTemplate(), + true, // EL is enabled for the default constraint violation basePath, messageParameters != null ? new HashMap<>( messageParameters ) : Collections.emptyMap(), expressionVariables != null ? new HashMap<>( expressionVariables ) : Collections.emptyMap(), @@ -179,19 +183,26 @@ private abstract class NodeBuilderBase { protected final String messageTemplate; protected PathImpl propertyPath; + protected final boolean expressionLanguageEnabled; - protected NodeBuilderBase(String template, PathImpl path) { + protected NodeBuilderBase(String template, PathImpl path, boolean expressionLanguageEnabled) { this.messageTemplate = template; this.propertyPath = path; + this.expressionLanguageEnabled = expressionLanguageEnabled; } public ConstraintValidatorContext addConstraintViolation() { if ( constraintViolationCreationContexts == null ) { constraintViolationCreationContexts = CollectionHelper.newArrayList( 3 ); } + if ( !(expressionVariables == null || expressionVariables.isEmpty()) && !expressionLanguageEnabled ) { + LOG.expressionVariablesDefinedWithExpressionLanguageNotEnabled( + constraintDescriptor.getAnnotation() != null ? constraintDescriptor.getAnnotation().annotationType() : Annotation.class ); + } constraintViolationCreationContexts.add( new ConstraintViolationCreationContext( messageTemplate, + expressionLanguageEnabled, propertyPath, messageParameters != null ? new HashMap<>( messageParameters ) : Collections.emptyMap(), expressionVariables != null ? new HashMap<>( expressionVariables ) : Collections.emptyMap(), @@ -202,12 +213,12 @@ public ConstraintValidatorContext addConstraintViolation() { } } - private class ConstraintViolationBuilderImpl extends NodeBuilderBase implements ConstraintViolationBuilder { + protected class ConstraintViolationBuilderImpl extends NodeBuilderBase implements ConstraintViolationBuilder { private final List methodParameterNames; private ConstraintViolationBuilderImpl(List methodParameterNames, String template, PathImpl path) { - super( template, path ); + super( template, path, ConstraintValidatorContextImpl.this.expressionLanguageEnabled ); this.methodParameterNames = methodParameterNames; } @@ -216,19 +227,19 @@ public NodeBuilderDefinedContext addNode(String name) { dropLeafNodeIfRequired(); propertyPath.addPropertyNode( name ); - return new NodeBuilder( messageTemplate, propertyPath ); + return new NodeBuilder( messageTemplate, propertyPath, expressionLanguageEnabled ); } @Override public NodeBuilderCustomizableContext addPropertyNode(String name) { dropLeafNodeIfRequired(); - return new DeferredNodeBuilder( messageTemplate, propertyPath, name, ElementKind.PROPERTY ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, name, ElementKind.PROPERTY ); } @Override public LeafNodeBuilderCustomizableContext addBeanNode() { - return new DeferredNodeBuilder( messageTemplate, propertyPath, null, ElementKind.BEAN ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, null, ElementKind.BEAN ); } @Override @@ -240,14 +251,14 @@ public NodeBuilderDefinedContext addParameterNode(int index) { dropLeafNodeIfRequired(); propertyPath.addParameterNode( methodParameterNames.get( index ), index ); - return new NodeBuilder( messageTemplate, propertyPath ); + return new NodeBuilder( messageTemplate, propertyPath, expressionLanguageEnabled ); } @Override public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class containerType, Integer typeArgumentIndex) { dropLeafNodeIfRequired(); - return new DeferredNodeBuilder( messageTemplate, propertyPath, name, containerType, typeArgumentIndex ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, name, containerType, typeArgumentIndex ); } /** @@ -266,8 +277,8 @@ private void dropLeafNodeIfRequired() { private class NodeBuilder extends NodeBuilderBase implements NodeBuilderDefinedContext, LeafNodeBuilderDefinedContext, ContainerElementNodeBuilderDefinedContext { - private NodeBuilder(String template, PathImpl path) { - super( template, path ); + private NodeBuilder(String template, PathImpl path, boolean expressionLanguageEnabled) { + super( template, path, expressionLanguageEnabled ); } @Override @@ -278,17 +289,17 @@ public ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String @Override public NodeBuilderCustomizableContext addPropertyNode(String name) { - return new DeferredNodeBuilder( messageTemplate, propertyPath, name, ElementKind.PROPERTY ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, name, ElementKind.PROPERTY ); } @Override public LeafNodeBuilderCustomizableContext addBeanNode() { - return new DeferredNodeBuilder( messageTemplate, propertyPath, null, ElementKind.BEAN ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, null, ElementKind.BEAN ); } @Override public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class containerType, Integer typeArgumentIndex) { - return new DeferredNodeBuilder( messageTemplate, propertyPath, name, containerType, typeArgumentIndex ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, name, containerType, typeArgumentIndex ); } } @@ -304,16 +315,21 @@ private class DeferredNodeBuilder extends NodeBuilderBase private final Integer leafNodeTypeArgumentIndex; - private DeferredNodeBuilder(String template, PathImpl path, String nodeName, ElementKind leafNodeKind) { - super( template, path ); + private DeferredNodeBuilder(String template, boolean expressionLanguageEnabled, PathImpl path, String nodeName, ElementKind leafNodeKind) { + super( template, path, expressionLanguageEnabled ); this.leafNodeName = nodeName; this.leafNodeKind = leafNodeKind; this.leafNodeContainerType = null; this.leafNodeTypeArgumentIndex = null; } - private DeferredNodeBuilder(String template, PathImpl path, String nodeName, Class leafNodeContainerType, Integer leafNodeTypeArgumentIndex) { - super( template, path ); + private DeferredNodeBuilder(String template, + boolean expressionLanguageEnabled, + PathImpl path, + String nodeName, + Class leafNodeContainerType, + Integer leafNodeTypeArgumentIndex) { + super( template, path, expressionLanguageEnabled ); this.leafNodeName = nodeName; this.leafNodeKind = ElementKind.CONTAINER_ELEMENT; this.leafNodeContainerType = leafNodeContainerType; @@ -336,14 +352,14 @@ public DeferredNodeBuilder inContainer(Class containerClass, Integer typeArgu public NodeBuilder atKey(Object key) { propertyPath.makeLeafNodeIterableAndSetMapKey( key ); addLeafNode(); - return new NodeBuilder( messageTemplate, propertyPath ); + return new NodeBuilder( messageTemplate, propertyPath, expressionLanguageEnabled ); } @Override public NodeBuilder atIndex(Integer index) { propertyPath.makeLeafNodeIterableAndSetIndex( index ); addLeafNode(); - return new NodeBuilder( messageTemplate, propertyPath ); + return new NodeBuilder( messageTemplate, propertyPath, expressionLanguageEnabled ); } @Override @@ -355,19 +371,19 @@ public NodeBuilderCustomizableContext addNode(String name) { @Override public NodeBuilderCustomizableContext addPropertyNode(String name) { addLeafNode(); - return new DeferredNodeBuilder( messageTemplate, propertyPath, name, ElementKind.PROPERTY ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, name, ElementKind.PROPERTY ); } @Override public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class containerType, Integer typeArgumentIndex) { addLeafNode(); - return new DeferredNodeBuilder( messageTemplate, propertyPath, name, containerType, typeArgumentIndex ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, name, containerType, typeArgumentIndex ); } @Override public LeafNodeBuilderCustomizableContext addBeanNode() { addLeafNode(); - return new DeferredNodeBuilder( messageTemplate, propertyPath, null, ElementKind.BEAN ); + return new DeferredNodeBuilder( messageTemplate, expressionLanguageEnabled, propertyPath, null, ElementKind.BEAN ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java index 7132ad1c9a..cb4da5b2ec 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorDescriptor.java @@ -44,8 +44,9 @@ public interface ConstraintValidatorDescriptor { */ ConstraintValidator newInstance(ConstraintValidatorFactory constraintValidatorFactory); - static ConstraintValidatorDescriptor forClass(Class> validatorClass) { - return new ClassBasedValidatorDescriptor<>( validatorClass ); + static ConstraintValidatorDescriptor forClass(Class> validatorClass, + Class constraintAnnotationType) { + return ClassBasedValidatorDescriptor.of( validatorClass, constraintAnnotationType ); } static ConstraintValidatorDescriptor forLambda(Class annotationType, Type validatedType, ValidationCallable lambda) { 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 ce0396a78a..27ed0d5dbc 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 @@ -18,9 +18,12 @@ * Container class for the information needed to create a constraint violation. * * @author Hardy Ferentschik + * @author Guillaume Smet */ public class ConstraintViolationCreationContext { + private final String message; + private final boolean expressionLanguageEnabled; private final PathImpl propertyPath; @Immutable private final Map messageParameters; @@ -29,12 +32,17 @@ public class ConstraintViolationCreationContext { private final Object dynamicPayload; public ConstraintViolationCreationContext(String message, PathImpl property) { - this( message, property, Collections.emptyMap(), Collections.emptyMap(), null ); + this( message, true, property, Collections.emptyMap(), Collections.emptyMap(), null ); } - public ConstraintViolationCreationContext(String message, PathImpl property, Map messageParameters, Map expressionVariables, + public ConstraintViolationCreationContext(String message, + boolean expressionLanguageEnabled, + PathImpl property, + Map messageParameters, + Map expressionVariables, Object dynamicPayload) { this.message = message; + this.expressionLanguageEnabled = expressionLanguageEnabled; this.propertyPath = property; this.messageParameters = toImmutableMap( messageParameters ); this.expressionVariables = toImmutableMap( expressionVariables ); @@ -45,6 +53,10 @@ public final String getMessage() { return message; } + public boolean isExpressionLanguageEnabled() { + return expressionLanguageEnabled; + } + public final PathImpl getPath() { return propertyPath; } @@ -65,6 +77,7 @@ public Object getDynamicPayload() { public String toString() { final StringBuilder sb = new StringBuilder( "ConstraintViolationCreationContext{" ); sb.append( "message='" ).append( message ).append( '\'' ); + sb.append( ", expressionLanguageEnabled=" ).append( expressionLanguageEnabled ); sb.append( ", propertyPath=" ).append( propertyPath ); sb.append( ", messageParameters=" ).append( messageParameters ); sb.append( ", expressionVariables=" ).append( expressionVariables ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java index 586c398708..403373c815 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/SimpleConstraintTree.java @@ -60,7 +60,8 @@ protected void validateConstraints(ValidationContext validationContext, validationContext.getClockProvider(), valueContext.getPropertyPath(), descriptor, - validationContext.getConstraintValidatorPayload() + validationContext.getConstraintValidatorPayload(), + validationContext.isExpressionLanguageEnabled() ); // validate diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ParameterTermResolver.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ParameterTermResolver.java index a880afe755..83337d0fea 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ParameterTermResolver.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/ParameterTermResolver.java @@ -18,6 +18,7 @@ * @author Hardy Ferentschik * @author Adam Stawicki * @author Guillaume Smet + * @author Alexander Gatsenko */ public class ParameterTermResolver implements TermResolver { @@ -26,12 +27,7 @@ public String interpolate(Context context, String expression) { String resolvedExpression; Object variable = getVariable( context, removeCurlyBraces( expression ) ); if ( variable != null ) { - if ( variable.getClass().isArray() ) { - resolvedExpression = Arrays.toString( (Object[]) variable ); - } - else { - resolvedExpression = variable.toString(); - } + resolvedExpression = resolveExpression( variable ); } else { resolvedExpression = expression; @@ -52,4 +48,41 @@ private Object getVariable(Context context, String parameter) { private String removeCurlyBraces(String parameter) { return parameter.substring( 1, parameter.length() - 1 ); } + + private String resolveExpression(Object variable) { + final String resolvedExpression; + if ( variable.getClass().isArray() ) { + if ( variable.getClass() == boolean[].class ) { + resolvedExpression = Arrays.toString( (boolean[]) variable ); + } + else if ( variable.getClass() == char[].class ) { + resolvedExpression = Arrays.toString( (char[]) variable ); + } + else if ( variable.getClass() == byte[].class ) { + resolvedExpression = Arrays.toString( (byte[]) variable ); + } + else if ( variable.getClass() == short[].class ) { + resolvedExpression = Arrays.toString( (short[]) variable ); + } + else if ( variable.getClass() == int[].class ) { + resolvedExpression = Arrays.toString( (int[]) variable ); + } + else if ( variable.getClass() == long[].class ) { + resolvedExpression = Arrays.toString( (long[]) variable ); + } + else if ( variable.getClass() == float[].class ) { + resolvedExpression = Arrays.toString( (float[]) variable ); + } + else if ( variable.getClass() == double[].class ) { + resolvedExpression = Arrays.toString( (double[]) variable ); + } + else { + resolvedExpression = Arrays.toString( (Object[]) variable ); + } + } + else { + resolvedExpression = variable.toString(); + } + return resolvedExpression; + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java index c4d2518b53..8647e34837 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/el/SimpleELContext.java @@ -15,7 +15,6 @@ import javax.el.MapELResolver; import javax.el.ResourceBundleELResolver; import javax.el.StandardELContext; -import javax.el.StaticFieldELResolver; /** * @author Hardy Ferentschik @@ -25,7 +24,6 @@ public class SimpleELContext extends StandardELContext { private static final ELResolver DEFAULT_RESOLVER = new CompositeELResolver() { { add( new RootResolver() ); - add( new StaticFieldELResolver() ); add( new ArrayELResolver( true ) ); add( new ListELResolver( true ) ); add( new MapELResolver( true ) ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java index 9460fae057..9f480f8482 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/ELState.java @@ -47,7 +47,7 @@ public void handleBeginTerm(char character, TokenCollector tokenCollector) throw @Override public void handleEndTerm(char character, TokenCollector tokenCollector) throws MessageDescriptorFormatException { - throw LOG.getNonTerminatedParameterException( + throw LOG.getUnbalancedBeginEndParameterException( tokenCollector.getOriginalMessageDescriptor(), character ); @@ -56,7 +56,11 @@ public void handleEndTerm(char character, TokenCollector tokenCollector) throws @Override public void handleEscapeCharacter(char character, TokenCollector tokenCollector) throws MessageDescriptorFormatException { - tokenCollector.transitionState( new EscapedState( this ) ); + tokenCollector.appendToToken( EL_DESIGNATOR ); + tokenCollector.appendToToken( character ); + // Do not go back to this state after the escape: $\ is not the start of an EL expression + ParserState stateAfterEscape = new MessageState(); + tokenCollector.transitionState( new EscapedState( stateAfterEscape ) ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java index 9b00c35948..809db34dd7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/InterpolationTermState.java @@ -22,7 +22,7 @@ public class InterpolationTermState implements ParserState { @Override public void terminate(TokenCollector tokenCollector) throws MessageDescriptorFormatException { - throw LOG.getNonTerminatedParameterException( + throw LOG.getUnbalancedBeginEndParameterException( tokenCollector.getOriginalMessageDescriptor(), BEGIN_TERM ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java index a8b1de63fd..88171668a2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/parser/MessageState.java @@ -43,7 +43,7 @@ public void handleBeginTerm(char character, TokenCollector tokenCollector) throw @Override public void handleEndTerm(char character, TokenCollector tokenCollector) throws MessageDescriptorFormatException { - throw LOG.getNonTerminatedParameterException( + throw LOG.getUnbalancedBeginEndParameterException( tokenCollector.getOriginalMessageDescriptor(), character ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java index b1d401ea9a..c4ed299cab 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/JPATraversableResolver.java @@ -21,11 +21,13 @@ * query the reachability of a property. * This resolver will be automatically enabled if JPA 2 is on the classpath and the default {@code TraversableResolver} is * used. + *

+ * This class needs to be public as it's instantiated via a privileged action that is not in this package. * * @author Hardy Ferentschik * @author Emmanuel Bernard */ -class JPATraversableResolver implements TraversableResolver { +public class JPATraversableResolver implements TraversableResolver { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java index f1392120bd..e7c513b884 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/resolver/TraversableResolvers.java @@ -106,10 +106,7 @@ public static TraversableResolver getDefault() { return run( NewInstance.action( jpaAwareResolverClass, "" ) ); } catch (ValidationException e) { - LOG.debugf( - "Unable to load or instantiate JPA aware resolver %s. All properties will per default be traversable.", - JPA_AWARE_TRAVERSABLE_RESOLVER_CLASS_NAME - ); + LOG.logUnableToLoadOrInstantiateJPAAwareResolver( JPA_AWARE_TRAVERSABLE_RESOLVER_CLASS_NAME ); return getTraverseAllTraversableResolver(); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java index 59b691b8fc..bcccb67566 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ListPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ListProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ListPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ListPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java index e2b507fae0..bbd5dc7bc4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyKeyExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.MapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class MapPropertyKeyExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new MapPropertyKeyExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java index 0db1b74ba4..179ed8e51b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/MapPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.MapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class MapPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new MapPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java index 986ed2b559..12232824d0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ObservableValueValueExtractor.java @@ -10,6 +10,8 @@ import javax.validation.valueextraction.UnwrapByDefault; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; + import javafx.beans.value.ObservableValue; /** @@ -18,6 +20,7 @@ * @author Gunnar Morling */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") @UnwrapByDefault class ObservableValueValueExtractor implements ValueExtractor> { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java index e91f838030..e003c52245 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyListPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlyListProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlyListPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlyListPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java index 6632054deb..c98cefd76f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyKeyExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlyMapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlyMapPropertyKeyExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlyMapPropertyKeyExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java index 4217177d4d..e843400dd0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlyMapPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlyMapProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlyMapPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlyMapPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java index 1366eebdd0..18635dc017 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ReadOnlySetPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.ReadOnlySetProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class ReadOnlySetPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new ReadOnlySetPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java index e7847ddd0e..bcd0d9b55b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/SetPropertyValueExtractor.java @@ -11,6 +11,7 @@ import javax.validation.valueextraction.ExtractedValue; import javax.validation.valueextraction.ValueExtractor; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.engine.path.NodeImpl; import javafx.beans.property.SetProperty; @@ -25,6 +26,7 @@ * @author Guillaume Smet */ @SuppressWarnings("restriction") +@IgnoreForbiddenApisErrors(reason = "Usage of JavaFX classes") class SetPropertyValueExtractor implements ValueExtractor> { static final ValueExtractorDescriptor DESCRIPTOR = new ValueExtractorDescriptor( new SetPropertyValueExtractor() ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java index 4532c0b7ad..e872ac57ab 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorManager.java @@ -178,7 +178,7 @@ public boolean equals(Object obj) { } private static boolean isJavaFxInClasspath() { - return isClassPresent( "javafx.application.Application", false ); + return isClassPresent( "javafx.beans.value.ObservableValue", false ); } private static boolean isClassPresent(String className, boolean fallbackOnTCCL) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java index afaa2e247a..4285b34da2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/valueextraction/ValueExtractorResolver.java @@ -47,8 +47,6 @@ public class ValueExtractorResolver { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - private static final Object NON_CONTAINER_VALUE = new Object(); - @Immutable private final Set registeredValueExtractors; @@ -56,7 +54,7 @@ public class ValueExtractorResolver { private final ConcurrentHashMap, Set> possibleValueExtractorsByRuntimeType = new ConcurrentHashMap<>(); - private final ConcurrentHashMap, Object> nonContainerTypes = new ConcurrentHashMap<>(); + private final Set> nonContainerTypes = Collections.newSetFromMap( new ConcurrentHashMap<>() ); ValueExtractorResolver(Set valueExtractors) { this.registeredValueExtractors = CollectionHelper.toImmutableSet( valueExtractors ); @@ -303,26 +301,27 @@ private Set getRuntimeCompliantValueExtractors(Class valueExtractorDescriptors = possibleValueExtractorsByRuntimeType.get( runtimeType ); - if ( valueExtractorDescriptors == null ) { - //otherwise we just look for maximally specific extractors for the runtime type - Set possibleValueExtractors = potentialValueExtractorDescriptors - .stream() - .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) - .collect( Collectors.toSet() ); - - valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + + if ( valueExtractorDescriptors != null ) { + return valueExtractorDescriptors; } + Set possibleValueExtractors = potentialValueExtractorDescriptors + .stream() + .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) + .collect( Collectors.toSet() ); + + valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + if ( valueExtractorDescriptors.isEmpty() ) { - nonContainerTypes.put( runtimeType, NON_CONTAINER_VALUE ); - } - else { - Set extractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); - possibleValueExtractorsByRuntimeType.put( runtimeType, extractorDescriptorsToCache ); - return extractorDescriptorsToCache; + nonContainerTypes.add( runtimeType ); + return Collections.emptySet(); } - return valueExtractorDescriptors; + Set valueExtractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); + Set cachedValueExtractorDescriptors = possibleValueExtractorsByRuntimeType.putIfAbsent( runtimeType, + valueExtractorDescriptorsToCache ); + return cachedValueExtractorDescriptors != null ? cachedValueExtractorDescriptors : valueExtractorDescriptorsToCache; } private Set getRuntimeAndContainerElementCompliantValueExtractorsFromPossibleCandidates(Type declaredType, @@ -334,32 +333,34 @@ private Set getRuntimeAndContainerElementCompliantValu ValueExtractorCacheKey cacheKey = new ValueExtractorCacheKey( runtimeType, typeParameter ); Set valueExtractorDescriptors = possibleValueExtractorsByRuntimeTypeAndTypeParameter.get( cacheKey ); - if ( valueExtractorDescriptors == null ) { - boolean isInternal = TypeVariables.isInternal( typeParameter ); - Class erasedDeclaredType = TypeHelper.getErasedReferenceType( declaredType ); - Set possibleValueExtractors = valueExtractorCandidates - .stream() - .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) - .filter( extractorDescriptor -> - checkValueExtractorTypeCompatibility( - typeParameter, isInternal, erasedDeclaredType, extractorDescriptor - ) - ).collect( Collectors.toSet() ); + if ( valueExtractorDescriptors != null ) { + return valueExtractorDescriptors; + } + + boolean isInternal = TypeVariables.isInternal( typeParameter ); + Class erasedDeclaredType = TypeHelper.getErasedReferenceType( declaredType ); - valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + Set possibleValueExtractors = valueExtractorCandidates + .stream() + .filter( e -> TypeHelper.isAssignable( e.getContainerType(), runtimeType ) ) + .filter( extractorDescriptor -> + checkValueExtractorTypeCompatibility( + typeParameter, isInternal, erasedDeclaredType, extractorDescriptor + ) + ).collect( Collectors.toSet() ); - if ( valueExtractorDescriptors.isEmpty() ) { - nonContainerTypes.put( runtimeType, NON_CONTAINER_VALUE ); - } - else { - Set extractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); - possibleValueExtractorsByRuntimeTypeAndTypeParameter.put( cacheKey, extractorDescriptorsToCache ); - return extractorDescriptorsToCache; - } + valueExtractorDescriptors = getMaximallySpecificValueExtractors( possibleValueExtractors ); + + if ( valueExtractorDescriptors.isEmpty() ) { + nonContainerTypes.add( runtimeType ); + return Collections.emptySet(); } - return valueExtractorDescriptors; + Set valueExtractorDescriptorsToCache = CollectionHelper.toImmutableSet( valueExtractorDescriptors ); + Set cachedValueExtractorDescriptors = possibleValueExtractorsByRuntimeTypeAndTypeParameter.putIfAbsent( cacheKey, + valueExtractorDescriptorsToCache ); + return cachedValueExtractorDescriptors != null ? cachedValueExtractorDescriptors : valueExtractorDescriptorsToCache; } private boolean checkValueExtractorTypeCompatibility(TypeVariable typeParameter, boolean isInternal, Class erasedDeclaredType, 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 5a5abbf095..e71ab8aaf3 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 @@ -34,6 +34,7 @@ import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; /** * This manager is in charge of providing all constraint related meta data @@ -103,6 +104,8 @@ public class BeanMetaDataManager { */ private final ExecutableHelper executableHelper; + private final BeanMetaDataClassNormalizer beanMetaDataClassNormalizer; + private final ValidationOrderGenerator validationOrderGenerator; /** @@ -117,6 +120,7 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ExecutableParameterNameProvider parameterNameProvider, ValueExtractorManager valueExtractorManager, + BeanMetaDataClassNormalizer beanMetaDataClassNormalizer, ValidationOrderGenerator validationOrderGenerator, List optionalMetaDataProviders, MethodValidationConfiguration methodValidationConfiguration) { @@ -125,6 +129,7 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, this.typeResolutionHelper = typeResolutionHelper; this.valueExtractorManager = valueExtractorManager; this.parameterNameProvider = parameterNameProvider; + this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer; this.validationOrderGenerator = validationOrderGenerator; this.metaDataProviders = newArrayList(); @@ -152,14 +157,30 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper, this.metaDataProviders.add( defaultProvider ); } + // TODO Some of these casts from BeanMetadata to BeanMetadata may not be safe. + // Maybe we should return a wrapper around the BeanMetadata if the normalized class is different from beanClass? @SuppressWarnings("unchecked") public BeanMetaData getBeanMetaData(Class beanClass) { Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() ); - BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataCache.computeIfAbsent( beanClass, - bc -> createBeanMetaData( bc ) ); + Class normalizedBeanClass = beanMetaDataClassNormalizer.normalize( beanClass ); + + // First, let's do a simple lookup as it's the default case + BeanMetaData beanMetaData = (BeanMetaData) beanMetaDataCache.get( normalizedBeanClass ); + + if ( beanMetaData != null ) { + return (BeanMetaData) beanMetaData; + } + + beanMetaData = createBeanMetaData( normalizedBeanClass ); + BeanMetaData previousBeanMetaData = (BeanMetaData) beanMetaDataCache.putIfAbsent( normalizedBeanClass, beanMetaData ); + + // we return the previous value if not null + if ( previousBeanMetaData != null ) { + return (BeanMetaData) previousBeanMetaData; + } - return beanMetaData; + return (BeanMetaData) beanMetaData; } public void clear() { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/DefaultBeanMetaDataClassNormalizer.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/DefaultBeanMetaDataClassNormalizer.java new file mode 100644 index 0000000000..14e6e40f37 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/DefaultBeanMetaDataClassNormalizer.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.internal.metadata; + +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; + +/** + * The default implementation of {@link BeanMetaDataClassNormalizer}. + *

+ * Simply returns the provided class. + * + * @author Guillaume Smet + */ +public class DefaultBeanMetaDataClassNormalizer implements BeanMetaDataClassNormalizer { + + @Override + public Class normalize(Class beanClass) { + return beanClass; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java index 59c433f01c..6ca82faea5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/FieldCascadable.java @@ -7,7 +7,6 @@ package org.hibernate.validator.internal.metadata.aggregated; import java.lang.annotation.ElementType; -import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.security.AccessController; @@ -85,14 +84,9 @@ public FieldCascadable build() { } /** - * Returns an accessible version of the given member. Will be the given member itself in case it is accessible, - * otherwise a copy which is set accessible. + * Returns an accessible copy of the given member. */ private Field getAccessible(Field original) { - if ( ( (AccessibleObject) original ).isAccessible() ) { - return original; - } - SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); 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 c1731a8678..f616e8e923 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 @@ -33,6 +33,7 @@ import org.hibernate.validator.internal.metadata.descriptor.PropertyDescriptorImpl; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.location.GetterConstraintLocation; import org.hibernate.validator.internal.metadata.location.TypeArgumentConstraintLocation; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; @@ -262,7 +263,13 @@ private MetaConstraint withGetterLocation(ConstraintLocation getterConstraint // fast track if it's a regular constraint if ( !(constraint.getLocation() instanceof TypeArgumentConstraintLocation) ) { - converted = getterConstraintLocation; + // Change the constraint location to a GetterConstraintLocation if it is not already one + if ( constraint.getLocation() instanceof GetterConstraintLocation ) { + converted = constraint.getLocation(); + } + else { + converted = getterConstraintLocation; + } } else { Deque locationStack = new ArrayDeque<>(); @@ -283,7 +290,13 @@ private MetaConstraint withGetterLocation(ConstraintLocation getterConstraint // 2. beginning at the root, transform each location so it references the transformed delegate for ( ConstraintLocation location : locationStack ) { if ( !(location instanceof TypeArgumentConstraintLocation) ) { - converted = getterConstraintLocation; + // Change the constraint location to a GetterConstraintLocation if it is not already one + if ( location instanceof GetterConstraintLocation ) { + converted = location; + } + else { + converted = getterConstraintLocation; + } } else { converted = ConstraintLocation.forTypeArgument( @@ -310,14 +323,9 @@ else if ( constrainedElement.getKind() == ConstrainedElementKind.METHOD ) { } /** - * Returns an accessible version of the given member. Will be the given member itself in case it is accessible, - * otherwise a copy which is set accessible. + * Returns an accessible copy of the given member. */ private Method getAccessible(Method original) { - if ( original.isAccessible() ) { - return original; - } - SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); 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 9d0ca63446..2c5baf0eea 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 @@ -679,21 +679,24 @@ public ConstraintHelper() { this.builtinConstraints = Collections.unmodifiableMap( tmpConstraints ); } - private static void putConstraint(Map, List>> validators, Class constraintType, Class> validatorType) { - validators.put( constraintType, Collections.singletonList( ConstraintValidatorDescriptor.forClass( validatorType ) ) ); + private static void putConstraint(Map, List>> validators, + Class constraintType, Class> validatorType) { + validators.put( constraintType, Collections.singletonList( ConstraintValidatorDescriptor.forClass( validatorType, constraintType ) ) ); } - private static void putConstraints(Map, List>> validators, Class constraintType, Class> validatorType1, Class> validatorType2) { + private static void putConstraints(Map, List>> validators, + Class constraintType, Class> validatorType1, Class> validatorType2) { List> descriptors = Stream.of( validatorType1, validatorType2 ) - .map( ConstraintValidatorDescriptor::forClass ) + .map( vt -> ConstraintValidatorDescriptor.forClass( vt, constraintType ) ) .collect( Collectors.toList() ); validators.put( constraintType, CollectionHelper.toImmutableList( descriptors ) ); } - private static void putConstraints(Map, List>> validators, Class constraintType, List>> validatorDescriptors) { + private static void putConstraints(Map, List>> validators, + Class constraintType, List>> validatorDescriptors) { List> descriptors = validatorDescriptors.stream() - .map( ConstraintValidatorDescriptor::forClass ) + .map( vt -> ConstraintValidatorDescriptor.forClass( vt, constraintType ) ) .collect( Collectors.toList() ); validators.put( constraintType, CollectionHelper.toImmutableList( descriptors ) ); @@ -881,7 +884,7 @@ private void assertPayloadParameterExists(Class annotation throw LOG.getConstraintWithoutMandatoryParameterException( PAYLOAD, annotationType.getName() ); } Class[] defaultPayload = (Class[]) method.getDefaultValue(); - if ( defaultPayload.length != 0 ) { + if ( defaultPayload == null || defaultPayload.length != 0 ) { throw LOG.getWrongDefaultValueForPayloadParameterException( annotationType.getName() ); } } @@ -897,7 +900,7 @@ private void assertGroupsParameterExists(Class annotationT throw LOG.getConstraintWithoutMandatoryParameterException( GROUPS, annotationType.getName() ); } Class[] defaultGroups = (Class[]) method.getDefaultValue(); - if ( defaultGroups.length != 0 ) { + if ( defaultGroups == null || defaultGroups.length != 0 ) { throw LOG.getWrongDefaultValueForGroupsParameterException( annotationType.getName() ); } } @@ -995,8 +998,8 @@ private List> getDefault .validatedBy(); return Stream.of( validatedBy ) - .map( c -> ConstraintValidatorDescriptor.forClass( c ) ) - .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ); + .map( c -> ConstraintValidatorDescriptor.forClass( c, annotationType ) ) + .collect( Collectors.collectingAndThen( Collectors.toList(), CollectionHelper::toImmutableList ) ); } private static boolean isClassPresent(String className) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java index 0a95ddd10d..ced135aa74 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraints.java @@ -159,14 +159,14 @@ private static void addValueExtractorDescriptorForTypeArgumentLocation( ValueExt */ private static Class getWrappedValueType(TypeResolutionHelper typeResolutionHelper, Type declaredType, ValueExtractorDescriptor valueExtractorDescriptor) { ResolvedType resolvedType = typeResolutionHelper.getTypeResolver().resolve( declaredType ); + List resolvedTypeParameters = resolvedType.typeParametersFor( valueExtractorDescriptor.getContainerType() ); - if ( resolvedTypeParameters.isEmpty() ) { + if ( resolvedTypeParameters == null || resolvedTypeParameters.isEmpty() ) { throw LOG.getNoValueExtractorFoundForUnwrapException( declaredType ); } - else { - return resolvedTypeParameters.get( TypeVariables.getTypeParameterIndex( valueExtractorDescriptor.getExtractedTypeParameter() ) ).getErasedType(); - } + + return resolvedTypeParameters.get( TypeVariables.getTypeParameterIndex( valueExtractorDescriptor.getExtractedTypeParameter() ) ).getErasedType(); } private static TypeVariable getContainerClassTypeParameter(Class declaredType, ValueExtractorDescriptor selectedValueExtractorDescriptor) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java index 429d7d711d..1277f85051 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java @@ -43,8 +43,29 @@ static ConstraintLocation forField(Field field) { return new FieldConstraintLocation( field ); } + /** + * Create a new {@link GetterConstraintLocation} for the given getter method. + * + * @param getter The getter method being constrained + * @return A new GetterConstraintLocation + */ static ConstraintLocation forGetter(Method getter) { - return new GetterConstraintLocation( getter ); + return new GetterConstraintLocation( getter.getDeclaringClass(), getter ); + } + + /** + * Create a new {@link GetterConstraintLocation} for the given declaring class and getter method. + *

+ * This provides an alternative to {@link ConstraintLocation#forGetter(Method)} where the given declaring class is usually a sub-class of the + * actual class on which the getter method is declared. This is provided to support XML mapping configurations used to specify constraints on + * subclasses for inherited getter methods. + * + * @param declaringClass The class on which the constraint is defined. + * @param getter The getter method being constrained. + * @return A new GetterConstraintLocation + */ + static ConstraintLocation forGetter(Class declaringClass, Method getter ) { + return new GetterConstraintLocation( declaringClass, getter ); } static ConstraintLocation forTypeArgument(ConstraintLocation delegate, TypeVariable typeParameter, Type typeOfAnnotatedElement) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java index 5b88190ba6..4e8c21f963 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FieldConstraintLocation.java @@ -116,14 +116,9 @@ public int hashCode() { } /** - * Returns an accessible version of the given member. Will be the given member itself in case it is accessible, - * otherwise a copy which is set accessible. + * Returns an accessible copy of the given member. */ private static Field getAccessible(Field original) { - if ( original.isAccessible() ) { - return original; - } - SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java index 0e59e27cf4..129772a320 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/GetterConstraintLocation.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.internal.metadata.location; -import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.security.AccessController; @@ -44,17 +43,26 @@ public class GetterConstraintLocation implements ConstraintLocation { */ private final Type typeForValidatorResolution; + /** + * The class of the method for which the constraint was defined. + *

+ * It is usually the same as the declaring class of the method itself, except in the XML case when a user could + * declare a constraint for a specific subclass. + */ + private final Class declaringClass; - GetterConstraintLocation(Method method) { + + GetterConstraintLocation( Class declaringClass, Method method ) { this.method = method; this.accessibleMethod = getAccessible( method ); this.propertyName = ReflectionHelper.getPropertyName( method ); this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( method ) ); + this.declaringClass = declaringClass; } @Override public Class getDeclaringClass() { - return method.getDeclaringClass(); + return declaringClass; } @Override @@ -116,14 +124,9 @@ public int hashCode() { } /** - * Returns an accessible version of the given method. Will be the given method itself in case it is accessible, - * otherwise a copy which is set accessible. + * Returns an accessible copy of the given method. */ private static Method getAccessible(Method original) { - if ( ( (AccessibleObject) original ).isAccessible() ) { - return original; - } - SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS ); 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 1b387b8e13..2910323617 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 @@ -524,6 +524,10 @@ private Map, Class> getGroupConversions(AnnotatedElement annotatedEl } private Map, Class> getGroupConversions(ConvertGroup groupConversion, ConvertGroup.List groupConversionList) { + if ( groupConversion == null && ( groupConversionList == null || groupConversionList.value().length == 0 ) ) { + return Collections.emptyMap(); + } + Map, Class> groupConversions = newHashMap(); if ( groupConversion != null ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java index bbfc102c7d..9cef432d79 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/XmlMetaDataProvider.java @@ -20,7 +20,7 @@ import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; -import org.hibernate.validator.internal.xml.MappingXmlParser; +import org.hibernate.validator.internal.xml.mapping.MappingXmlParser; /** * A {@link MetaDataProvider} providing constraint related meta data based on diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java b/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java index a5867276a9..dc57395795 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/Contracts.java @@ -10,6 +10,7 @@ import java.lang.invoke.MethodHandles; import java.util.Collection; +import java.util.Locale; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -69,7 +70,7 @@ public static void assertTrue(boolean condition, String message) { public static void assertTrue(boolean condition, String message, Object... messageParameters) { if ( !condition ) { - throw LOG.getIllegalArgumentException( String.format( message, messageParameters ) ); + throw LOG.getIllegalArgumentException( String.format( Locale.ROOT, message, messageParameters ) ); } } @@ -87,7 +88,7 @@ public static void assertNotEmpty(Collection collection, String message) { public static void assertNotEmpty(Collection collection, String message, Object... messageParameters) { if ( collection.size() == 0 ) { - throw LOG.getIllegalArgumentException( String.format( message, messageParameters ) ); + throw LOG.getIllegalArgumentException( String.format( Locale.ROOT, message, messageParameters ) ); } } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/DomainNameUtil.java b/engine/src/main/java/org/hibernate/validator/internal/util/DomainNameUtil.java index 73b6f65f76..49cf3f0869 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/DomainNameUtil.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/DomainNameUtil.java @@ -25,12 +25,12 @@ public final class DomainNameUtil { private static final int MAX_DOMAIN_PART_LENGTH = 255; private static final String DOMAIN_CHARS_WITHOUT_DASH = "[a-z\u0080-\uFFFF0-9!#$%&'*+/=?^_`{|}~]"; - private static final String DOMAIN_LABEL = "(" + DOMAIN_CHARS_WITHOUT_DASH + "-*)*" + DOMAIN_CHARS_WITHOUT_DASH + "+"; - private static final String DOMAIN = DOMAIN_LABEL + "+(\\." + DOMAIN_LABEL + "+)*"; + private static final String DOMAIN_LABEL = "(?:" + DOMAIN_CHARS_WITHOUT_DASH + "-*)*" + DOMAIN_CHARS_WITHOUT_DASH + "+"; + private static final String DOMAIN = DOMAIN_LABEL + "+(?:\\." + DOMAIN_LABEL + "+)*"; private static final String IP_DOMAIN = "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"; //IP v6 regex taken from http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses - private static final String IP_V6_DOMAIN = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; + private static final String IP_V6_DOMAIN = "(?:(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(:0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; /** * Regular expression for the domain part of an URL @@ -80,11 +80,6 @@ private static boolean isValidDomainAddress(String domain, Pattern pattern) { return false; } - Matcher matcher = pattern.matcher( domain ); - if ( !matcher.matches() ) { - return false; - } - String asciiString; try { asciiString = IDN.toASCII( domain ); @@ -97,6 +92,11 @@ private static boolean isValidDomainAddress(String domain, Pattern pattern) { return false; } + Matcher matcher = pattern.matcher( domain ); + if ( !matcher.matches() ) { + return false; + } + return true; } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java b/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java index 680100121c..1a348ce20e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/TypeHelper.java @@ -31,6 +31,7 @@ import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -52,6 +53,7 @@ public final class TypeHelper { private static final Map, Set>> SUBTYPES_BY_PRIMITIVE; + private static final int CONSTRAINT_TYPE_INDEX = 0; private static final int VALIDATOR_TYPE_INDEX = 1; private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); @@ -324,24 +326,32 @@ public static Map> return validatorsTypes; } - public static Type extractType(Class> validator) { - Map resolvedTypes = newHashMap(); + public static Type extractValidatedType(Class> validator) { + return extractConstraintValidatorTypeArgumentType( validator, VALIDATOR_TYPE_INDEX ); + } + + public static Type extractConstraintType(Class> validator) { + return extractConstraintValidatorTypeArgumentType( validator, CONSTRAINT_TYPE_INDEX ); + } + + public static Type extractConstraintValidatorTypeArgumentType(Class> validator, int typeArgumentIndex) { + Map resolvedTypes = new HashMap<>(); Type constraintValidatorType = resolveTypes( resolvedTypes, validator ); //we now have all bind from a type to its resolution at one level - Type validatorType = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[VALIDATOR_TYPE_INDEX]; - if ( validatorType == null ) { + Type type = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[typeArgumentIndex]; + if ( type == null ) { throw LOG.getNullIsAnInvalidTypeForAConstraintValidatorException(); } - else if ( validatorType instanceof GenericArrayType ) { - validatorType = TypeHelper.getArrayType( TypeHelper.getComponentType( validatorType ) ); + else if ( type instanceof GenericArrayType ) { + type = TypeHelper.getArrayType( TypeHelper.getComponentType( type ) ); } - while ( resolvedTypes.containsKey( validatorType ) ) { - validatorType = resolvedTypes.get( validatorType ); + while ( resolvedTypes.containsKey( type ) ) { + type = resolvedTypes.get( type ); } //FIXME raise an exception if validatorType is not a class - return validatorType; + return type; } public static boolean isUnboundWildcard(Type type) { @@ -528,6 +538,7 @@ private static boolean isSuperAssignable(Type supertype, Type type) { *

  • a parameterized type
  • *
  • a type variable
  • *
  • the null type
  • + *
  • a wildcard type
  • * * * @param type the type to check @@ -541,7 +552,8 @@ private static boolean isReferenceType(Type type) { || type instanceof Class || type instanceof ParameterizedType || type instanceof TypeVariable - || type instanceof GenericArrayType; + || type instanceof GenericArrayType + || type instanceof WildcardType; } private static boolean isArraySupertype(Class type) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java b/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java index 9bd6c9270e..973137a018 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/annotation/AnnotationFactory.java @@ -7,16 +7,11 @@ package org.hibernate.validator.internal.util.annotation; import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.PrivilegedAction; -import org.hibernate.validator.internal.util.privilegedactions.ConstructorInstance; import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructor; +import org.hibernate.validator.internal.util.privilegedactions.NewProxyInstance; /** * Creates live annotations (actually {@link AnnotationProxy} instances) from {@code AnnotationDescriptor}s. @@ -32,31 +27,11 @@ private AnnotationFactory() { } public static T create(AnnotationDescriptor descriptor) { - @SuppressWarnings("unchecked") - Class proxyClass = (Class) Proxy.getProxyClass( + return run( NewProxyInstance.action( run( GetClassLoader.fromClass( descriptor.getType() ) ), - descriptor.getType() - ); - InvocationHandler handler = new AnnotationProxy( descriptor ); - try { - return getProxyInstance( proxyClass, handler ); - } - catch (RuntimeException e) { - throw e; - } - catch (Exception e) { - throw new RuntimeException( e ); - } - } - - private static T getProxyInstance(Class proxyClass, InvocationHandler handler) throws - SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, - IllegalAccessException, InvocationTargetException { - final Constructor constructor = run( GetDeclaredConstructor.action( - proxyClass, - InvocationHandler.class + descriptor.getType(), + new AnnotationProxy( descriptor ) ) ); - return run( ConstructorInstance.action( constructor, handler ) ); } /** 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 816e30a440..8be986c376 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 @@ -48,6 +48,7 @@ import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.logging.formatter.ArrayOfClassesObjectFormatter; import org.hibernate.validator.internal.util.logging.formatter.ClassObjectFormatter; import org.hibernate.validator.internal.util.logging.formatter.CollectionOfClassesObjectFormatter; import org.hibernate.validator.internal.util.logging.formatter.CollectionOfObjectsToStringFormatter; @@ -55,7 +56,7 @@ import org.hibernate.validator.internal.util.logging.formatter.ExecutableFormatter; import org.hibernate.validator.internal.util.logging.formatter.ObjectArrayFormatter; import org.hibernate.validator.internal.util.logging.formatter.TypeFormatter; -import org.hibernate.validator.internal.xml.ContainerElementTypePath; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypePath; import org.hibernate.validator.spi.scripting.ScriptEvaluationException; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; import org.hibernate.validator.spi.scripting.ScriptEvaluatorNotFoundException; @@ -379,7 +380,7 @@ RuntimeException getTryingToInstantiateAnnotationWithUnknownAttributesException( ValidationException getIsNotAConstraintValidatorClassException(@FormatWith(ClassObjectFormatter.class) Class validatorClass); @Message(id = 103, value = "%s is configured at least twice in xml.") - ValidationException getBeanClassHasAlreadyBeConfiguredInXmlException(@FormatWith(ClassObjectFormatter.class) Class beanClass); + ValidationException getBeanClassHasAlreadyBeenConfiguredInXmlException(@FormatWith(ClassObjectFormatter.class) Class beanClass); @Message(id = 104, value = "%1$s is defined twice in mapping xml for bean %2$s.") ValidationException getIsDefinedTwiceInMappingXmlForBeanException(String name, @FormatWith(ClassObjectFormatter.class) Class beanClass); @@ -468,14 +469,14 @@ ConstraintDeclarationException getMultipleGroupConversionsForSameSourceException @Message(id = 133, value = "%1$s does not contain a constructor with the parameter types %2$s.") ValidationException getBeanDoesNotContainConstructorException(@FormatWith(ClassObjectFormatter.class) Class beanClass, - @FormatWith(CollectionOfClassesObjectFormatter.class) List> parameterTypes); + @FormatWith(ArrayOfClassesObjectFormatter.class) Class[] parameterTypes); @Message(id = 134, value = "Unable to load parameter of type '%1$s' in %2$s.") ValidationException getInvalidParameterTypeException(String type, @FormatWith(ClassObjectFormatter.class) Class beanClass); @Message(id = 135, value = "%1$s does not contain a method with the name '%2$s' and parameter types %3$s.") ValidationException getBeanDoesNotContainMethodException(@FormatWith(ClassObjectFormatter.class) Class beanClass, String methodName, - @FormatWith(CollectionOfClassesObjectFormatter.class) List> parameterTypes); + @FormatWith(ArrayOfClassesObjectFormatter.class) Class[] parameterTypes); @Message(id = 136, value = "The specified constraint annotation class %1$s cannot be loaded.") ValidationException getUnableToLoadConstraintAnnotationClassException(String constraintAnnotationClassName, @Cause Exception e); @@ -605,8 +606,8 @@ ConstraintDefinitionException getValidatorForCrossParameterConstraintMustEitherV ValidationException getOverridingConstraintDefinitionsInMultipleMappingFilesException(String constraintClassName); @Message(id = 168, - value = "The message descriptor '%1$s' contains an unbalanced meta character '%2$c' parameter.") - MessageDescriptorFormatException getNonTerminatedParameterException(String messageDescriptor, char character); + value = "The message descriptor '%1$s' contains an unbalanced meta character '%2$c'.") + MessageDescriptorFormatException getUnbalancedBeginEndParameterException(String messageDescriptor, char character); @Message(id = 169, value = "The message descriptor '%1$s' has nested parameters.") @@ -841,4 +842,29 @@ ValidationException getUnableToAccessMethodException(Lookup lookup, @FormatWith( @LogMessage(level = DEBUG) @Message(id = 240, value = "Constraint validator payload set to %1$s.") void logConstraintValidatorPayload(Object payload); + + @Message(id = 241, value = "Encountered unsupported element %1$s while parsing the XML configuration.") + ValidationException logUnknownElementInXmlConfiguration(String tag); + + @LogMessage(level = WARN) + @Message(id = 242, value = "Unable to load or instantiate JPA aware resolver %1$s. All properties will per default be traversable.") + void logUnableToLoadOrInstantiateJPAAwareResolver(String traversableResolverClassName); + + @Message(id = 243, value = "Constraint %2$s references constraint validator type %1$s, but this validator is defined for constraint type %3$s.") + ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatchException( + @FormatWith(ClassObjectFormatter.class) Class> constraintValidatorImplementationType, + @FormatWith(ClassObjectFormatter.class) Class registeredConstraintAnnotationType, + @FormatWith(TypeFormatter.class) Type declaredConstraintAnnotationType); + + @Message(id = 248, value = "Unable to get an XML schema named %s.") + ValidationException unableToGetXmlSchema(String schemaResourceName); + + // Message ID is to match the one from the "future" versions of HV from which this change is cherry-picked + @LogMessage(level = WARN) + @Message(id = 257, value = "Expression variables have been defined for constraint %1$s while Expression Language is not enabled.") + void expressionVariablesDefinedWithExpressionLanguageNotEnabled(Class constraintAnnotation); + + @LogMessage(level = WARN) + @Message(id = 258, value = "Expression Language interpolation features are not explicitly enabled/disabled. Use '%1$s' environment variable, or '%2$s' system property to define the behavior. Defaulting to expression language features being disabled.") + void expressionLanguageFeaturesNoExplicitSetting(String expressionLanguageEnabledEnv, String expressionLanguageEnabledSystem); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ArrayOfClassesObjectFormatter.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ArrayOfClassesObjectFormatter.java new file mode 100644 index 0000000000..9399bf630c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/formatter/ArrayOfClassesObjectFormatter.java @@ -0,0 +1,32 @@ +/* + * 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.util.logging.formatter; + +import java.util.Arrays; +import java.util.stream.Collectors; + +/** + * Used with JBoss Logging to display array of class names in log messages. + * + * @author Guillaume Smet + * @author Marko Bekhta + */ +public class ArrayOfClassesObjectFormatter { + + private final String stringRepresentation; + + public ArrayOfClassesObjectFormatter(Class[] classes) { + this.stringRepresentation = Arrays.stream( classes ) + .map( c -> c.getName() ) + .collect( Collectors.joining( ", " ) ); + } + + @Override + public String toString() { + return stringRepresentation; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java index 9f1a626e94..79a817d456 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetClassLoader.java @@ -16,10 +16,13 @@ * @author Emmanuel Bernard */ public final class GetClassLoader implements PrivilegedAction { + + private static final GetClassLoader CONTEXT = new GetClassLoader( null ); + private final Class clazz; public static GetClassLoader fromContext() { - return new GetClassLoader( null ); + return CONTEXT; } public static GetClassLoader fromClass(Class clazz) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetEnvOrSystemVariableValue.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetEnvOrSystemVariableValue.java new file mode 100644 index 0000000000..41e45bf91a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/GetEnvOrSystemVariableValue.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.util.privilegedactions; + +import java.security.PrivilegedAction; + +/** + * Get an environment variable {@code System.getenv(..)} or if unavailable -- system property {@code System.getProperty(..)}. + */ +public final class GetEnvOrSystemVariableValue implements PrivilegedAction { + + private final String environmentVariableName; + private final String systemPropertyName; + + public static GetEnvOrSystemVariableValue action(String environmentVariableName, String systemPropertyName) { + return new GetEnvOrSystemVariableValue( environmentVariableName, systemPropertyName ); + } + + private GetEnvOrSystemVariableValue(String environmentVariableName, String systemPropertyName) { + this.environmentVariableName = environmentVariableName; + this.systemPropertyName = systemPropertyName; + } + + @Override + public String run() { + String value = System.getenv( environmentVariableName ); + if ( value == null ) { + value = System.getProperty( systemPropertyName ); + } + return value; + } +} 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 da33182ca3..2ea36ccf30 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 @@ -26,6 +26,7 @@ * @author Hardy Ferentschik * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Gunnar Morling + * @author Guillaume Smet */ public final class LoadClass implements PrivilegedAction> { @@ -37,22 +38,32 @@ public final class LoadClass implements PrivilegedAction> { private final ClassLoader classLoader; + private final ClassLoader initialThreadContextClassLoader; + /** * when true, it will check the Thread Context ClassLoader when the class is not found in the provided one */ private final boolean fallbackOnTCCL; public static LoadClass action(String className, ClassLoader classLoader) { - return new LoadClass( className, classLoader, true ); + return action( className, classLoader, true ); } public static LoadClass action(String className, ClassLoader classLoader, boolean fallbackOnTCCL) { - return new LoadClass( className, classLoader, fallbackOnTCCL ); + return new LoadClass( className, classLoader, null, fallbackOnTCCL ); + } + + /** + * in some cases, the TCCL has been overridden so we need to pass it explicitly. + */ + public static LoadClass action(String className, ClassLoader classLoader, ClassLoader initialThreadContextClassLoader) { + return new LoadClass( className, classLoader, initialThreadContextClassLoader, true ); } - private LoadClass(String className, ClassLoader classLoader, boolean fallbackOnTCCL) { + private LoadClass(String className, ClassLoader classLoader, ClassLoader initialThreadContextClassLoader, boolean fallbackOnTCCL) { this.className = className; this.classLoader = classLoader; + this.initialThreadContextClassLoader = initialThreadContextClassLoader; this.fallbackOnTCCL = fallbackOnTCCL; } @@ -80,7 +91,9 @@ private Class loadClassInValidatorNameSpace() { exception = e; } if ( fallbackOnTCCL ) { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader contextClassLoader = initialThreadContextClassLoader != null + ? initialThreadContextClassLoader + : Thread.currentThread().getContextClassLoader(); if ( contextClassLoader != null ) { try { return Class.forName( className, false, contextClassLoader ); @@ -100,20 +113,22 @@ private Class loadClassInValidatorNameSpace() { private Class loadNonValidatorClass() { Exception exception = null; - try { - if ( classLoader != null ) { + if ( classLoader != null ) { + try { return Class.forName( className, false, classLoader ); } - } - catch (ClassNotFoundException e) { - exception = e; - } - catch (RuntimeException e) { - exception = e; + catch (ClassNotFoundException e) { + exception = e; + } + catch (RuntimeException e) { + exception = e; + } } if ( fallbackOnTCCL ) { try { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader contextClassLoader = initialThreadContextClassLoader != null + ? initialThreadContextClassLoader + : Thread.currentThread().getContextClassLoader(); if ( contextClassLoader != null ) { return Class.forName( className, false, contextClassLoader ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java index 92404467a9..8951907f44 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewInstance.java @@ -7,6 +7,7 @@ package org.hibernate.validator.internal.util.privilegedactions; import java.lang.invoke.MethodHandles; +import java.lang.reflect.InvocationTargetException; import java.security.PrivilegedAction; import org.hibernate.validator.internal.util.logging.Log; @@ -37,9 +38,9 @@ private NewInstance(Class clazz, String message) { @Override public T run() { try { - return clazz.newInstance(); + return clazz.getConstructor().newInstance(); } - catch (InstantiationException e) { + catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) { throw LOG.getUnableToInstantiateException( message, clazz, e ); } catch (IllegalAccessException e) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewJaxbContext.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewJaxbContext.java deleted file mode 100644 index cb8f7e16da..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewJaxbContext.java +++ /dev/null @@ -1,35 +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.util.privilegedactions; - -import java.security.PrivilegedExceptionAction; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; - -/** - * Returns a new {@link JAXBContext} for the given class. - * - * @author Gunnar Morling - */ -public final class NewJaxbContext implements PrivilegedExceptionAction { - - private final Class clazz; - - public static NewJaxbContext action(Class clazz) { - return new NewJaxbContext( clazz ); - } - - private NewJaxbContext(Class clazz) { - this.clazz = clazz; - } - - @Override - public JAXBContext run() throws JAXBException { - return JAXBContext.newInstance( clazz ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewProxyInstance.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewProxyInstance.java new file mode 100644 index 0000000000..46dfbf2bee --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/NewProxyInstance.java @@ -0,0 +1,49 @@ +/* + * 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.util.privilegedactions; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.security.PrivilegedAction; + +/** + * Execute proxy creation as privileged action. + * + * @author Guillaume Smet + */ +public final class NewProxyInstance implements PrivilegedAction { + + private final ClassLoader classLoader; + private final Class[] interfaces; + private final InvocationHandler invocationHandler; + + public static NewProxyInstance action(ClassLoader classLoader, Class interfaze, InvocationHandler invocationHandler) { + return new NewProxyInstance( classLoader, interfaze, invocationHandler ); + } + + public static NewProxyInstance action(ClassLoader classLoader, Class[] interfaces, InvocationHandler invocationHandler) { + return new NewProxyInstance( classLoader, interfaces, invocationHandler ); + } + + private NewProxyInstance(ClassLoader classLoader, Class[] interfaces, InvocationHandler invocationHandler) { + this.classLoader = classLoader; + this.interfaces = interfaces; + this.invocationHandler = invocationHandler; + } + + private NewProxyInstance(ClassLoader classLoader, Class interfaze, InvocationHandler invocationHandler) { + this.classLoader = classLoader; + this.interfaces = new Class[] { interfaze }; + this.invocationHandler = invocationHandler; + } + + @SuppressWarnings("unchecked") + @Override + public T run() { + return (T) Proxy.newProxyInstance( classLoader, interfaces, invocationHandler ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/Unmarshal.java b/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/Unmarshal.java deleted file mode 100644 index 73073081f3..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/util/privilegedactions/Unmarshal.java +++ /dev/null @@ -1,40 +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.util.privilegedactions; - -import java.security.PrivilegedExceptionAction; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLEventReader; - -/** - * Unmarshals the given source. - * - * @author Gunnar Morling - */ -public final class Unmarshal implements PrivilegedExceptionAction> { - - private final Unmarshaller unmarshaller; - private final XMLEventReader xmlEventReader; - private final Class clazz; - - public static Unmarshal action(Unmarshaller unmarshaller, XMLEventReader xmlEventReader, Class clazz) { - return new Unmarshal( unmarshaller, xmlEventReader, clazz ); - } - - private Unmarshal(Unmarshaller unmarshaller, XMLEventReader xmlEventReader, Class clazz) { - this.unmarshaller = unmarshaller; - this.xmlEventReader = xmlEventReader; - this.clazz = clazz; - } - - @Override - public JAXBElement run() throws JAXBException { - return unmarshaller.unmarshal( xmlEventReader, clazz ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/AbstractStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/AbstractStaxBuilder.java new file mode 100644 index 0000000000..bbe5f22108 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/AbstractStaxBuilder.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.xml; + +import java.util.Optional; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +/** + * Other Stax xml builders should extend from this one. + * Provides some common functionality like reading an attribute value + * or value of a simple tag. + * + * @author Marko Bekhta + */ +public abstract class AbstractStaxBuilder { + + protected abstract String getAcceptableQName(); + + /** + * Checks if the given {@link XMLEvent} is a {@link StartElement} and if the + * corresponding xml tag can be processed based on a tag name. + * + * @param xmlEvent an event to check + * + * @return {@code true} if corresponding event can be processed by current builder, + * {@code false} otherwise + */ + protected boolean accept(XMLEvent xmlEvent) { + return xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals( getAcceptableQName() ); + } + + public boolean process(XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + if ( accept( xmlEvent ) ) { + try { + add( xmlEventReader, xmlEvent ); + } + catch (XMLStreamException e) { + throw new IllegalStateException( e ); + } + return true; + } + return false; + } + + protected abstract void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException; + + /** + * Reads a value between a simple tag element. In case of a {@code some-value} will + * return {@code some-value} as a string. + * + * @param xmlEventReader a current {@link XMLEventReader} + * + * @return a value of a current xml tag as a string + */ + protected String readSingleElement(XMLEventReader xmlEventReader) throws XMLStreamException { + // trimming the string value as it might contain leading/trailing spaces or \n + XMLEvent xmlEvent = xmlEventReader.nextEvent(); + StringBuilder stringBuilder = new StringBuilder( xmlEvent.asCharacters().getData() ); + while ( xmlEventReader.peek().isCharacters() ) { + xmlEvent = xmlEventReader.nextEvent(); + stringBuilder.append( xmlEvent.asCharacters().getData() ); + } + return stringBuilder.toString().trim(); + } + + /** + * Reads a value of an attribute of a given element. + * + * @param startElement an element to get an attribute from + * @param qName a {@link QName} of an attribute to read + * + * @return a value of an attribute if it is present, {@link Optional#empty()} otherwise + */ + protected Optional readAttribute(StartElement startElement, QName qName) { + Attribute attribute = startElement.getAttributeByName( qName ); + return Optional.ofNullable( attribute ).map( Attribute::getValue ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java b/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java index 7db4d6fc7b..e5def26adc 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/CloseIgnoringInputStream.java @@ -17,7 +17,7 @@ * * @author Guillaume Smet */ -class CloseIgnoringInputStream extends FilterInputStream { +public class CloseIgnoringInputStream extends FilterInputStream { public CloseIgnoringInputStream(InputStream in) { super( in ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedExecutableBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedExecutableBuilder.java deleted file mode 100644 index 382abe3206..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedExecutableBuilder.java +++ /dev/null @@ -1,345 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.validation.ValidationException; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; -import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -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.GetDeclaredConstructor; -import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ConstructorType; -import org.hibernate.validator.internal.xml.binding.CrossParameterType; -import org.hibernate.validator.internal.xml.binding.MethodType; -import org.hibernate.validator.internal.xml.binding.ParameterType; -import org.hibernate.validator.internal.xml.binding.ReturnValueType; - -/** - * Builder for constrained methods and constructors. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedExecutableBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final ClassLoadingHelper classLoadingHelper; - private final MetaConstraintBuilder metaConstraintBuilder; - private final GroupConversionBuilder groupConversionBuilder; - private final ConstrainedParameterBuilder constrainedParameterBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedExecutableBuilder(ClassLoadingHelper classLoadingHelper, MetaConstraintBuilder metaConstraintBuilder, - GroupConversionBuilder groupConversionBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.classLoadingHelper = classLoadingHelper; - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.constrainedParameterBuilder = new ConstrainedParameterBuilder( - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - this.annotationProcessingOptions = annotationProcessingOptions; - } - - Set buildMethodConstrainedExecutable(List methods, - Class beanClass, - String defaultPackage) { - Set constrainedExecutables = newHashSet(); - List alreadyProcessedMethods = newArrayList(); - for ( MethodType methodType : methods ) { - // parse the parameters - List> parameterTypes = createParameterTypes( - methodType.getParameter(), - beanClass, - defaultPackage - ); - - String methodName = methodType.getName(); - - final Method method = run( - GetDeclaredMethod.action( - beanClass, - methodName, - parameterTypes.toArray( new Class[parameterTypes.size()] ) - ) - ); - - if ( method == null ) { - throw LOG.getBeanDoesNotContainMethodException( - beanClass, - methodName, - parameterTypes - ); - } - - if ( alreadyProcessedMethods.contains( method ) ) { - throw LOG.getMethodIsDefinedTwiceInMappingXmlForBeanException( method, beanClass ); - } - else { - alreadyProcessedMethods.add( method ); - } - - // ignore annotations - if ( methodType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - method, - methodType.getIgnoreAnnotations() - ); - } - - ConstrainedExecutable constrainedExecutable = parseExecutableType( - defaultPackage, - methodType.getParameter(), - methodType.getCrossParameter(), - methodType.getReturnValue(), - method - ); - - constrainedExecutables.add( constrainedExecutable ); - } - return constrainedExecutables; - } - - Set buildConstructorConstrainedExecutable(List constructors, - Class beanClass, - String defaultPackage) { - Set constrainedExecutables = newHashSet(); - List> alreadyProcessedConstructors = newArrayList(); - for ( ConstructorType constructorType : constructors ) { - // parse the parameters - List> constructorParameterTypes = createParameterTypes( - constructorType.getParameter(), - beanClass, - defaultPackage - ); - - final Constructor constructor = run( - GetDeclaredConstructor.action( - beanClass, - constructorParameterTypes.toArray( new Class[constructorParameterTypes.size()] ) - ) - ); - - if ( constructor == null ) { - throw LOG.getBeanDoesNotContainConstructorException( - beanClass, - constructorParameterTypes - ); - } - if ( alreadyProcessedConstructors.contains( constructor ) ) { - throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( - constructor, - beanClass - ); - } - else { - alreadyProcessedConstructors.add( constructor ); - } - - // ignore annotations - if ( constructorType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - constructor, - constructorType.getIgnoreAnnotations() - ); - } - - ConstrainedExecutable constrainedExecutable = parseExecutableType( - defaultPackage, - constructorType.getParameter(), - constructorType.getCrossParameter(), - constructorType.getReturnValue(), - constructor - ); - constrainedExecutables.add( constrainedExecutable ); - } - return constrainedExecutables; - } - - private ConstrainedExecutable parseExecutableType(String defaultPackage, - List parameterTypeList, - CrossParameterType crossParameterType, - ReturnValueType returnValueType, - Executable executable) { - List parameterMetaData = constrainedParameterBuilder.buildConstrainedParameters( - parameterTypeList, - executable, - defaultPackage - ); - - Set> crossParameterConstraints = parseCrossParameterConstraints( - defaultPackage, - crossParameterType, - executable - ); - - // parse the return value - Set> returnValueConstraints = new HashSet<>(); - Set> returnValueTypeArgumentConstraints = new HashSet<>(); - CascadingMetaDataBuilder cascadingMetaDataBuilder = parseReturnValueType( - returnValueType, - executable, - returnValueConstraints, - returnValueTypeArgumentConstraints, - defaultPackage - ); - - return new ConstrainedExecutable( - ConfigurationSource.XML, - executable, - parameterMetaData, - crossParameterConstraints, - returnValueConstraints, - returnValueTypeArgumentConstraints, - cascadingMetaDataBuilder - ); - } - - private Set> parseCrossParameterConstraints(String defaultPackage, - CrossParameterType crossParameterType, - Executable executable) { - - Set> crossParameterConstraints = newHashSet(); - if ( crossParameterType == null ) { - return crossParameterConstraints; - } - - ConstraintLocation constraintLocation = ConstraintLocation.forCrossParameter( executable ); - - for ( ConstraintType constraintType : crossParameterType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraintType, - ExecutableHelper.getElementType( executable ), - defaultPackage, - ConstraintDescriptorImpl.ConstraintType.CROSS_PARAMETER - ); - crossParameterConstraints.add( metaConstraint ); - } - - // ignore annotations - if ( crossParameterType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsForCrossParameterConstraint( - executable, - crossParameterType.getIgnoreAnnotations() - ); - } - - return crossParameterConstraints; - } - - private CascadingMetaDataBuilder parseReturnValueType(ReturnValueType returnValueType, - Executable executable, - Set> returnValueConstraints, - Set> returnValueTypeArgumentConstraints, - String defaultPackage) { - if ( returnValueType == null ) { - return CascadingMetaDataBuilder.nonCascading(); - } - - ConstraintLocation constraintLocation = ConstraintLocation.forReturnValue( executable ); - for ( ConstraintType constraint : returnValueType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - ExecutableHelper.getElementType( executable ), - defaultPackage, - ConstraintDescriptorImpl.ConstraintType.GENERIC - ); - returnValueConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( returnValueType.getContainerElementType(), ReflectionHelper.typeOf( executable ) ); - - returnValueTypeArgumentConstraints.addAll( containerElementTypeConfiguration.getMetaConstraints() ); - - // ignore annotations - if ( returnValueType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsForReturnValue( - executable, - returnValueType.getIgnoreAnnotations() - ); - } - - return getCascadingMetaDataForReturnValue( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), executable, returnValueType, - defaultPackage ); - } - - private List> createParameterTypes(List parameterList, - Class beanClass, - String defaultPackage) { - List> parameterTypes = newArrayList(); - for ( ParameterType parameterType : parameterList ) { - String type = null; - try { - type = parameterType.getType(); - Class parameterClass = classLoadingHelper.loadClass( type, defaultPackage ); - parameterTypes.add( parameterClass ); - } - catch (ValidationException e) { - throw LOG.getInvalidParameterTypeException( type, beanClass ); - } - } - - return parameterTypes; - } - - private CascadingMetaDataBuilder getCascadingMetaDataForReturnValue(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Executable executable, - ReturnValueType returnValueType, String defaultPackage) { - Type type = ReflectionHelper.typeOf( executable ); - boolean isCascaded = returnValueType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - returnValueType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } - - /** - * 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 static T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedFieldBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedFieldBuilder.java deleted file mode 100644 index f36ed6e054..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedFieldBuilder.java +++ /dev/null @@ -1,138 +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.xml; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedField; -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.GetDeclaredField; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.FieldType; - -/** - * Builder for constraint fields. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedFieldBuilder { - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final GroupConversionBuilder groupConversionBuilder; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedFieldBuilder(MetaConstraintBuilder metaConstraintBuilder, GroupConversionBuilder groupConversionBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - } - - Set buildConstrainedFields(List fields, - Class beanClass, - String defaultPackage) { - Set constrainedFields = new HashSet<>(); - List alreadyProcessedFieldNames = new ArrayList<>(); - for ( FieldType fieldType : fields ) { - Field field = findField( beanClass, fieldType.getName(), alreadyProcessedFieldNames ); - ConstraintLocation constraintLocation = ConstraintLocation.forField( field ); - Set> metaConstraints = new HashSet<>(); - - for ( ConstraintType constraint : fieldType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - java.lang.annotation.ElementType.FIELD, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( fieldType.getContainerElementType(), ReflectionHelper.typeOf( field ) ); - - ConstrainedField constrainedField = new ConstrainedField( - ConfigurationSource.XML, - field, - metaConstraints, - containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaDataForField( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), field, fieldType, defaultPackage ) - ); - constrainedFields.add( constrainedField ); - - // ignore annotations - if ( fieldType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - field, - fieldType.getIgnoreAnnotations() - ); - } - } - - return constrainedFields; - } - - private CascadingMetaDataBuilder getCascadingMetaDataForField(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Field field, - FieldType fieldType, String defaultPackage) { - Type type = ReflectionHelper.typeOf( field ); - boolean isCascaded = fieldType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - fieldType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } - - private static Field findField(Class beanClass, String fieldName, List alreadyProcessedFieldNames) { - if ( alreadyProcessedFieldNames.contains( fieldName ) ) { - throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( fieldName, beanClass ); - } - else { - alreadyProcessedFieldNames.add( fieldName ); - } - - final Field field = run( GetDeclaredField.action( beanClass, fieldName ) ); - if ( field == null ) { - throw LOG.getBeanDoesNotContainTheFieldException( beanClass, fieldName ); - } - return field; - } - - /** - * 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 static T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedGetterBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedGetterBuilder.java deleted file mode 100644 index f78c94f4e2..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedGetterBuilder.java +++ /dev/null @@ -1,146 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; -import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -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.GetMethodFromPropertyName; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.GetterType; - -/** - * Builder for constraint getters. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedGetterBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final GroupConversionBuilder groupConversionBuilder; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedGetterBuilder(MetaConstraintBuilder metaConstraintBuilder, GroupConversionBuilder groupConversionBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - } - - Set buildConstrainedGetters(List getterList, - Class beanClass, - String defaultPackage) { - Set constrainedExecutables = newHashSet(); - List alreadyProcessedGetterNames = newArrayList(); - for ( GetterType getterType : getterList ) { - String getterName = getterType.getName(); - Method getter = findGetter( beanClass, getterName, alreadyProcessedGetterNames ); - ConstraintLocation constraintLocation = ConstraintLocation.forGetter( getter ); - - Set> metaConstraints = newHashSet(); - for ( ConstraintType constraint : getterType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - java.lang.annotation.ElementType.METHOD, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( getterType.getContainerElementType(), ReflectionHelper.typeOf( getter ) ); - - ConstrainedExecutable constrainedGetter = new ConstrainedExecutable( - ConfigurationSource.XML, - getter, - Collections.emptyList(), - Collections.>emptySet(), - metaConstraints, - containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaDataForGetter( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), getter, getterType, defaultPackage ) - ); - constrainedExecutables.add( constrainedGetter ); - - // ignore annotations - if ( getterType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( - getter, - getterType.getIgnoreAnnotations() - ); - } - } - - return constrainedExecutables; - } - - private CascadingMetaDataBuilder getCascadingMetaDataForGetter(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Method method, - GetterType getterType, String defaultPackage) { - Type type = ReflectionHelper.typeOf( method ); - boolean isCascaded = getterType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - getterType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } - - private static Method findGetter(Class beanClass, String getterName, List alreadyProcessedGetterNames) { - if ( alreadyProcessedGetterNames.contains( getterName ) ) { - throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( getterName, beanClass ); - } - else { - alreadyProcessedGetterNames.add( getterName ); - } - - final Method method = run( GetMethodFromPropertyName.action( beanClass, getterName ) ); - if ( method == null ) { - throw LOG.getBeanDoesNotContainThePropertyException( beanClass, getterName ); - } - - return method; - } - - /** - * 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 static T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedParameterBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedParameterBuilder.java deleted file mode 100644 index 576d6de700..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedParameterBuilder.java +++ /dev/null @@ -1,113 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - -import java.lang.annotation.ElementType; -import java.lang.reflect.Executable; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.xml.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ParameterType; - -/** - * Builder for constraint parameters. - * - * @author Hardy Ferentschik - * @author Guillaume Smet - */ -class ConstrainedParameterBuilder { - - private final GroupConversionBuilder groupConversionBuilder; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - - ConstrainedParameterBuilder(MetaConstraintBuilder metaConstraintBuilder, - GroupConversionBuilder groupConversionBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - } - - List buildConstrainedParameters(List parameterList, - Executable executable, - String defaultPackage) { - List constrainedParameters = newArrayList(); - int i = 0; - for ( ParameterType parameterType : parameterList ) { - ConstraintLocation constraintLocation = ConstraintLocation.forParameter( executable, i ); - Type type = ReflectionHelper.typeOf( executable, i ); - - Set> metaConstraints = new HashSet<>(); - for ( ConstraintType constraint : parameterType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - ElementType.PARAMETER, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder( - metaConstraintBuilder, groupConversionBuilder, constraintLocation, defaultPackage ); - ContainerElementTypeConfiguration containerElementTypeConfiguration = containerElementTypeConfigurationBuilder - .build( parameterType.getContainerElementType(), type ); - - // ignore annotations - if ( parameterType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreConstraintAnnotationsOnParameter( - executable, - i, - parameterType.getIgnoreAnnotations() - ); - } - - ConstrainedParameter constrainedParameter = new ConstrainedParameter( - ConfigurationSource.XML, - executable, - type, - i, - metaConstraints, - containerElementTypeConfiguration.getMetaConstraints(), - getCascadingMetaDataForParameter( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), type, parameterType, defaultPackage ) - ); - constrainedParameters.add( constrainedParameter ); - i++; - } - - return constrainedParameters; - } - - - private CascadingMetaDataBuilder getCascadingMetaDataForParameter(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Type type, - ParameterType parameterType, String defaultPackage) { - boolean isCascaded = parameterType.getValid() != null; - Map, Class> groupConversions = groupConversionBuilder.buildGroupConversionMap( - parameterType.getConvertGroup(), - defaultPackage - ); - - return CascadingMetaDataBuilder.annotatedObject( type, isCascaded, containerElementTypesCascadingMetaData, groupConversions ); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedTypeBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedTypeBuilder.java deleted file mode 100644 index 4c54ae39ad..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ConstrainedTypeBuilder.java +++ /dev/null @@ -1,99 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; -import org.hibernate.validator.internal.metadata.raw.ConstrainedType; -import org.hibernate.validator.internal.xml.binding.ClassType; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.GroupSequenceType; - -/** - * Builder for constraint types. - * - * @author Hardy Ferentschik - */ -class ConstrainedTypeBuilder { - - private final ClassLoadingHelper classLoadingHelper; - private final MetaConstraintBuilder metaConstraintBuilder; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - private final Map, List>> defaultSequences; - - public ConstrainedTypeBuilder(ClassLoadingHelper classLoadingHelper, - MetaConstraintBuilder metaConstraintBuilder, - AnnotationProcessingOptionsImpl annotationProcessingOptions, - Map, List>> defaultSequences) { - this.classLoadingHelper = classLoadingHelper; - this.metaConstraintBuilder = metaConstraintBuilder; - this.annotationProcessingOptions = annotationProcessingOptions; - this.defaultSequences = defaultSequences; - } - - ConstrainedType buildConstrainedType(ClassType classType, Class beanClass, String defaultPackage) { - if ( classType == null ) { - return null; - } - - // group sequence - List> groupSequence = createGroupSequence( classType.getGroupSequence(), defaultPackage ); - if ( !groupSequence.isEmpty() ) { - defaultSequences.put( beanClass, groupSequence ); - } - - // constraints - ConstraintLocation constraintLocation = ConstraintLocation.forClass( beanClass ); - Set> metaConstraints = newHashSet(); - for ( ConstraintType constraint : classType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - constraintLocation, - constraint, - java.lang.annotation.ElementType.TYPE, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - // ignore annotation - if ( classType.getIgnoreAnnotations() != null ) { - annotationProcessingOptions.ignoreClassLevelConstraintAnnotations( - beanClass, - classType.getIgnoreAnnotations() - ); - } - - return new ConstrainedType( - ConfigurationSource.XML, - beanClass, - metaConstraints - ); - } - - private List> createGroupSequence(GroupSequenceType groupSequenceType, String defaultPackage) { - List> groupSequence = newArrayList(); - if ( groupSequenceType != null ) { - for ( String groupName : groupSequenceType.getValue() ) { - Class group = classLoadingHelper.loadClass( groupName, defaultPackage ); - groupSequence.add( group ); - } - } - return groupSequence; - } -} - - diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypeConfigurationBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypeConfigurationBuilder.java deleted file mode 100644 index 521fbfd3c7..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypeConfigurationBuilder.java +++ /dev/null @@ -1,186 +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.xml; - -import java.lang.invoke.MethodHandles; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; -import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.internal.util.TypeHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ContainerElementTypeType; - -/** - * Builds the cascading and type argument constraints configuration from the {@link ContainerElementType} elements. - * - * @author Guillaume Smet - */ -class ContainerElementTypeConfigurationBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final ConstraintLocation rootConstraintLocation; - - private final MetaConstraintBuilder metaConstraintBuilder; - - private final GroupConversionBuilder groupConversionBuilder; - - private final String defaultPackage; - - private final Set configuredPaths = new HashSet<>(); - - ContainerElementTypeConfigurationBuilder(MetaConstraintBuilder metaConstraintBuilder, GroupConversionBuilder groupConversionBuilder, - ConstraintLocation rootConstraintLocation, String defaultPackage) { - this.metaConstraintBuilder = metaConstraintBuilder; - this.groupConversionBuilder = groupConversionBuilder; - this.rootConstraintLocation = rootConstraintLocation; - this.defaultPackage = defaultPackage; - } - - ContainerElementTypeConfiguration build(List xmlContainerElementTypes, Type enclosingType) { - return add( ContainerElementTypePath.root(), xmlContainerElementTypes, rootConstraintLocation, enclosingType ); - } - - private ContainerElementTypeConfiguration add(ContainerElementTypePath parentConstraintElementTypePath, List xmlContainerElementTypes, - ConstraintLocation parentConstraintLocation, Type enclosingType) { - if ( xmlContainerElementTypes.isEmpty() ) { - return new ContainerElementTypeConfiguration( Collections.emptySet(), Collections.emptyMap() ); - } - - // HV-1428 Container element support is disabled for arrays - if ( TypeHelper.isArray( enclosingType ) ) { - throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( enclosingType ); - } - - if ( !( enclosingType instanceof ParameterizedType ) && !TypeHelper.isArray( enclosingType ) ) { - throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( enclosingType ); - } - - Set> metaConstraints = new HashSet<>(); - Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder = - CollectionHelper.newHashMap( xmlContainerElementTypes.size() ); - - boolean isArray = TypeHelper.isArray( enclosingType ); - TypeVariable[] typeParameters = isArray ? new TypeVariable[0] : ReflectionHelper.getClassFromType( enclosingType ).getTypeParameters(); - - for ( ContainerElementTypeType xmlContainerElementType : xmlContainerElementTypes ) { - Integer typeArgumentIndex = getTypeArgumentIndex( xmlContainerElementType, typeParameters, isArray, enclosingType ); - - ContainerElementTypePath constraintElementTypePath = ContainerElementTypePath.of( parentConstraintElementTypePath, typeArgumentIndex ); - boolean configuredBefore = !configuredPaths.add( constraintElementTypePath ); - if ( configuredBefore ) { - throw LOG.getContainerElementTypeHasAlreadyBeenConfiguredViaXmlMappingConfigurationException( rootConstraintLocation, constraintElementTypePath ); - } - - TypeVariable typeParameter = getTypeParameter( typeParameters, typeArgumentIndex, isArray, enclosingType ); - Type containerElementType = getContainerElementType( enclosingType, typeArgumentIndex, isArray ); - ConstraintLocation containerElementTypeConstraintLocation = ConstraintLocation.forTypeArgument( parentConstraintLocation, typeParameter, - containerElementType ); - - for ( ConstraintType constraint : xmlContainerElementType.getConstraint() ) { - MetaConstraint metaConstraint = metaConstraintBuilder.buildMetaConstraint( - containerElementTypeConstraintLocation, - constraint, - java.lang.annotation.ElementType.TYPE_USE, - defaultPackage, - null - ); - metaConstraints.add( metaConstraint ); - } - - ContainerElementTypeConfiguration nestedContainerElementTypeConfiguration = add( constraintElementTypePath, xmlContainerElementType.getContainerElementType(), - containerElementTypeConstraintLocation, containerElementType ); - - metaConstraints.addAll( nestedContainerElementTypeConfiguration.getMetaConstraints() ); - - boolean isCascaded = xmlContainerElementType.getValid() != null; - - containerElementTypesCascadingMetaDataBuilder.put( typeParameter, new CascadingMetaDataBuilder( enclosingType, typeParameter, isCascaded, - nestedContainerElementTypeConfiguration.getTypeParametersCascadingMetaData(), - groupConversionBuilder.buildGroupConversionMap( xmlContainerElementType.getConvertGroup(), defaultPackage ) ) - ); - } - - return new ContainerElementTypeConfiguration( metaConstraints, containerElementTypesCascadingMetaDataBuilder ); - } - - private Integer getTypeArgumentIndex(ContainerElementTypeType xmlContainerElementType, TypeVariable[] typeParameters, boolean isArray, Type enclosingType) { - if ( isArray ) { - return null; - } - - Integer typeArgumentIndex = xmlContainerElementType.getTypeArgumentIndex(); - if ( typeArgumentIndex == null ) { - if ( typeParameters.length > 1 ) { - throw LOG.getNoTypeArgumentIndexIsGivenForTypeWithMultipleTypeArgumentsException( enclosingType ); - } - typeArgumentIndex = 0; - } - - return typeArgumentIndex; - } - - private TypeVariable getTypeParameter(TypeVariable[] typeParameters, Integer typeArgumentIndex, boolean isArray, Type enclosingType) { - TypeVariable typeParameter; - if ( !isArray ) { - if ( typeArgumentIndex > typeParameters.length - 1 ) { - throw LOG.getInvalidTypeArgumentIndexException( enclosingType, typeArgumentIndex ); - } - - typeParameter = typeParameters[typeArgumentIndex]; - } - else { - typeParameter = new ArrayElement( enclosingType ); - } - return typeParameter; - } - - private Type getContainerElementType(Type enclosingType, Integer typeArgumentIndex, boolean isArray) { - Type containerElementType; - if ( !isArray ) { - containerElementType = ( (ParameterizedType) enclosingType ).getActualTypeArguments()[typeArgumentIndex]; - } - else { - containerElementType = TypeHelper.getComponentType( enclosingType ); - } - return containerElementType; - } - - static class ContainerElementTypeConfiguration { - - private final Set> metaConstraints; - - private final Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder; - - private ContainerElementTypeConfiguration(Set> metaConstraints, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) { - this.metaConstraints = metaConstraints; - this.containerElementTypesCascadingMetaDataBuilder = containerElementTypesCascadingMetaData; - } - - public Set> getMetaConstraints() { - return metaConstraints; - } - - public Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetaData() { - return containerElementTypesCascadingMetaDataBuilder; - } - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/GroupConversionBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/GroupConversionBuilder.java deleted file mode 100644 index 6077edc763..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/GroupConversionBuilder.java +++ /dev/null @@ -1,57 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; - -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.Map; - -import javax.validation.groups.Default; - -import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.xml.binding.GroupConversionType; - -/** - * Builder for group conversions. - * - * @author Hardy Ferentschik - */ -class GroupConversionBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final ClassLoadingHelper classLoadingHelper; - - GroupConversionBuilder(ClassLoadingHelper classLoadingHelper) { - this.classLoadingHelper = classLoadingHelper; - } - - Map, Class> buildGroupConversionMap(List groupConversionTypes, - String defaultPackage) { - Map, Class> groupConversionMap = newHashMap(); - for ( GroupConversionType groupConversionType : groupConversionTypes ) { - Class fromClass = groupConversionType.getFrom() == null ? - Default.class : - classLoadingHelper.loadClass( groupConversionType.getFrom(), defaultPackage ); - Class toClass = classLoadingHelper.loadClass( groupConversionType.getTo(), defaultPackage ); - - if ( groupConversionMap.containsKey( fromClass ) ) { - throw LOG.getMultipleGroupConversionsForSameSourceException( - fromClass, - CollectionHelper.>asSet( groupConversionMap.get( fromClass ), toClass ) ); - } - - groupConversionMap.put( fromClass, toClass ); - } - - return groupConversionMap; - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/LocalNamespace.java b/engine/src/main/java/org/hibernate/validator/internal/xml/LocalNamespace.java deleted file mode 100644 index ffca5e1f1e..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/LocalNamespace.java +++ /dev/null @@ -1,31 +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.xml; - -/** - * Bean Validation namespaces reference. - * - * @author Guillaume Smet - */ -public enum LocalNamespace { - VALIDATION_1_CONFIGURATION("http://jboss.org/xml/ns/javax/validation/configuration"), - VALIDATION_1_MAPPING("http://jboss.org/xml/ns/javax/validation/mapping"), - - VALIDATION_2_CONFIGURATION("http://xmlns.jcp.org/xml/ns/validation/configuration"), - VALIDATION_2_MAPPING("http://xmlns.jcp.org/xml/ns/validation/mapping"); - - private String namespaceURI; - - private LocalNamespace(String namespaceURI) { - this.namespaceURI = namespaceURI; - } - - public String getNamespaceURI() { - return namespaceURI; - } - -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/MappingXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/MappingXmlParser.java deleted file mode 100644 index 271b879b53..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/MappingXmlParser.java +++ /dev/null @@ -1,428 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; -import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.lang.invoke.MethodHandles; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.validation.ConstraintValidator; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.Validator; - -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; -import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; -import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; -import org.hibernate.validator.internal.metadata.raw.ConstrainedField; -import org.hibernate.validator.internal.metadata.raw.ConstrainedType; -import org.hibernate.validator.internal.util.TypeResolutionHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.NewJaxbContext; -import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.Unmarshal; -import org.hibernate.validator.internal.xml.binding.BeanType; -import org.hibernate.validator.internal.xml.binding.ConstraintDefinitionType; -import org.hibernate.validator.internal.xml.binding.ConstraintMappingsType; -import org.hibernate.validator.internal.xml.binding.ValidatedByType; -import org.xml.sax.SAXException; - -/** - * XML parser for validation-mapping files. - * - * @author Hardy Ferentschik - */ -public class MappingXmlParser { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private final Set> processedClasses = newHashSet(); - private final ConstraintHelper constraintHelper; - private final TypeResolutionHelper typeResolutionHelper; - private final ValueExtractorManager valueExtractorManager; - private final AnnotationProcessingOptionsImpl annotationProcessingOptions; - private final Map, List>> defaultSequences; - private final Map, Set> constrainedElements; - - private final XmlParserHelper xmlParserHelper; - - private final ClassLoadingHelper classLoadingHelper; - - private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); - - private static Map getSchemasByVersion() { - Map schemasByVersion = new HashMap<>(); - - schemasByVersion.put( "1.0", "META-INF/validation-mapping-1.0.xsd" ); - schemasByVersion.put( "1.1", "META-INF/validation-mapping-1.1.xsd" ); - schemasByVersion.put( "2.0", "META-INF/validation-mapping-2.0.xsd" ); - - return schemasByVersion; - } - - public MappingXmlParser(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, - ClassLoader externalClassLoader) { - this.constraintHelper = constraintHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; - this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); - this.defaultSequences = newHashMap(); - this.constrainedElements = newHashMap(); - this.xmlParserHelper = new XmlParserHelper(); - this.classLoadingHelper = new ClassLoadingHelper( externalClassLoader ); - } - - /** - * Parses the given set of input stream representing XML constraint - * mappings. - * - * @param mappingStreams The streams to parse. Must support the mark/reset contract. - */ - public final void parse(Set mappingStreams) { - try { - // JAXBContext#newInstance() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBContext jc = run( NewJaxbContext.action( ConstraintMappingsType.class ) ); - - MetaConstraintBuilder metaConstraintBuilder = new MetaConstraintBuilder( - classLoadingHelper, - constraintHelper, - typeResolutionHelper, - valueExtractorManager - ); - GroupConversionBuilder groupConversionBuilder = new GroupConversionBuilder( classLoadingHelper ); - - ConstrainedTypeBuilder constrainedTypeBuilder = new ConstrainedTypeBuilder( - classLoadingHelper, - metaConstraintBuilder, - annotationProcessingOptions, - defaultSequences - ); - ConstrainedFieldBuilder constrainedFieldBuilder = new ConstrainedFieldBuilder( - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - ConstrainedExecutableBuilder constrainedExecutableBuilder = new ConstrainedExecutableBuilder( - classLoadingHelper, - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - ConstrainedGetterBuilder constrainedGetterBuilder = new ConstrainedGetterBuilder( - metaConstraintBuilder, - groupConversionBuilder, - annotationProcessingOptions - ); - - Set alreadyProcessedConstraintDefinitions = newHashSet(); - for ( InputStream in : mappingStreams ) { - ConstraintMappingsType mapping = unmarshal( jc, in ); - String defaultPackage = mapping.getDefaultPackage(); - - parseConstraintDefinitions( - mapping.getConstraintDefinition(), - defaultPackage, - alreadyProcessedConstraintDefinitions - ); - - for ( BeanType bean : mapping.getBean() ) { - processBeanType( - constrainedTypeBuilder, - constrainedFieldBuilder, - constrainedExecutableBuilder, - constrainedGetterBuilder, - defaultPackage, - bean - ); - } - - in.reset(); - } - } - catch (JAXBException | SAXException | IOException | XMLStreamException e) { - throw LOG.getErrorParsingMappingFileException( e ); - } - } - - private ConstraintMappingsType unmarshal(JAXBContext jc, InputStream in) throws JAXBException, XMLStreamException, IOException, SAXException { - ClassLoader previousTccl = run( GetClassLoader.fromContext() ); - - try { - run( SetContextClassLoader.action( MappingXmlParser.class.getClassLoader() ) ); - - // the InputStreams passed in parameters support mark and reset - in.mark( Integer.MAX_VALUE ); - - XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); - String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader ); - xmlEventReader.close(); - - in.reset(); - - // The validation is done first as we manipulate the XML document before pushing it to the unmarshaller - // and it might not be valid anymore as we might have switched the namespace to the latest namespace - // supported. - String schemaResourceName = getSchemaResourceName( schemaVersion ); - Schema schema = xmlParserHelper.getSchema( schemaResourceName ); - Validator validator = schema.newValidator(); - validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); - - in.reset(); - - xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); - Unmarshaller unmarshaller = jc.createUnmarshaller(); - ConstraintMappingsType mapping = getValidationConfig( xmlEventReader, unmarshaller ); - xmlEventReader.close(); - - return mapping; - } - finally { - run( SetContextClassLoader.action( previousTccl ) ); - } - } - - public final Set> getXmlConfiguredClasses() { - return processedClasses; - } - - public final AnnotationProcessingOptions getAnnotationProcessingOptions() { - return annotationProcessingOptions; - } - - public final Set getConstrainedElementsForClass(Class beanClass) { - if ( constrainedElements.containsKey( beanClass ) ) { - return constrainedElements.get( beanClass ); - } - else { - return Collections.emptySet(); - } - } - - public final List> getDefaultSequenceForClass(Class beanClass) { - return defaultSequences.get( beanClass ); - } - - private void processBeanType(ConstrainedTypeBuilder constrainedTypeBuilder, ConstrainedFieldBuilder constrainedFieldBuilder, ConstrainedExecutableBuilder constrainedExecutableBuilder, ConstrainedGetterBuilder constrainedGetterBuilder, String defaultPackage, BeanType bean) { - Class beanClass = classLoadingHelper.loadClass( bean.getClazz(), defaultPackage ); - checkClassHasNotBeenProcessed( processedClasses, beanClass ); - - // update annotation ignores - annotationProcessingOptions.ignoreAnnotationConstraintForClass( - beanClass, - bean.getIgnoreAnnotations() - ); - - ConstrainedType constrainedType = constrainedTypeBuilder.buildConstrainedType( - bean.getClassType(), - beanClass, - defaultPackage - ); - if ( constrainedType != null ) { - addConstrainedElement( beanClass, constrainedType ); - } - - Set constrainedFields = constrainedFieldBuilder.buildConstrainedFields( - bean.getField(), - beanClass, - defaultPackage - ); - addConstrainedElements( beanClass, constrainedFields ); - - Set constrainedGetters = constrainedGetterBuilder.buildConstrainedGetters( - bean.getGetter(), - beanClass, - defaultPackage - - ); - addConstrainedElements( beanClass, constrainedGetters ); - - Set constrainedConstructors = constrainedExecutableBuilder.buildConstructorConstrainedExecutable( - bean.getConstructor(), - beanClass, - defaultPackage - ); - addConstrainedElements( beanClass, constrainedConstructors ); - - Set constrainedMethods = constrainedExecutableBuilder.buildMethodConstrainedExecutable( - bean.getMethod(), - beanClass, - defaultPackage - ); - addConstrainedElements( beanClass, constrainedMethods ); - - processedClasses.add( beanClass ); - } - - @SuppressWarnings("unchecked") - private void parseConstraintDefinitions(List constraintDefinitionList, - 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() ) { - throw LOG.getIsNotAnAnnotationException( clazz ); - } - Class annotationClass = (Class) clazz; - - addValidatorDefinitions( annotationClass, defaultPackage, constraintDefinition.getValidatedBy() ); - } - } - - private void addValidatorDefinitions(Class annotationClass, String defaultPackage, - ValidatedByType validatedByType) { - List> constraintValidatorDescriptors = new ArrayList<>( validatedByType.getValue().size() ); - - for ( String validatorClassName : validatedByType.getValue() ) { - @SuppressWarnings("unchecked") - Class> validatorClass = (Class>) classLoadingHelper - .loadClass( validatorClassName, defaultPackage ); - - if ( !ConstraintValidator.class.isAssignableFrom( validatorClass ) ) { - throw LOG.getIsNotAConstraintValidatorClassException( validatorClass ); - } - - constraintValidatorDescriptors.add( ConstraintValidatorDescriptor.forClass( validatorClass ) ); - } - constraintHelper.putValidatorDescriptors( - annotationClass, - constraintValidatorDescriptors, - Boolean.TRUE.equals( validatedByType.getIncludeExistingValidators() ) - ); - } - - private void checkClassHasNotBeenProcessed(Set> processedClasses, Class beanClass) { - if ( processedClasses.contains( beanClass ) ) { - throw LOG.getBeanClassHasAlreadyBeConfiguredInXmlException( beanClass ); - } - } - - private void addConstrainedElement(Class beanClass, ConstrainedElement constrainedElement) { - if ( constrainedElements.containsKey( beanClass ) ) { - constrainedElements.get( beanClass ).add( constrainedElement ); - } - else { - Set tmpList = newHashSet(); - tmpList.add( constrainedElement ); - constrainedElements.put( beanClass, tmpList ); - } - } - - private void addConstrainedElements(Class beanClass, Set newConstrainedElements) { - if ( constrainedElements.containsKey( beanClass ) ) { - - Set existingConstrainedElements = constrainedElements.get( beanClass ); - - for ( ConstrainedElement constrainedElement : newConstrainedElements ) { - if ( existingConstrainedElements.contains( constrainedElement ) ) { - throw LOG.getConstrainedElementConfiguredMultipleTimesException( - constrainedElement.toString() - ); - } - } - - existingConstrainedElements.addAll( newConstrainedElements ); - } - else { - Set tmpSet = newHashSet(); - tmpSet.addAll( newConstrainedElements ); - constrainedElements.put( beanClass, tmpSet ); - } - } - - private ConstraintMappingsType getValidationConfig(XMLEventReader xmlEventReader, Unmarshaller unmarshaller) { - ConstraintMappingsType constraintMappings; - try { - // Unmashaller#unmarshal() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBElement root = run( - Unmarshal.action( - unmarshaller, - xmlEventReader, - ConstraintMappingsType.class - ) - ); - constraintMappings = root.getValue(); - } - catch (Exception e) { - throw LOG.getErrorParsingMappingFileException( e ); - } - return constraintMappings; - } - - private String getSchemaResourceName(String schemaVersion) { - String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); - - if ( schemaResource == null ) { - throw LOG.getUnsupportedSchemaVersionException( "constraint mapping file", schemaVersion ); - } - - return schemaResource; - } - - /** - * 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 static T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - - /** - * 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(PrivilegedExceptionAction action) throws JAXBException { - try { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - catch (JAXBException e) { - throw e; - } - catch (Exception e) { - throw LOG.getErrorParsingMappingFileException( e ); - } - } - -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/MetaConstraintBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/MetaConstraintBuilder.java deleted file mode 100644 index 7851214f3b..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/MetaConstraintBuilder.java +++ /dev/null @@ -1,327 +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.xml; - -import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList; - -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Pattern; - -import javax.validation.Payload; -import javax.validation.ValidationException; -import javax.xml.bind.JAXBElement; - -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.core.MetaConstraints; -import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; -import org.hibernate.validator.internal.metadata.location.ConstraintLocation; -import org.hibernate.validator.internal.util.TypeResolutionHelper; -import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; -import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.GetMethod; -import org.hibernate.validator.internal.xml.binding.AnnotationType; -import org.hibernate.validator.internal.xml.binding.ConstraintType; -import org.hibernate.validator.internal.xml.binding.ElementType; -import org.hibernate.validator.internal.xml.binding.GroupsType; -import org.hibernate.validator.internal.xml.binding.PayloadType; - -/** - * Build meta constraint from XML - * - * @author Hardy Ferentschik - */ -class MetaConstraintBuilder { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private static final Pattern IS_ONLY_WHITESPACE = Pattern.compile( "\\s*" ); - private static final Class[] EMPTY_CLASSES_ARRAY = new Class[0]; - - private final ClassLoadingHelper classLoadingHelper; - private final ConstraintHelper constraintHelper; - private final TypeResolutionHelper typeResolutionHelper; - private final ValueExtractorManager valueExtractorManager; - - MetaConstraintBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, - ValueExtractorManager valueExtractorManager) { - this.classLoadingHelper = classLoadingHelper; - this.constraintHelper = constraintHelper; - this.typeResolutionHelper = typeResolutionHelper; - this.valueExtractorManager = valueExtractorManager; - } - - @SuppressWarnings("unchecked") - MetaConstraint buildMetaConstraint(ConstraintLocation constraintLocation, - ConstraintType constraint, - java.lang.annotation.ElementType type, - String defaultPackage, - ConstraintDescriptorImpl.ConstraintType constraintType) { - Class annotationClass; - try { - annotationClass = (Class) classLoadingHelper.loadClass( constraint.getAnnotation(), defaultPackage ); - } - catch (ValidationException e) { - throw LOG.getUnableToLoadConstraintAnnotationClassException( constraint.getAnnotation(), e ); - } - ConstraintAnnotationDescriptor.Builder annotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( annotationClass ); - - if ( constraint.getMessage() != null ) { - annotationDescriptorBuilder.setMessage( constraint.getMessage() ); - } - annotationDescriptorBuilder.setGroups( getGroups( constraint.getGroups(), defaultPackage ) ) - .setPayload( getPayload( constraint.getPayload(), defaultPackage ) ); - - for ( ElementType elementType : constraint.getElement() ) { - String name = elementType.getName(); - checkNameIsValid( name ); - Class returnType = getAnnotationParameterType( annotationClass, name ); - Object elementValue = getElementValue( elementType, returnType, defaultPackage ); - annotationDescriptorBuilder.setAttribute( name, elementValue ); - } - - ConstraintAnnotationDescriptor annotationDescriptor; - try { - annotationDescriptor = annotationDescriptorBuilder.build(); - } - catch (RuntimeException e) { - throw LOG.getUnableToCreateAnnotationForConfiguredConstraintException( e ); - } - - // we set initially ConstraintOrigin.DEFINED_LOCALLY for all xml configured constraints - // later we will make copies of this constraint descriptor when needed and adjust the ConstraintOrigin - ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( - constraintHelper, constraintLocation.getMember(), annotationDescriptor, type, constraintType - ); - - return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, constraintLocation ); - } - - private Annotation buildAnnotation(AnnotationType annotationType, Class returnType, String defaultPackage) { - AnnotationDescriptor.Builder annotationDescriptorBuilder = new AnnotationDescriptor.Builder<>( returnType ); - for ( ElementType elementType : annotationType.getElement() ) { - String name = elementType.getName(); - Class parameterType = getAnnotationParameterType( returnType, name ); - Object elementValue = getElementValue( elementType, parameterType, defaultPackage ); - annotationDescriptorBuilder.setAttribute( name, elementValue ); - } - return annotationDescriptorBuilder.build().getAnnotation(); - } - - private static void checkNameIsValid(String name) { - if ( ConstraintHelper.MESSAGE.equals( name ) || ConstraintHelper.GROUPS.equals( name ) ) { - throw LOG.getReservedParameterNamesException( ConstraintHelper.MESSAGE, ConstraintHelper.GROUPS, ConstraintHelper.PAYLOAD ); - } - } - - private static Class getAnnotationParameterType(Class annotationClass, String name) { - Method m = run( GetMethod.action( annotationClass, name ) ); - if ( m == null ) { - throw LOG.getAnnotationDoesNotContainAParameterException( annotationClass, name ); - } - return m.getReturnType(); - } - - private Object getElementValue(ElementType elementType, Class returnType, String defaultPackage) { - removeEmptyContentElements( elementType ); - - boolean isArray = returnType.isArray(); - if ( !isArray ) { - if ( elementType.getContent().size() == 0 ) { - if ( returnType == String.class ) { - return ""; - } - else { - throw LOG.getEmptyElementOnlySupportedWhenCharSequenceIsExpectedExpection(); - } - } - else if ( elementType.getContent().size() > 1 ) { - throw LOG.getAttemptToSpecifyAnArrayWhereSingleValueIsExpectedException(); - } - return getSingleValue( elementType.getContent().get( 0 ), returnType, defaultPackage ); - } - else { - List values = newArrayList(); - for ( Serializable s : elementType.getContent() ) { - values.add( getSingleValue( s, returnType.getComponentType(), defaultPackage ) ); - } - return values.toArray( (Object[]) Array.newInstance( returnType.getComponentType(), values.size() ) ); - } - } - - private static void removeEmptyContentElements(ElementType elementType) { - for ( Iterator contentIterator = elementType.getContent().iterator(); contentIterator.hasNext(); ) { - Serializable content = contentIterator.next(); - if ( content instanceof String && IS_ONLY_WHITESPACE.matcher( (String) content ).matches() ) { - contentIterator.remove(); - } - } - } - - private Object getSingleValue(Serializable serializable, Class returnType, String defaultPackage) { - - Object returnValue; - if ( serializable instanceof String ) { - String value = (String) serializable; - returnValue = convertStringToReturnType( returnType, value, defaultPackage ); - } - else if ( serializable instanceof JAXBElement && ( (JAXBElement) serializable ).getDeclaredType() - .equals( String.class ) ) { - JAXBElement elem = (JAXBElement) serializable; - String value = (String) elem.getValue(); - returnValue = convertStringToReturnType( returnType, value, defaultPackage ); - } - else if ( serializable instanceof JAXBElement && ( (JAXBElement) serializable ).getDeclaredType() - .equals( AnnotationType.class ) ) { - JAXBElement elem = (JAXBElement) serializable; - AnnotationType annotationType = (AnnotationType) elem.getValue(); - try { - @SuppressWarnings("unchecked") - Class annotationClass = (Class) returnType; - returnValue = buildAnnotation( annotationType, annotationClass, defaultPackage ); - } - catch (ClassCastException e) { - throw LOG.getUnexpectedParameterValueException( e ); - } - } - else { - throw LOG.getUnexpectedParameterValueException(); - } - return returnValue; - - } - - private Object convertStringToReturnType(Class returnType, String value, String defaultPackage) { - Object returnValue; - if ( returnType == byte.class ) { - try { - returnValue = Byte.parseByte( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "byte", e ); - } - } - else if ( returnType == short.class ) { - try { - returnValue = Short.parseShort( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "short", e ); - } - } - else if ( returnType == int.class ) { - try { - returnValue = Integer.parseInt( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "int", e ); - } - } - else if ( returnType == long.class ) { - try { - returnValue = Long.parseLong( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "long", e ); - } - } - else if ( returnType == float.class ) { - try { - returnValue = Float.parseFloat( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "float", e ); - } - } - else if ( returnType == double.class ) { - try { - returnValue = Double.parseDouble( value ); - } - catch (NumberFormatException e) { - throw LOG.getInvalidNumberFormatException( "double", e ); - } - } - else if ( returnType == boolean.class ) { - returnValue = Boolean.parseBoolean( value ); - } - else if ( returnType == char.class ) { - if ( value.length() != 1 ) { - throw LOG.getInvalidCharValueException( value ); - } - returnValue = value.charAt( 0 ); - } - else if ( returnType == String.class ) { - returnValue = value; - } - else if ( returnType == Class.class ) { - returnValue = classLoadingHelper.loadClass( value, defaultPackage ); - } - else { - try { - @SuppressWarnings("unchecked") - Class enumClass = (Class) returnType; - returnValue = Enum.valueOf( enumClass, value ); - } - catch (ClassCastException e) { - throw LOG.getInvalidReturnTypeException( returnType, e ); - } - } - return returnValue; - } - - private Class[] getGroups(GroupsType groupsType, String defaultPackage) { - if ( groupsType == null ) { - return EMPTY_CLASSES_ARRAY; - } - - List> groupList = newArrayList(); - for ( String groupClass : groupsType.getValue() ) { - groupList.add( classLoadingHelper.loadClass( groupClass, defaultPackage ) ); - } - return groupList.toArray( new Class[groupList.size()] ); - } - - @SuppressWarnings("unchecked") - private Class[] getPayload(PayloadType payloadType, String defaultPackage) { - if ( payloadType == null ) { - return EMPTY_CLASSES_ARRAY; - } - - List> payloadList = newArrayList(); - for ( String groupClass : payloadType.getValue() ) { - Class payload = classLoadingHelper.loadClass( groupClass, defaultPackage ); - if ( !Payload.class.isAssignableFrom( payload ) ) { - throw LOG.getWrongPayloadClassException( payload ); - } - else { - payloadList.add( (Class) payload ); - } - } - return payloadList.toArray( new Class[payloadList.size()] ); - } - - /** - * 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 static T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/NamespaceNormalizingXMLEventReaderDelegate.java b/engine/src/main/java/org/hibernate/validator/internal/xml/NamespaceNormalizingXMLEventReaderDelegate.java deleted file mode 100644 index 99215486e6..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/NamespaceNormalizingXMLEventReaderDelegate.java +++ /dev/null @@ -1,101 +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.xml; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventFactory; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.Namespace; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import javax.xml.stream.util.EventReaderDelegate; - -/** - * An XML {@link EventReaderDelegate} designed to normalize the XML namespaces. - *

    - * From BV 1.x to BV 2, we changed the namespaces and we need to normalize the namespaces to the ones of BV 2 so that - * the unmarshaller can do its job. - *

    - * Note: it used to work in JDK 1.8 before the 102 release but the JDK is now stricter: - * https://bugs.openjdk.java.net/browse/JDK-8134111. - * - * @author Guillaume Smet - */ -public class NamespaceNormalizingXMLEventReaderDelegate extends EventReaderDelegate { - - private final XMLEventFactory eventFactory; - - private final Map namespaceMapping; - - public NamespaceNormalizingXMLEventReaderDelegate(XMLEventReader eventReader, XMLEventFactory eventFactory, Map namespaceMapping) { - super( eventReader ); - this.eventFactory = eventFactory; - this.namespaceMapping = namespaceMapping; - } - - @Override - public XMLEvent peek() throws XMLStreamException { - return normalizeXMLEvent( super.peek() ); - } - - @Override - public XMLEvent nextEvent() throws XMLStreamException { - return normalizeXMLEvent( super.nextEvent() ); - } - - private XMLEvent normalizeXMLEvent(XMLEvent xmlEvent) { - if ( xmlEvent.isStartElement() ) { - return normalizeNamespace( xmlEvent.asStartElement() ); - } - else if ( xmlEvent.isEndElement() ) { - return normalizeNamespace( xmlEvent.asEndElement() ); - } - else { - return xmlEvent; - } - } - - @SuppressWarnings("unchecked") - private StartElement normalizeNamespace(StartElement element) { - eventFactory.setLocation( element.getLocation() ); - return eventFactory.createStartElement( normalizeQName( element.getName() ), element.getAttributes(), normalizeNamespaces( element.getNamespaces() ) ); - } - - @SuppressWarnings("unchecked") - private EndElement normalizeNamespace(EndElement element) { - eventFactory.setLocation( element.getLocation() ); - return eventFactory.createEndElement( normalizeQName( element.getName() ), normalizeNamespaces( element.getNamespaces() ) ); - } - - private QName normalizeQName(QName qName) { - return new QName( normalizeNamespaceURI( qName.getNamespaceURI() ), qName.getLocalPart() ); - } - - private Iterator normalizeNamespaces(Iterator namespaces) { - List newNamespaces = new ArrayList<>(); - while ( namespaces.hasNext() ) { - newNamespaces.add( normalizeNamespace( namespaces.next() ) ); - } - return newNamespaces.iterator(); - } - - private Namespace normalizeNamespace(Namespace namespace) { - return eventFactory.createNamespace( namespace.getPrefix(), normalizeNamespaceURI( namespace.getNamespaceURI() ) ); - } - - private String normalizeNamespaceURI(String namespaceURI) { - return namespaceMapping.getOrDefault( namespaceURI, namespaceURI ); - } - -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationXmlParser.java deleted file mode 100644 index 8d7dd550b0..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationXmlParser.java +++ /dev/null @@ -1,274 +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.xml; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.invoke.MethodHandles; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.validation.BootstrapConfiguration; -import javax.validation.executable.ExecutableType; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.Validator; - -import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.internal.util.CollectionHelper; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; -import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.NewJaxbContext; -import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; -import org.hibernate.validator.internal.util.privilegedactions.Unmarshal; -import org.hibernate.validator.internal.xml.binding.DefaultValidatedExecutableTypesType; -import org.hibernate.validator.internal.xml.binding.ExecutableValidationType; -import org.hibernate.validator.internal.xml.binding.PropertyType; -import org.hibernate.validator.internal.xml.binding.ValidationConfigType; - -import org.xml.sax.SAXException; - -/** - * Parser for validation.xml using JAXB. - * - * @author Hardy Ferentschik - * @author Gunnar Morling - */ -public class ValidationXmlParser { - - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - - private static final String VALIDATION_XML_FILE = "META-INF/validation.xml"; - private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); - - private final ClassLoader externalClassLoader; - - private static Map getSchemasByVersion() { - Map schemasByVersion = CollectionHelper.newHashMap( 3 ); - - schemasByVersion.put( "1.0", "META-INF/validation-configuration-1.0.xsd" ); - schemasByVersion.put( "1.1", "META-INF/validation-configuration-1.1.xsd" ); - schemasByVersion.put( "2.0", "META-INF/validation-configuration-2.0.xsd" ); - - return schemasByVersion; - } - - public ValidationXmlParser(ClassLoader externalClassLoader) { - this.externalClassLoader = externalClassLoader; - } - - /** - * Tries to check whether a validation.xml file exists and parses it. - * - * @return The parameters parsed out of validation.xml wrapped in an instance of {@code ConfigurationImpl.ValidationBootstrapParameters}. - */ - public final BootstrapConfiguration parseValidationXml() { - InputStream in = getValidationXmlInputStream(); - if ( in == null ) { - return BootstrapConfigurationImpl.getDefaultBootstrapConfiguration(); - } - - ClassLoader previousTccl = run( GetClassLoader.fromContext() ); - - try { - run( SetContextClassLoader.action( ValidationXmlParser.class.getClassLoader() ) ); - - // HV-970 The parser helper is only loaded if there actually is a validation.xml file; - // this avoids accessing javax.xml.stream.* (which does not exist on Android) when not actually - // working with the XML configuration - XmlParserHelper xmlParserHelper = new XmlParserHelper(); - - // the InputStream supports mark and reset - in.mark( Integer.MAX_VALUE ); - - XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); - String schemaVersion = xmlParserHelper.getSchemaVersion( VALIDATION_XML_FILE, xmlEventReader ); - xmlEventReader.close(); - - in.reset(); - - // The validation is done first as we manipulate the XML document before pushing it to the unmarshaller - // and it might not be valid anymore as we might have switched the namespace to the latest namespace - // supported. - Schema schema = getSchema( xmlParserHelper, schemaVersion ); - Validator validator = schema.newValidator(); - validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); - - in.reset(); - - xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); - ValidationConfigType validationConfig = unmarshal( xmlEventReader ); - xmlEventReader.close(); - - return createBootstrapConfiguration( validationConfig ); - } - catch (XMLStreamException | IOException | SAXException e) { - throw LOG.getUnableToParseValidationXmlFileException( VALIDATION_XML_FILE, e ); - } - finally { - run( SetContextClassLoader.action( previousTccl ) ); - closeStream( in ); - } - } - - private InputStream getValidationXmlInputStream() { - LOG.debugf( "Trying to load %s for XML based Validator configuration.", VALIDATION_XML_FILE ); - InputStream inputStream = ResourceLoaderHelper.getResettableInputStreamForPath( VALIDATION_XML_FILE, externalClassLoader ); - - if ( inputStream != null ) { - return inputStream; - } - else { - LOG.debugf( "No %s found. Using annotation based configuration only.", VALIDATION_XML_FILE ); - return null; - } - } - - private Schema getSchema(XmlParserHelper xmlParserHelper, String schemaVersion) { - String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); - - if ( schemaResource == null ) { - throw LOG.getUnsupportedSchemaVersionException( VALIDATION_XML_FILE, schemaVersion ); - } - - return xmlParserHelper.getSchema( schemaResource ); - } - - private ValidationConfigType unmarshal(XMLEventReader xmlEventReader) { - LOG.parsingXMLFile( VALIDATION_XML_FILE ); - - try { - // JAXBContext#newInstance() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBContext jc = run( NewJaxbContext.action( ValidationConfigType.class ) ); - Unmarshaller unmarshaller = jc.createUnmarshaller(); - - // Unmashaller#unmarshal() requires several permissions internally and doesn't use any privileged blocks - // itself; Wrapping it here avoids that all calling code bases need to have these permissions as well - JAXBElement root = run( Unmarshal.action( unmarshaller, xmlEventReader, ValidationConfigType.class ) ); - return root.getValue(); - } - catch (Exception e) { - throw LOG.getUnableToParseValidationXmlFileException( VALIDATION_XML_FILE, e ); - } - } - - private void closeStream(InputStream inputStream) { - try { - inputStream.close(); - } - catch (IOException io) { - LOG.unableToCloseXMLFileInputStream( VALIDATION_XML_FILE ); - } - } - - private BootstrapConfiguration createBootstrapConfiguration(ValidationConfigType config) { - Map properties = new HashMap<>(); - for ( PropertyType property : config.getProperty() ) { - if ( LOG.isDebugEnabled() ) { - LOG.debugf( - "Found property '%s' with value '%s' in validation.xml.", - property.getName(), - property.getValue() - ); - } - properties.put( property.getName(), property.getValue() ); - } - - ExecutableValidationType executableValidationType = config.getExecutableValidation(); - EnumSet defaultValidatedExecutableTypes = executableValidationType == null - ? getValidatedExecutableTypes( null ) - : getValidatedExecutableTypes( executableValidationType.getDefaultValidatedExecutableTypes() ); - boolean executableValidationEnabled = executableValidationType == null || executableValidationType.getEnabled(); - - return new BootstrapConfigurationImpl( - config.getDefaultProvider(), - config.getConstraintValidatorFactory(), - config.getMessageInterpolator(), - config.getTraversableResolver(), - config.getParameterNameProvider(), - config.getClockProvider(), - getScriptEvaluatorFactoryClassProperty( config.getProperty() ), - getValueExtractorClassNames( config ), - defaultValidatedExecutableTypes, - executableValidationEnabled, - new HashSet<>( config.getConstraintMapping() ), - properties - ); - } - - private String getScriptEvaluatorFactoryClassProperty(List properties) { - return properties.stream() - .filter( property -> HibernateValidatorConfiguration.SCRIPT_EVALUATOR_FACTORY_CLASSNAME.equals( property.getName() ) ) - .map( PropertyType::getValue ) - .findFirst().orElse( null ); - } - - private Set getValueExtractorClassNames(ValidationConfigType config) { - Set valueExtractorClassNames = CollectionHelper.newHashSet( config.getValueExtractor().size() ); - for ( String className : config.getValueExtractor() ) { - if ( !valueExtractorClassNames.add( className ) ) { - throw LOG.getDuplicateDefinitionsOfValueExtractorException( className ); - } - } - return valueExtractorClassNames; - } - - /** - * Returns an enum set with the executable types corresponding to the given - * XML configuration, considering the special elements - * {@link ExecutableType#ALL} and {@link ExecutableType#NONE}. - * - * @param validatedExecutables Schema type with executable types. - * - * @return An enum set representing the given executable types. - */ - private EnumSet getValidatedExecutableTypes(DefaultValidatedExecutableTypesType validatedExecutables) { - if ( validatedExecutables == null ) { - return null; - } - - EnumSet executableTypes = EnumSet.noneOf( ExecutableType.class ); - executableTypes.addAll( validatedExecutables.getExecutableType() ); - - return executableTypes; - } - - /** - * 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 static T run(PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } - - /** - * 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(PrivilegedExceptionAction action) throws Exception { - return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java index a2001c4c03..511da201f6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/XmlParserHelper.java @@ -14,14 +14,10 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; @@ -54,29 +50,16 @@ public class XmlParserHelper { */ private static final int NUMBER_OF_SCHEMAS = 4; private static final String DEFAULT_VERSION = "1.0"; - - private static final Map NAMESPACE_NORMALIZATION_MAPPING; + private static final QName VERSION_QNAME = new QName( "version" ); // xmlInputFactory used to be static in order to cache the factory, but that introduced a leakage of // class loader in WildFly. See HV-842 private final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); - // xmlEventFactory should not be static either. - private final XMLEventFactory xmlEventFactory = XMLEventFactory.newInstance(); - private static final ConcurrentMap schemaCache = new ConcurrentHashMap( NUMBER_OF_SCHEMAS ); - static { - Map namespaceNormalizationMapping = new HashMap<>(); - namespaceNormalizationMapping.put( LocalNamespace.VALIDATION_1_CONFIGURATION.getNamespaceURI(), - LocalNamespace.VALIDATION_2_CONFIGURATION.getNamespaceURI() ); - namespaceNormalizationMapping.put( LocalNamespace.VALIDATION_1_MAPPING.getNamespaceURI(), - LocalNamespace.VALIDATION_2_MAPPING.getNamespaceURI() ); - NAMESPACE_NORMALIZATION_MAPPING = Collections.unmodifiableMap( namespaceNormalizationMapping ); - } - /** * Retrieves the schema version applying for the given XML input stream as * represented by the "version" attribute of the root element of the stream. @@ -105,11 +88,7 @@ public String getSchemaVersion(String resourceName, XMLEventReader xmlEventReade public synchronized XMLEventReader createXmlEventReader(String resourceName, InputStream xmlStream) { try { - return new NamespaceNormalizingXMLEventReaderDelegate( - xmlInputFactory.createXMLEventReader( xmlStream ), - xmlEventFactory, - NAMESPACE_NORMALIZATION_MAPPING - ); + return xmlInputFactory.createXMLEventReader( xmlStream ); } catch (Exception e) { throw LOG.getUnableToCreateXMLEventReader( resourceName, e ); @@ -121,7 +100,7 @@ private String getVersionValue(StartElement startElement) { return null; } - Attribute versionAttribute = startElement.getAttributeByName( new QName( "version" ) ); + Attribute versionAttribute = startElement.getAttributeByName( VERSION_QNAME ); return versionAttribute != null ? versionAttribute.getValue() : DEFAULT_VERSION; } @@ -143,7 +122,7 @@ private StartElement getRootElement(XMLEventReader xmlEventReader) throws XMLStr * @return the schema identified by the given resource name or {@code null} if the resource was not found or could * not be loaded. */ - Schema getSchema(String schemaResource) { + public Schema getSchema(String schemaResource) { Schema schema = schemaCache.get( schemaResource ); if ( schema != null ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/BootstrapConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/BootstrapConfigurationImpl.java similarity index 90% rename from engine/src/main/java/org/hibernate/validator/internal/xml/BootstrapConfigurationImpl.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/config/BootstrapConfigurationImpl.java index 0c6f03a914..f29c16f3b5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/BootstrapConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/BootstrapConfigurationImpl.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.xml; +package org.hibernate.validator.internal.xml.config; import java.util.Collections; import java.util.EnumSet; @@ -16,6 +16,7 @@ import javax.validation.BootstrapConfiguration; import javax.validation.executable.ExecutableType; +import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; /** @@ -23,7 +24,7 @@ * * @author Hardy Ferentschik */ -public class BootstrapConfigurationImpl implements BootstrapConfiguration { +class BootstrapConfigurationImpl implements BootstrapConfiguration { /** * The executable types validated by default. @@ -57,7 +58,6 @@ public class BootstrapConfigurationImpl implements BootstrapConfiguration { private final String traversableResolverClassName; private final String parameterNameProviderClassName; private final String clockProviderClassName; - private final String scriptEvaluatorFactoryClassName; private final Set valueExtractorClassNames; private final Set constraintMappingResourcePaths; private final Map properties; @@ -71,7 +71,6 @@ private BootstrapConfigurationImpl() { this.traversableResolverClassName = null; this.parameterNameProviderClassName = null; this.clockProviderClassName = null; - this.scriptEvaluatorFactoryClassName = null; this.valueExtractorClassNames = new HashSet<>(); this.validatedExecutableTypes = DEFAULT_VALIDATED_EXECUTABLE_TYPES; this.isExecutableValidationEnabled = true; @@ -85,7 +84,6 @@ public BootstrapConfigurationImpl(String defaultProviderClassName, String traversableResolverClassName, String parameterNameProviderClassName, String clockProviderClassName, - String scriptEvaluatorFactoryClassName, Set valueExtractorClassNames, EnumSet validatedExecutableTypes, boolean isExecutableValidationEnabled, @@ -97,7 +95,6 @@ public BootstrapConfigurationImpl(String defaultProviderClassName, this.traversableResolverClassName = traversableResolverClassName; this.parameterNameProviderClassName = parameterNameProviderClassName; this.clockProviderClassName = clockProviderClassName; - this.scriptEvaluatorFactoryClassName = scriptEvaluatorFactoryClassName; this.valueExtractorClassNames = valueExtractorClassNames; this.validatedExecutableTypes = prepareValidatedExecutableTypes( validatedExecutableTypes ); this.isExecutableValidationEnabled = isExecutableValidationEnabled; @@ -113,15 +110,23 @@ private Set prepareValidatedExecutableTypes(EnumSet preparedValidatedExecutableTypes = EnumSet.copyOf( validatedExecutableTypes ); + preparedValidatedExecutableTypes.remove( ExecutableType.NONE ); + return CollectionHelper.toImmutableSet( preparedValidatedExecutableTypes ); + } } + + return CollectionHelper.toImmutableSet( validatedExecutableTypes ); } @Override @@ -154,10 +159,6 @@ public String getClockProviderClassName() { return clockProviderClassName; } - public String getScriptEvaluatorFactoryClassName() { - return scriptEvaluatorFactoryClassName; - } - @Override public Set getValueExtractorClassNames() { return new HashSet<>( valueExtractorClassNames ); @@ -195,7 +196,6 @@ public String toString() { sb.append( ", traversableResolverClassName='" ).append( traversableResolverClassName ).append( '\'' ); sb.append( ", parameterNameProviderClassName='" ).append( parameterNameProviderClassName ).append( '\'' ); sb.append( ", clockProviderClassName='" ).append( clockProviderClassName ).append( '\'' ); - sb.append( ", scriptEvaluatorFactoryClassName='" ).append( scriptEvaluatorFactoryClassName ).append( '\'' ); sb.append( ", validatedExecutableTypes='" ).append( validatedExecutableTypes ).append( '\'' ); sb.append( ", constraintMappingResourcePaths=" ).append( constraintMappingResourcePaths ).append( '\'' ); sb.append( ", properties=" ).append( properties ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ResourceLoaderHelper.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ResourceLoaderHelper.java similarity index 98% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ResourceLoaderHelper.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/config/ResourceLoaderHelper.java index db36ca736a..442113cc00 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ResourceLoaderHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ResourceLoaderHelper.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.xml; +package org.hibernate.validator.internal.xml.config; import java.io.BufferedInputStream; import java.io.InputStream; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationBootstrapParameters.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationBootstrapParameters.java similarity index 99% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ValidationBootstrapParameters.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationBootstrapParameters.java index 4460711659..47925728c7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ValidationBootstrapParameters.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationBootstrapParameters.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.xml; +package org.hibernate.validator.internal.xml.config; import java.io.InputStream; import java.lang.invoke.MethodHandles; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationConfigStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationConfigStaxBuilder.java new file mode 100644 index 0000000000..8ea287b9ee --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationConfigStaxBuilder.java @@ -0,0 +1,305 @@ +/* + * 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.xml.config; + +import java.lang.invoke.MethodHandles; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import javax.validation.BootstrapConfiguration; +import javax.validation.executable.ExecutableType; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Stax parser/builder for {@code validation.xml} that reads {@code } + * information and creates {@link BootstrapConfiguration}. + * + * @author Marko Bekhta + */ +class ValidationConfigStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String VALIDATION_CONFIG_QNAME = "validation-config"; + + private final SimpleConfigurationsStaxBuilder simpleConfigurationsStaxBuilder = new SimpleConfigurationsStaxBuilder(); + private final PropertyStaxBuilder propertyStaxBuilder = new PropertyStaxBuilder(); + private final ValueExtractorsStaxBuilder valueExtractorsStaxBuilder = new ValueExtractorsStaxBuilder(); + private final ConstraintMappingsStaxBuilder constraintMappingsStaxBuilder = new ConstraintMappingsStaxBuilder(); + private final ExecutableValidationStaxBuilder executableValidationStaxBuilder = new ExecutableValidationStaxBuilder(); + + private final Map builders; + + public ValidationConfigStaxBuilder(XMLEventReader xmlEventReader) throws XMLStreamException { + builders = new HashMap<>(); + builders.put( propertyStaxBuilder.getAcceptableQName(), propertyStaxBuilder ); + builders.put( valueExtractorsStaxBuilder.getAcceptableQName(), valueExtractorsStaxBuilder ); + builders.put( constraintMappingsStaxBuilder.getAcceptableQName(), constraintMappingsStaxBuilder ); + builders.put( executableValidationStaxBuilder.getAcceptableQName(), executableValidationStaxBuilder ); + for ( String name : SimpleConfigurationsStaxBuilder.getProcessedElementNames() ) { + builders.put( name, simpleConfigurationsStaxBuilder ); + } + + while ( xmlEventReader.hasNext() ) { + process( xmlEventReader, xmlEventReader.nextEvent() ); + } + } + + @Override + protected String getAcceptableQName() { + return VALIDATION_CONFIG_QNAME; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( VALIDATION_CONFIG_QNAME ) ) ) { + XMLEvent currentEvent = xmlEventReader.nextEvent(); + xmlEvent = currentEvent; + if ( currentEvent.isStartElement() ) { + StartElement startElement = currentEvent.asStartElement(); + String localPart = startElement.getName().getLocalPart(); + AbstractStaxBuilder builder = builders.get( localPart ); + if ( builder != null ) { + builder.process( xmlEventReader, xmlEvent ); + } + else { + LOG.logUnknownElementInXmlConfiguration( localPart ); + } + } + } + } + + public BootstrapConfiguration build() { + Map properties = propertyStaxBuilder.build(); + return new BootstrapConfigurationImpl( + simpleConfigurationsStaxBuilder.getDefaultProvider(), + simpleConfigurationsStaxBuilder.getConstraintValidatorFactory(), + simpleConfigurationsStaxBuilder.getMessageInterpolator(), + simpleConfigurationsStaxBuilder.getTraversableResolver(), + simpleConfigurationsStaxBuilder.getParameterNameProvider(), + simpleConfigurationsStaxBuilder.getClockProvider(), + valueExtractorsStaxBuilder.build(), + executableValidationStaxBuilder.build(), + executableValidationStaxBuilder.isEnabled(), + constraintMappingsStaxBuilder.build(), + properties + ); + } + + private static class SimpleConfigurationsStaxBuilder extends AbstractStaxBuilder { + + /** + * Single occurrence elements: + */ + private static final String DEFAULT_PROVIDER = "default-provider"; + private static final String MESSAGE_INTERPOLATOR = "message-interpolator"; + private static final String TRAVERSABLE_RESOLVER = "traversable-resolver"; + private static final String CONSTRAINT_VALIDATOR_FACTORY = "constraint-validator-factory"; + private static final String PARAMETER_NAME_PROVIDER = "parameter-name-provider"; + private static final String CLOCK_PROVIDER = "clock-provider"; + + private static final Set SINGLE_ELEMENTS = CollectionHelper.toImmutableSet( + CollectionHelper.asSet( + DEFAULT_PROVIDER, MESSAGE_INTERPOLATOR, TRAVERSABLE_RESOLVER, + CONSTRAINT_VALIDATOR_FACTORY, PARAMETER_NAME_PROVIDER, CLOCK_PROVIDER + ) + ); + + /** + * Map that contains any of the {@link this#SINGLE_ELEMENTS} elements. + */ + private final Map singleValuedElements = new HashMap<>(); + + @Override + protected String getAcceptableQName() { + throw new UnsupportedOperationException( "this method shouldn't be called" ); + } + + @Override + protected boolean accept(XMLEvent xmlEvent) { + return xmlEvent.isStartElement() + && SINGLE_ELEMENTS.contains( xmlEvent.asStartElement().getName().getLocalPart() ); + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String localPart = xmlEvent.asStartElement().getName().getLocalPart(); + singleValuedElements.put( localPart, readSingleElement( xmlEventReader ) ); + } + + public String getDefaultProvider() { + return singleValuedElements.get( DEFAULT_PROVIDER ); + } + + public String getMessageInterpolator() { + return singleValuedElements.get( MESSAGE_INTERPOLATOR ); + } + + public String getTraversableResolver() { + return singleValuedElements.get( TRAVERSABLE_RESOLVER ); + } + + public String getClockProvider() { + return singleValuedElements.get( CLOCK_PROVIDER ); + } + + public String getConstraintValidatorFactory() { + return singleValuedElements.get( CONSTRAINT_VALIDATOR_FACTORY ); + } + + public String getParameterNameProvider() { + return singleValuedElements.get( PARAMETER_NAME_PROVIDER ); + } + + public static Set getProcessedElementNames() { + return SINGLE_ELEMENTS; + } + } + + private static class PropertyStaxBuilder extends AbstractStaxBuilder { + + private static final String PROPERTY_QNAME_LOCAL_PART = "property"; + private static final QName NAME_QNAME = new QName( "name" ); + + private final Map properties; + + private PropertyStaxBuilder() { + properties = new HashMap<>(); + } + + @Override + protected String getAcceptableQName() { + return PROPERTY_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + StartElement startElement = xmlEvent.asStartElement(); + String name = readAttribute( startElement, NAME_QNAME ).get(); + String value = readSingleElement( xmlEventReader ); + if ( LOG.isDebugEnabled() ) { + LOG.debugf( + "Found property '%s' with value '%s' in validation.xml.", + name, + value + ); + } + properties.put( name, value ); + } + + public Map build() { + return properties; + } + } + + private static class ValueExtractorsStaxBuilder extends AbstractStaxBuilder { + + private static final String VALUE_EXTRACTOR_QNAME_LOCAL_PART = "value-extractor"; + + private final Set valueExtractors = new HashSet<>(); + + @Override + protected String getAcceptableQName() { + return VALUE_EXTRACTOR_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String value = readSingleElement( xmlEventReader ); + if ( !valueExtractors.add( value ) ) { + throw LOG.getDuplicateDefinitionsOfValueExtractorException( value ); + } + } + + public Set build() { + return valueExtractors; + } + } + + private static class ConstraintMappingsStaxBuilder extends AbstractStaxBuilder { + + private static final String CONSTRAINT_MAPPING_QNAME_LOCAL_PART = "constraint-mapping"; + + private final Set constraintMappings = new HashSet<>(); + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_MAPPING_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String value = readSingleElement( xmlEventReader ); + constraintMappings.add( value ); + } + + public Set build() { + return constraintMappings; + } + } + + private static class ExecutableValidationStaxBuilder extends AbstractStaxBuilder { + + private static final String EXECUTABLE_VALIDATION_QNAME_LOCAL_PART = "executable-validation"; + private static final String EXECUTABLE_TYPE_QNAME_LOCAL_PART = "executable-type"; + + private static final QName ENABLED_QNAME = new QName( "enabled" ); + + private Boolean enabled; + + private EnumSet executableTypes = EnumSet.noneOf( ExecutableType.class ); + + @Override + protected String getAcceptableQName() { + return EXECUTABLE_VALIDATION_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional enabledAttribute = readAttribute( xmlEvent.asStartElement(), ENABLED_QNAME ); + if ( enabledAttribute.isPresent() ) { + enabled = Boolean.parseBoolean( enabledAttribute.get() ); + } + + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( EXECUTABLE_VALIDATION_QNAME_LOCAL_PART ) ) ) { + XMLEvent currentEvent = xmlEventReader.nextEvent(); + xmlEvent = currentEvent; + if ( currentEvent.isStartElement() && currentEvent.asStartElement().getName().getLocalPart().equals( EXECUTABLE_TYPE_QNAME_LOCAL_PART ) ) { + executableTypes.add( ExecutableType.valueOf( readSingleElement( xmlEventReader ) ) ); + } + } + } + + public boolean isEnabled() { + return enabled == null ? true : enabled; + } + + /** + * Returns an enum set with the executable types corresponding to the given + * XML configuration, considering the special elements + * {@link ExecutableType#ALL} and {@link ExecutableType#NONE}. + * + * @return An enum set representing the given executable types. + */ + public EnumSet build() { + return executableTypes.isEmpty() ? null : executableTypes; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationXmlParser.java new file mode 100644 index 0000000000..45d15c333a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/config/ValidationXmlParser.java @@ -0,0 +1,167 @@ +/* + * 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.xml.config; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Map; + +import javax.validation.BootstrapConfiguration; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.Validator; + +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; +import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; +import org.hibernate.validator.internal.xml.CloseIgnoringInputStream; +import org.hibernate.validator.internal.xml.XmlParserHelper; + +import org.xml.sax.SAXException; + +/** + * Parser for validation.xml. + * + * @author Hardy Ferentschik + * @author Gunnar Morling + */ +public class ValidationXmlParser { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String VALIDATION_XML_FILE = "META-INF/validation.xml"; + private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); + + private final ClassLoader externalClassLoader; + + private static Map getSchemasByVersion() { + Map schemasByVersion = CollectionHelper.newHashMap( 3 ); + + schemasByVersion.put( "1.0", "META-INF/validation-configuration-1.0.xsd" ); + schemasByVersion.put( "1.1", "META-INF/validation-configuration-1.1.xsd" ); + schemasByVersion.put( "2.0", "META-INF/validation-configuration-2.0.xsd" ); + + return schemasByVersion; + } + + public ValidationXmlParser(ClassLoader externalClassLoader) { + this.externalClassLoader = externalClassLoader; + } + + /** + * Tries to check whether a validation.xml file exists and parses it. + * + * @return The parameters parsed out of validation.xml wrapped in an instance of {@code ConfigurationImpl.ValidationBootstrapParameters}. + */ + public final BootstrapConfiguration parseValidationXml() { + InputStream in = getValidationXmlInputStream(); + if ( in == null ) { + return BootstrapConfigurationImpl.getDefaultBootstrapConfiguration(); + } + + ClassLoader previousTccl = run( GetClassLoader.fromContext() ); + + try { + run( SetContextClassLoader.action( ValidationXmlParser.class.getClassLoader() ) ); + + // HV-970 The parser helper is only loaded if there actually is a validation.xml file; + // this avoids accessing javax.xml.stream.* (which does not exist on Android) when not actually + // working with the XML configuration + XmlParserHelper xmlParserHelper = new XmlParserHelper(); + + // the InputStream supports mark and reset + in.mark( Integer.MAX_VALUE ); + + XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); + String schemaVersion = xmlParserHelper.getSchemaVersion( VALIDATION_XML_FILE, xmlEventReader ); + xmlEventReader.close(); + + in.reset(); + + // The validation is done first as StAX builders used below are assuming that the XML file is correct and don't + // do any validation of the input. + Schema schema = getSchema( xmlParserHelper, schemaVersion ); + Validator validator = schema.newValidator(); + validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); + + in.reset(); + + xmlEventReader = xmlParserHelper.createXmlEventReader( VALIDATION_XML_FILE, new CloseIgnoringInputStream( in ) ); + + ValidationConfigStaxBuilder validationConfigStaxBuilder = new ValidationConfigStaxBuilder( xmlEventReader ); + + xmlEventReader.close(); + in.reset(); + + return validationConfigStaxBuilder.build(); + } + catch (XMLStreamException | IOException | SAXException e) { + throw LOG.getUnableToParseValidationXmlFileException( VALIDATION_XML_FILE, e ); + } + finally { + run( SetContextClassLoader.action( previousTccl ) ); + closeStream( in ); + } + } + + private InputStream getValidationXmlInputStream() { + LOG.debugf( "Trying to load %s for XML based Validator configuration.", VALIDATION_XML_FILE ); + InputStream inputStream = ResourceLoaderHelper.getResettableInputStreamForPath( VALIDATION_XML_FILE, externalClassLoader ); + + if ( inputStream != null ) { + return inputStream; + } + else { + LOG.debugf( "No %s found. Using annotation based configuration only.", VALIDATION_XML_FILE ); + return null; + } + } + + private Schema getSchema(XmlParserHelper xmlParserHelper, String schemaVersion) { + String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); + + if ( schemaResource == null ) { + throw LOG.getUnsupportedSchemaVersionException( VALIDATION_XML_FILE, schemaVersion ); + } + + Schema schema = xmlParserHelper.getSchema( schemaResource ); + + if ( schema == null ) { + throw LOG.unableToGetXmlSchema( schemaResource ); + } + + return schema; + } + + private void closeStream(InputStream inputStream) { + try { + inputStream.close(); + } + catch (IOException io) { + LOG.unableToCloseXMLFileInputStream( VALIDATION_XML_FILE ); + } + } + + /** + * 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 static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java new file mode 100644 index 0000000000..4791436852 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedElementStaxBuilder.java @@ -0,0 +1,111 @@ +/* + * 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.xml.mapping; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Base builder for all constrained element builders that can have constraints or type argument constraints. + * + * @author Marko Bekhta + */ +abstract class AbstractConstrainedElementStaxBuilder extends AbstractStaxBuilder { + + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + protected final ClassLoadingHelper classLoadingHelper; + protected final ConstraintHelper constraintHelper; + protected final TypeResolutionHelper typeResolutionHelper; + protected final ValueExtractorManager valueExtractorManager; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + protected final AnnotationProcessingOptionsImpl annotationProcessingOptions; + + protected String mainAttributeValue; + protected Optional ignoreAnnotations; + protected final GroupConversionStaxBuilder groupConversionBuilder; + protected final ValidStaxBuilder validStaxBuilder; + protected final List constraintTypeStaxBuilders; + protected final ContainerElementTypeConfigurationBuilder containerElementTypeConfigurationBuilder; + + AbstractConstrainedElementStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + this.groupConversionBuilder = new GroupConversionStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.validStaxBuilder = new ValidStaxBuilder(); + this.containerElementTypeConfigurationBuilder = new ContainerElementTypeConfigurationBuilder(); + this.annotationProcessingOptions = annotationProcessingOptions; + + this.constraintTypeStaxBuilders = new ArrayList<>(); + } + + abstract Optional getMainAttributeValueQname(); + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional mainAttributeValueQname = getMainAttributeValueQname(); + if ( mainAttributeValueQname.isPresent() ) { + mainAttributeValue = readAttribute( xmlEvent.asStartElement(), mainAttributeValueQname.get() ).get(); + } + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + ContainerElementTypeStaxBuilder containerElementTypeStaxBuilder = getNewContainerElementTypeStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + validStaxBuilder.process( xmlEventReader, xmlEvent ); + groupConversionBuilder.process( xmlEventReader, xmlEvent ); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + if ( containerElementTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + containerElementTypeConfigurationBuilder.add( containerElementTypeStaxBuilder ); + containerElementTypeStaxBuilder = getNewContainerElementTypeStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder ); + } + + private ContainerElementTypeStaxBuilder getNewContainerElementTypeStaxBuilder() { + return new ContainerElementTypeStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder ); + } + + protected ContainerElementTypeConfiguration getContainerElementTypeConfiguration(Type type, ConstraintLocation constraintLocation) { + return containerElementTypeConfigurationBuilder.build( constraintLocation, type ); + } + + protected CascadingMetaDataBuilder getCascadingMetaData(Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData, Type type) { + return CascadingMetaDataBuilder.annotatedObject( type, validStaxBuilder.build(), containerElementTypesCascadingMetaData, groupConversionBuilder.build() ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedExecutableElementStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedExecutableElementStaxBuilder.java new file mode 100644 index 0000000000..c466e28fec --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractConstrainedExecutableElementStaxBuilder.java @@ -0,0 +1,107 @@ +/* + * 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.xml.mapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * A base builder for constraint executables. Provides read logic only. Build logic should be added in implementations. + * + * @author Marko Bekhta + */ +abstract class AbstractConstrainedExecutableElementStaxBuilder extends AbstractStaxBuilder { + + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + protected final ClassLoadingHelper classLoadingHelper; + protected final ConstraintHelper constraintHelper; + protected final TypeResolutionHelper typeResolutionHelper; + protected final ValueExtractorManager valueExtractorManager; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + protected final AnnotationProcessingOptionsImpl annotationProcessingOptions; + + protected String mainAttributeValue; + protected Optional ignoreAnnotations; + protected final List constrainedParameterStaxBuilders; + private CrossParameterStaxBuilder crossParameterStaxBuilder; + private ReturnValueStaxBuilder returnValueStaxBuilder; + + AbstractConstrainedExecutableElementStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + this.annotationProcessingOptions = annotationProcessingOptions; + + this.constrainedParameterStaxBuilders = new ArrayList<>(); + } + + abstract Optional getMainAttributeValueQname(); + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional mainAttributeValueQname = getMainAttributeValueQname(); + if ( mainAttributeValueQname.isPresent() ) { + mainAttributeValue = readAttribute( xmlEvent.asStartElement(), mainAttributeValueQname.get() ).get(); + } + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstrainedParameterStaxBuilder constrainedParameterStaxBuilder = getNewConstrainedParameterStaxBuilder(); + ReturnValueStaxBuilder localReturnValueStaxBuilder = getNewReturnValueStaxBuilder(); + CrossParameterStaxBuilder localCrossParameterStaxBuilder = getNewCrossParameterStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( constrainedParameterStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedParameterStaxBuilders.add( constrainedParameterStaxBuilder ); + constrainedParameterStaxBuilder = getNewConstrainedParameterStaxBuilder(); + } + else if ( localReturnValueStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + this.returnValueStaxBuilder = localReturnValueStaxBuilder; + } + else if ( localCrossParameterStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + this.crossParameterStaxBuilder = localCrossParameterStaxBuilder; + } + } + } + + private ConstrainedParameterStaxBuilder getNewConstrainedParameterStaxBuilder() { + return new ConstrainedParameterStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private CrossParameterStaxBuilder getNewCrossParameterStaxBuilder() { + return new CrossParameterStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ReturnValueStaxBuilder getNewReturnValueStaxBuilder() { + return new ReturnValueStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + public Optional getReturnValueStaxBuilder() { + return Optional.ofNullable( returnValueStaxBuilder ); + } + + public Optional getCrossParameterStaxBuilder() { + return Optional.ofNullable( crossParameterStaxBuilder ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractMultiValuedElementStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractMultiValuedElementStaxBuilder.java new file mode 100644 index 0000000000..f284261417 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractMultiValuedElementStaxBuilder.java @@ -0,0 +1,64 @@ +/* + * 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.xml.mapping; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * An abstract builder for an element that could have multiple {@code ... } entries. + * + * @author Marko Bekhta + */ +abstract class AbstractMultiValuedElementStaxBuilder extends AbstractStaxBuilder { + + private static final String VALUE_QNAME_LOCAL_PART = "value"; + + private static final Class[] EMPTY_CLASSES_ARRAY = new Class[0]; + + private final ClassLoadingHelper classLoadingHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private final List values; + + protected AbstractMultiValuedElementStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.values = new ArrayList<>(); + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals( VALUE_QNAME_LOCAL_PART ) ) { + values.add( readSingleElement( xmlEventReader ) ); + } + } + } + + public Class[] build() { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + if ( values.isEmpty() ) { + return EMPTY_CLASSES_ARRAY; + } + + return values.stream() + .map( valueClass -> classLoadingHelper.loadClass( valueClass, defaultPackage ) ) + .peek( this::verifyClass ) + .toArray( Class[]::new ); + } + + public abstract void verifyClass(Class clazz); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractOneLineStringStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractOneLineStringStaxBuilder.java new file mode 100644 index 0000000000..762872b093 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/AbstractOneLineStringStaxBuilder.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.internal.xml.mapping; + +import java.util.Optional; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * An abstract builder to be used for reading simple string element that can + * occur only once in a given block, e.g. {@code } or {@code }. + * + * @author Marko Bekhta + */ +abstract class AbstractOneLineStringStaxBuilder extends AbstractStaxBuilder { + + private String value; + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + this.value = readSingleElement( xmlEventReader ); + } + + public Optional build() { + return Optional.ofNullable( value ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/BeanStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/BeanStaxBuilder.java new file mode 100644 index 0000000000..e551a46485 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/BeanStaxBuilder.java @@ -0,0 +1,228 @@ +/* + * 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.xml.mapping; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for definition of all bean constraints. + * + * @author Marko Bekhta + */ +class BeanStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + private static final QName CLASS_QNAME = new QName( "class" ); + private static final String BEAN_QNAME_LOCAL_PART = "bean"; + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintHelper constraintHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final Map, List>> defaultSequences; + + protected String className; + protected Optional ignoreAnnotations; + private ClassConstraintTypeStaxBuilder classConstraintTypeStaxBuilder; + private final List constrainedFieldStaxBuilders; + private final List constrainedGetterStaxBuilders; + private final List constrainedMethodStaxBuilders; + private final List constrainedConstructorStaxBuilders; + + BeanStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions, + Map, List>> defaultSequences) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + this.annotationProcessingOptions = annotationProcessingOptions; + this.defaultSequences = defaultSequences; + + this.constrainedFieldStaxBuilders = new ArrayList<>(); + this.constrainedGetterStaxBuilders = new ArrayList<>(); + this.constrainedMethodStaxBuilders = new ArrayList<>(); + this.constrainedConstructorStaxBuilders = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return BEAN_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + this.className = readAttribute( xmlEvent.asStartElement(), CLASS_QNAME ).get(); + this.ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstrainedFieldStaxBuilder fieldStaxBuilder = getNewConstrainedFieldStaxBuilder(); + ConstrainedGetterStaxBuilder getterStaxBuilder = getNewConstrainedGetterStaxBuilder(); + ConstrainedMethodStaxBuilder methodStaxBuilder = getNewConstrainedMethodStaxBuilder(); + ConstrainedConstructorStaxBuilder constructorStaxBuilder = getNewConstrainedConstructorStaxBuilder(); + + ClassConstraintTypeStaxBuilder localClassConstraintTypeStaxBuilder = new ClassConstraintTypeStaxBuilder( + classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, + defaultPackageStaxBuilder, annotationProcessingOptions, defaultSequences + ); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( fieldStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedFieldStaxBuilders.add( fieldStaxBuilder ); + fieldStaxBuilder = getNewConstrainedFieldStaxBuilder(); + } + else if ( getterStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedGetterStaxBuilders.add( getterStaxBuilder ); + getterStaxBuilder = getNewConstrainedGetterStaxBuilder(); + } + else if ( methodStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedMethodStaxBuilders.add( methodStaxBuilder ); + methodStaxBuilder = getNewConstrainedMethodStaxBuilder(); + } + else if ( constructorStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constrainedConstructorStaxBuilders.add( constructorStaxBuilder ); + constructorStaxBuilder = getNewConstrainedConstructorStaxBuilder(); + } + else if ( localClassConstraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + classConstraintTypeStaxBuilder = localClassConstraintTypeStaxBuilder; + } + } + } + + private ConstrainedFieldStaxBuilder getNewConstrainedFieldStaxBuilder() { + return new ConstrainedFieldStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ConstrainedGetterStaxBuilder getNewConstrainedGetterStaxBuilder() { + return new ConstrainedGetterStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ConstrainedMethodStaxBuilder getNewConstrainedMethodStaxBuilder() { + return new ConstrainedMethodStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + private ConstrainedConstructorStaxBuilder getNewConstrainedConstructorStaxBuilder() { + return new ConstrainedConstructorStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + void build(Set> processedClasses, Map, Set> constrainedElementsByType) { + Class beanClass = classLoadingHelper.loadClass( className, defaultPackageStaxBuilder.build().orElse( "" ) ); + + checkClassHasNotBeenProcessed( processedClasses, beanClass ); + + // update annotation ignores + // NOTE: if there was no ignoreAnnotations attribute specified on a bean + // we use `true` as a default + annotationProcessingOptions.ignoreAnnotationConstraintForClass( + beanClass, + ignoreAnnotations.orElse( true ) + ); + + if ( classConstraintTypeStaxBuilder != null ) { + addConstrainedElements( + constrainedElementsByType, + beanClass, + Collections.singleton( classConstraintTypeStaxBuilder.build( beanClass ) ) + ); + } + + List alreadyProcessedFieldNames = new ArrayList<>( constrainedFieldStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, constrainedFieldStaxBuilders.stream() + .map( builder -> builder.build( beanClass, alreadyProcessedFieldNames ) ) + .collect( Collectors.toList() ) + ); + + List alreadyProcessedGetterNames = new ArrayList<>( constrainedGetterStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, + constrainedGetterStaxBuilders.stream() + .map( builder -> builder.build( beanClass, alreadyProcessedGetterNames ) ) + .collect( Collectors.toList() ) + ); + + List alreadyProcessedMethods = new ArrayList<>( constrainedMethodStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, + constrainedMethodStaxBuilders.stream() + .map( builder -> builder.build( beanClass, alreadyProcessedMethods ) ) + .collect( Collectors.toList() ) + ); + + List> alreadyProcessedConstructors = new ArrayList<>( constrainedConstructorStaxBuilders.size() ); + addConstrainedElements( + constrainedElementsByType, + beanClass, + constrainedConstructorStaxBuilders.stream() + .map( builder -> builder.build( beanClass, alreadyProcessedConstructors ) ) + .collect( Collectors.toList() ) + ); + } + + private void addConstrainedElements(Map, Set> constrainedElementsbyType, Class beanClass, Collection newConstrainedElements) { + if ( constrainedElementsbyType.containsKey( beanClass ) ) { + + Set existingConstrainedElements = constrainedElementsbyType.get( beanClass ); + + for ( ConstrainedElement constrainedElement : newConstrainedElements ) { + if ( existingConstrainedElements.contains( constrainedElement ) ) { + throw LOG.getConstrainedElementConfiguredMultipleTimesException( + constrainedElement.toString() + ); + } + } + + existingConstrainedElements.addAll( newConstrainedElements ); + } + else { + Set tmpSet = newHashSet(); + tmpSet.addAll( newConstrainedElements ); + constrainedElementsbyType.put( beanClass, tmpSet ); + } + } + + private void checkClassHasNotBeenProcessed(Set> processedClasses, Class beanClass) { + if ( processedClasses.contains( beanClass ) ) { + throw LOG.getBeanClassHasAlreadyBeenConfiguredInXmlException( beanClass ); + } + processedClasses.add( beanClass ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassConstraintTypeStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassConstraintTypeStaxBuilder.java new file mode 100644 index 0000000000..c75a851b14 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassConstraintTypeStaxBuilder.java @@ -0,0 +1,142 @@ +/* + * 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.xml.mapping; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedType; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for class level constraints. + * + * @author Marko Bekhta + */ +class ClassConstraintTypeStaxBuilder extends AbstractStaxBuilder { + + private static final String CLASS_QNAME_LOCAL_PART = "class"; + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintHelper constraintHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final Map, List>> defaultSequences; + + private Optional ignoreAnnotations; + private final List constraintTypeStaxBuilders; + private final GroupSequenceStaxBuilder groupSequenceStaxBuilder; + + ClassConstraintTypeStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions, + Map, List>> defaultSequences) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + this.annotationProcessingOptions = annotationProcessingOptions; + this.defaultSequences = defaultSequences; + + this.constraintTypeStaxBuilders = new ArrayList<>(); + this.groupSequenceStaxBuilder = new GroupSequenceStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + protected String getAcceptableQName() { + return CLASS_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + groupSequenceStaxBuilder.process( xmlEventReader, xmlEvent ); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder ); + } + + ConstrainedType build(Class beanClass) { + // group sequence + List> groupSequence = Arrays.asList( groupSequenceStaxBuilder.build() ); + if ( !groupSequence.isEmpty() ) { + defaultSequences.put( beanClass, groupSequence ); + } + + // constraints + ConstraintLocation constraintLocation = ConstraintLocation.forClass( beanClass ); + + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.TYPE, null ) ) + .collect( Collectors.toSet() ); + + // ignore annotation + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreClassLevelConstraintAnnotations( + beanClass, + ignoreAnnotations.get() + ); + } + + return new ConstrainedType( + ConfigurationSource.XML, + beanClass, + metaConstraints + ); + } + + private static class GroupSequenceStaxBuilder extends AbstractMultiValuedElementStaxBuilder { + + private static final String GROUP_SEQUENCE_QNAME_LOCAL_PART = "group-sequence"; + + private GroupSequenceStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + public void verifyClass(Class clazz) { + // do nothing + } + + @Override + protected String getAcceptableQName() { + return GROUP_SEQUENCE_QNAME_LOCAL_PART; + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ClassLoadingHelper.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassLoadingHelper.java similarity index 87% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ClassLoadingHelper.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassLoadingHelper.java index 7d5392df18..959e3e65b5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ClassLoadingHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ClassLoadingHelper.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.xml; +package org.hibernate.validator.internal.xml.mapping; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; @@ -20,8 +20,9 @@ * types, qualified names or unqualified names (in which case a given default package will be assumed). * * @author Gunnar Morling + * @author Guillaume Smet */ -/*package*/ class ClassLoadingHelper { +class ClassLoadingHelper { private static final String PACKAGE_SEPARATOR = "."; private static final String ARRAY_CLASS_NAME_PREFIX = "[L"; @@ -47,11 +48,14 @@ private final ClassLoader externalClassLoader; - ClassLoadingHelper(ClassLoader externalClassLoader) { + private final ClassLoader threadContextClassLoader; + + ClassLoadingHelper(ClassLoader externalClassLoader, ClassLoader threadContextClassLoader) { this.externalClassLoader = externalClassLoader; + this.threadContextClassLoader = threadContextClassLoader; } - /*package*/ Class loadClass(String className, String defaultPackage) { + Class loadClass(String className, String defaultPackage) { if ( PRIMITIVE_NAME_TO_PRIMITIVE.containsKey( className ) ) { return PRIMITIVE_NAME_TO_PRIMITIVE.get( className ); } @@ -80,7 +84,7 @@ } private Class loadClass(String className) { - return run( LoadClass.action( className, externalClassLoader ) ); + return run( LoadClass.action( className, externalClassLoader, threadContextClassLoader ) ); } private static boolean isArrayClassName(String className) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java new file mode 100644 index 0000000000..d29312b7b8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedConstructorStaxBuilder.java @@ -0,0 +1,138 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Constructor; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructor; + +/** + * Builder for constrained constructors. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedConstructorStaxBuilder extends AbstractConstrainedExecutableElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String METHOD_QNAME_LOCAL_PART = "constructor"; + + ConstrainedConstructorStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, DefaultPackageStaxBuilder defaultPackageStaxBuilder, + AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.empty(); + } + + @Override + protected String getAcceptableQName() { + return METHOD_QNAME_LOCAL_PART; + } + + public String getMethodName() { + return mainAttributeValue; + } + + ConstrainedExecutable build(Class beanClass, List> alreadyProcessedConstructors) { + Class[] parameterTypes = constrainedParameterStaxBuilders.stream() + .map( builder -> builder.getParameterType( beanClass ) ) + .toArray( Class[]::new ); + + final Constructor constructor = run( + GetDeclaredConstructor.action( + beanClass, + parameterTypes + ) + ); + + if ( constructor == null ) { + throw LOG.getBeanDoesNotContainConstructorException( + beanClass, + parameterTypes + ); + } + + if ( alreadyProcessedConstructors.contains( constructor ) ) { + throw LOG.getConstructorIsDefinedTwiceInMappingXmlForBeanException( constructor, beanClass ); + } + else { + alreadyProcessedConstructors.add( constructor ); + } + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + constructor, + ignoreAnnotations.get() + ); + } + + List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); + for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { + ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); + constrainedParameters.add( builder.build( constructor, index ) ); + } + + Set> crossParameterConstraints = getCrossParameterStaxBuilder() + .map( builder -> builder.build( constructor ) ).orElse( Collections.emptySet() ); + + // parse the return value + Set> returnValueConstraints = new HashSet<>(); + Set> returnValueTypeArgumentConstraints = new HashSet<>(); + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( constructor, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + .orElse( CascadingMetaDataBuilder.nonCascading() ); + + return new ConstrainedExecutable( + ConfigurationSource.XML, + constructor, + constrainedParameters, + crossParameterConstraints, + returnValueConstraints, + returnValueTypeArgumentConstraints, + cascadingMetaDataBuilder + ); + } + + /** + * 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 static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedFieldStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedFieldStaxBuilder.java new file mode 100644 index 0000000000..8b2a8a97be --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedFieldStaxBuilder.java @@ -0,0 +1,116 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedField; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constrained fields. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedFieldStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String FIELD_QNAME_LOCAL_PART = "field"; + private static final QName NAME_QNAME = new QName( "name" ); + + ConstrainedFieldStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( NAME_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return FIELD_QNAME_LOCAL_PART; + } + + ConstrainedField build(Class beanClass, List alreadyProcessedFieldNames) { + if ( alreadyProcessedFieldNames.contains( mainAttributeValue ) ) { + throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, beanClass ); + } + else { + alreadyProcessedFieldNames.add( mainAttributeValue ); + } + Field field = findField( beanClass, mainAttributeValue ); + ConstraintLocation constraintLocation = ConstraintLocation.forField( field ); + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.FIELD, null ) ) + .collect( Collectors.toSet() ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( + ReflectionHelper.typeOf( field ), constraintLocation ); + + ConstrainedField constrainedField = new ConstrainedField( + ConfigurationSource.XML, + field, + metaConstraints, + containerElementTypeConfiguration.getMetaConstraints(), + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), ReflectionHelper.typeOf( field ) ) + ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + field, + ignoreAnnotations.get() + ); + } + + return constrainedField; + } + + private static Field findField(Class beanClass, String fieldName) { + final Field field = run( GetDeclaredField.action( beanClass, fieldName ) ); + if ( field == null ) { + throw LOG.getBeanDoesNotContainTheFieldException( beanClass, fieldName ); + } + return field; + } + + /** + * 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 static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedGetterStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedGetterStaxBuilder.java new file mode 100644 index 0000000000..ba1b607ffd --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedGetterStaxBuilder.java @@ -0,0 +1,123 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetMethodFromPropertyName; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constrained getters. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedGetterStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + private static final QName NAME_QNAME = new QName( "name" ); + + private static final String GETTER_QNAME_LOCAL_PART = "getter"; + + ConstrainedGetterStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, DefaultPackageStaxBuilder defaultPackageStaxBuilder, + AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( NAME_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return GETTER_QNAME_LOCAL_PART; + } + + ConstrainedExecutable build(Class beanClass, List alreadyProcessedGetterNames) { + if ( alreadyProcessedGetterNames.contains( mainAttributeValue ) ) { + throw LOG.getIsDefinedTwiceInMappingXmlForBeanException( mainAttributeValue, beanClass ); + } + else { + alreadyProcessedGetterNames.add( mainAttributeValue ); + } + Method getter = findGetter( beanClass, mainAttributeValue ); + ConstraintLocation constraintLocation = ConstraintLocation.forGetter( beanClass, getter ); + + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.METHOD, null ) ) + .collect( Collectors.toSet() ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( + ReflectionHelper.typeOf( getter ), constraintLocation ); + + ConstrainedExecutable constrainedGetter = new ConstrainedExecutable( + ConfigurationSource.XML, + getter, + Collections.emptyList(), + Collections.>emptySet(), + metaConstraints, + containerElementTypeConfiguration.getMetaConstraints(), + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), ReflectionHelper.typeOf( getter ) ) + ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + getter, + ignoreAnnotations.get() + ); + } + + return constrainedGetter; + } + + private static Method findGetter(Class beanClass, String getterName) { + final Method method = run( GetMethodFromPropertyName.action( beanClass, getterName ) ); + if ( method == null ) { + throw LOG.getBeanDoesNotContainThePropertyException( beanClass, getterName ); + } + + return method; + } + + /** + * 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 static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java new file mode 100644 index 0000000000..ede789a730 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedMethodStaxBuilder.java @@ -0,0 +1,143 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod; + +/** + * Builder for constrained methods. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedMethodStaxBuilder extends AbstractConstrainedExecutableElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String METHOD_QNAME_LOCAL_PART = "method"; + private static final QName NAME_QNAME = new QName( "name" ); + + ConstrainedMethodStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, DefaultPackageStaxBuilder defaultPackageStaxBuilder, + AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( NAME_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return METHOD_QNAME_LOCAL_PART; + } + + public String getMethodName() { + return mainAttributeValue; + } + + ConstrainedExecutable build(Class beanClass, List alreadyProcessedMethods) { + Class[] parameterTypes = constrainedParameterStaxBuilders.stream() + .map( builder -> builder.getParameterType( beanClass ) ) + .toArray( Class[]::new ); + + String methodName = getMethodName(); + + final Method method = run( + GetDeclaredMethod.action( + beanClass, + methodName, + parameterTypes + ) + ); + + if ( method == null ) { + throw LOG.getBeanDoesNotContainMethodException( + beanClass, + methodName, + parameterTypes + ); + } + + if ( alreadyProcessedMethods.contains( method ) ) { + throw LOG.getMethodIsDefinedTwiceInMappingXmlForBeanException( method, beanClass ); + } + else { + alreadyProcessedMethods.add( method ); + } + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnMember( + method, + ignoreAnnotations.get() + ); + } + + List constrainedParameters = CollectionHelper.newArrayList( constrainedParameterStaxBuilders.size() ); + for ( int index = 0; index < constrainedParameterStaxBuilders.size(); index++ ) { + ConstrainedParameterStaxBuilder builder = constrainedParameterStaxBuilders.get( index ); + constrainedParameters.add( builder.build( method, index ) ); + } + + Set> crossParameterConstraints = getCrossParameterStaxBuilder() + .map( builder -> builder.build( method ) ).orElse( Collections.emptySet() ); + + // parse the return value + Set> returnValueConstraints = new HashSet<>(); + Set> returnValueTypeArgumentConstraints = new HashSet<>(); + CascadingMetaDataBuilder cascadingMetaDataBuilder = getReturnValueStaxBuilder().map( builder -> builder.build( method, returnValueConstraints, returnValueTypeArgumentConstraints ) ) + .orElse( CascadingMetaDataBuilder.nonCascading() ); + + return new ConstrainedExecutable( + ConfigurationSource.XML, + method, + constrainedParameters, + crossParameterConstraints, + returnValueConstraints, + returnValueTypeArgumentConstraints, + cascadingMetaDataBuilder + ); + } + + /** + * 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 static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedParameterStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedParameterStaxBuilder.java new file mode 100644 index 0000000000..cf5a77d690 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstrainedParameterStaxBuilder.java @@ -0,0 +1,102 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Executable; +import java.lang.reflect.Type; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.ValidationException; +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; +import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constrained parameters. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ConstrainedParameterStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String PARAMETER_QNAME_LOCAL_PART = "parameter"; + private static final QName TYPE_QNAME = new QName( "type" ); + + ConstrainedParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, + ValueExtractorManager valueExtractorManager, DefaultPackageStaxBuilder defaultPackageStaxBuilder, + AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.of( TYPE_QNAME ); + } + + @Override + protected String getAcceptableQName() { + return PARAMETER_QNAME_LOCAL_PART; + } + + public Class getParameterType(Class beanClass) { + try { + return classLoadingHelper.loadClass( mainAttributeValue, defaultPackageStaxBuilder.build().orElse( "" ) ); + } + catch (ValidationException e) { + throw LOG.getInvalidParameterTypeException( mainAttributeValue, beanClass ); + } + } + + ConstrainedParameter build(Executable executable, int index) { + + ConstraintLocation constraintLocation = ConstraintLocation.forParameter( executable, index ); + Type type = ReflectionHelper.typeOf( executable, index ); + + Set> metaConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.PARAMETER, null ) ) + .collect( Collectors.toSet() ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( type, constraintLocation ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsOnParameter( + executable, + index, + ignoreAnnotations.get() + ); + } + + ConstrainedParameter constrainedParameter = new ConstrainedParameter( + ConfigurationSource.XML, + executable, + type, + index, + metaConstraints, + containerElementTypeConfiguration.getMetaConstraints(), + getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), type ) + ); + return constrainedParameter; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintDefinitionStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintDefinitionStaxBuilder.java new file mode 100644 index 0000000000..a600d370be --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintDefinitionStaxBuilder.java @@ -0,0 +1,158 @@ +/* + * 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.xml.mapping; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.ConstraintValidator; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for a constraint definition. + * + * @author Marko Bekhta + */ +class ConstraintDefinitionStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String CONSTRAINT_DEFINITION_QNAME_LOCAL_PART = "constraint-definition"; + private static final QName ANNOTATION_QNAME = new QName( "annotation" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintHelper constraintHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private String annotation; + private ValidatedByStaxBuilder validatedByStaxBuilder; + + ConstraintDefinitionStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.constraintHelper = constraintHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.validatedByStaxBuilder = new ValidatedByStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_DEFINITION_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + annotation = readAttribute( xmlEvent.asStartElement(), ANNOTATION_QNAME ).get(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + validatedByStaxBuilder.process( xmlEventReader, xmlEvent ); + xmlEvent = xmlEventReader.nextEvent(); + } + } + + @SuppressWarnings("unchecked") + void build(Set alreadyProcessedConstraintDefinitions) { + checkProcessedAnnotations( alreadyProcessedConstraintDefinitions ); + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + Class clazz = classLoadingHelper.loadClass( annotation, defaultPackage ); + if ( !clazz.isAnnotation() ) { + throw LOG.getIsNotAnAnnotationException( clazz ); + } + Class annotationClass = (Class) clazz; + addValidatorDefinitions( annotationClass ); + } + + private void checkProcessedAnnotations(Set alreadyProcessedConstraintDefinitions) { + if ( alreadyProcessedConstraintDefinitions.contains( annotation ) ) { + throw LOG.getOverridingConstraintDefinitionsInMultipleMappingFilesException( annotation ); + } + else { + alreadyProcessedConstraintDefinitions.add( annotation ); + } + } + + private void addValidatorDefinitions(Class annotationClass) { + constraintHelper.putValidatorDescriptors( + annotationClass, + validatedByStaxBuilder.build( annotationClass ), + validatedByStaxBuilder.isIncludeExistingValidators() + ); + } + + private static class ValidatedByStaxBuilder extends AbstractStaxBuilder { + + private static final String VALIDATED_BY_QNAME_LOCAL_PART = "validated-by"; + private static final String VALUE_QNAME_LOCAL_PART = "value"; + private static final QName INCLUDE_EXISTING_VALIDATORS_QNAME = new QName( "include-existing-validators" ); + + private final ClassLoadingHelper classLoadingHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private boolean includeExistingValidators; + private final List values; + + protected ValidatedByStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.values = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return VALIDATED_BY_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + includeExistingValidators = readAttribute( xmlEvent.asStartElement(), INCLUDE_EXISTING_VALIDATORS_QNAME ) + .map( Boolean::parseBoolean ).orElse( true ); + + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + if ( xmlEvent.isStartElement() && xmlEvent.asStartElement().getName().getLocalPart().equals( VALUE_QNAME_LOCAL_PART ) ) { + values.add( readSingleElement( xmlEventReader ) ); + } + xmlEvent = xmlEventReader.nextEvent(); + } + } + + @SuppressWarnings("unchecked") + List> build(Class annotation) { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + + return values.stream() + .map( value -> classLoadingHelper.loadClass( value, defaultPackage ) ) + .peek( this::checkValidatorAssignability ) + .map( clazz -> (Class>) clazz ) + .map( validatorClass -> ConstraintValidatorDescriptor.forClass( validatorClass, annotation ) ) + .collect( Collectors.toList() ); + } + + public boolean isIncludeExistingValidators() { + return includeExistingValidators; + } + + private void checkValidatorAssignability(Class validatorClass) { + if ( !ConstraintValidator.class.isAssignableFrom( validatorClass ) ) { + throw LOG.getIsNotAConstraintValidatorClassException( validatorClass ); + } + } + + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintMappingsStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintMappingsStaxBuilder.java new file mode 100644 index 0000000000..50ac58eeb8 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintMappingsStaxBuilder.java @@ -0,0 +1,96 @@ +/* + * 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.xml.mapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Top level builder for constraint mappings. Reads the whole mapping file and builds the constraint definitions defined + * in it as well as the list of constrained elements per bean. + * + * @author Marko Bekhta + */ +class ConstraintMappingsStaxBuilder extends AbstractStaxBuilder { + + private static final String CONSTRAINT_MAPPINGS_QNAME = "constraint-mappings"; + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintHelper constraintHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final Map, List>> defaultSequences; + + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + private final List beanStaxBuilders; + private final List constraintDefinitionStaxBuilders; + + public ConstraintMappingsStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + AnnotationProcessingOptionsImpl annotationProcessingOptions, Map, List>> defaultSequences) { + this.classLoadingHelper = classLoadingHelper; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + this.annotationProcessingOptions = annotationProcessingOptions; + this.defaultSequences = defaultSequences; + + this.defaultPackageStaxBuilder = new DefaultPackageStaxBuilder(); + this.beanStaxBuilders = new ArrayList<>(); + this.constraintDefinitionStaxBuilders = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_MAPPINGS_QNAME; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + BeanStaxBuilder beanStaxBuilder = getNewBeanStaxBuilder(); + ConstraintDefinitionStaxBuilder constraintDefinitionStaxBuilder = getNewConstraintDefinitionStaxBuilder(); + + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( beanStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + beanStaxBuilders.add( beanStaxBuilder ); + beanStaxBuilder = getNewBeanStaxBuilder(); + } + else if ( constraintDefinitionStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintDefinitionStaxBuilders.add( constraintDefinitionStaxBuilder ); + constraintDefinitionStaxBuilder = getNewConstraintDefinitionStaxBuilder(); + } + defaultPackageStaxBuilder.process( xmlEventReader, xmlEvent ); + } + } + + private BeanStaxBuilder getNewBeanStaxBuilder() { + return new BeanStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions, defaultSequences ); + } + + private ConstraintDefinitionStaxBuilder getNewConstraintDefinitionStaxBuilder() { + return new ConstraintDefinitionStaxBuilder( classLoadingHelper, constraintHelper, defaultPackageStaxBuilder ); + } + + public void build(Set> processedClasses, Map, Set> constrainedElementsByType, Set alreadyProcessedConstraintDefinitions) { + constraintDefinitionStaxBuilders.forEach( builder -> builder.build( alreadyProcessedConstraintDefinitions ) ); + beanStaxBuilders.forEach( builder -> builder.build( processedClasses, constrainedElementsByType ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintTypeStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintTypeStaxBuilder.java new file mode 100644 index 0000000000..862a1226ad --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ConstraintTypeStaxBuilder.java @@ -0,0 +1,514 @@ +/* + * 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.xml.mapping; + +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.Payload; +import javax.validation.ValidationException; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.core.MetaConstraints; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetMethod; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for constraint information. Creates a constraint based on a set of given values. + * + * @author Hardy Ferentschik + * @author Marko Bekhta + */ +class ConstraintTypeStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final Pattern IS_ONLY_WHITESPACE = Pattern.compile( "\\s*" ); + + private static final String CONSTRAINT_QNAME_LOCAL_PART = "constraint"; + + private static final QName CONSTRAINT_ANNOTATION_QNAME = new QName( "annotation" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintHelper constraintHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + // Builders: + private final GroupsStaxBuilder groupsStaxBuilder; + private final PayloadStaxBuilder payloadStaxBuilder; + private final ConstraintParameterStaxBuilder constrainParameterStaxBuilder; + private final MessageStaxBuilder messageStaxBuilder; + + private final List builders; + + private String constraintAnnotation; + + ConstraintTypeStaxBuilder( + ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + this.groupsStaxBuilder = new GroupsStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.payloadStaxBuilder = new PayloadStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.constrainParameterStaxBuilder = new ConstraintParameterStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.messageStaxBuilder = new MessageStaxBuilder(); + + this.builders = Stream.of( groupsStaxBuilder, payloadStaxBuilder, constrainParameterStaxBuilder, messageStaxBuilder ) + .collect( Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList ) ); + + } + + @Override + protected String getAcceptableQName() { + return CONSTRAINT_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + StartElement startElement = xmlEvent.asStartElement(); + constraintAnnotation = readAttribute( startElement, CONSTRAINT_ANNOTATION_QNAME ).get(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( CONSTRAINT_QNAME_LOCAL_PART ) ) ) { + XMLEvent currentEvent = xmlEvent; + builders.forEach( builder -> builder.process( xmlEventReader, currentEvent ) ); + xmlEvent = xmlEventReader.nextEvent(); + } + } + + @SuppressWarnings("unchecked") + MetaConstraint build(ConstraintLocation constraintLocation, java.lang.annotation.ElementType type, ConstraintDescriptorImpl.ConstraintType constraintType) { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + + Class annotationClass; + try { + annotationClass = (Class) classLoadingHelper.loadClass( constraintAnnotation, defaultPackage ); + } + catch (ValidationException e) { + throw LOG.getUnableToLoadConstraintAnnotationClassException( constraintAnnotation, e ); + } + ConstraintAnnotationDescriptor.Builder annotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( annotationClass ); + + // set common things to all constraints: + Optional message = messageStaxBuilder.build(); + if ( message.isPresent() ) { + annotationDescriptorBuilder.setMessage( message.get() ); + } + annotationDescriptorBuilder.setGroups( groupsStaxBuilder.build() ) + .setPayload( payloadStaxBuilder.build() ); + + // set constraint specific attributes: + Map parameters = constrainParameterStaxBuilder.build( annotationClass ); + for ( Map.Entry parameter : parameters.entrySet() ) { + annotationDescriptorBuilder.setAttribute( parameter.getKey(), parameter.getValue() ); + } + + ConstraintAnnotationDescriptor annotationDescriptor; + try { + annotationDescriptor = annotationDescriptorBuilder.build(); + } + catch (RuntimeException e) { + throw LOG.getUnableToCreateAnnotationForConfiguredConstraintException( e ); + } + + // we set initially ConstraintOrigin.DEFINED_LOCALLY for all xml configured constraints + // later we will make copies of this constraint descriptor when needed and adjust the ConstraintOrigin + ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( + constraintHelper, constraintLocation.getMember(), annotationDescriptor, type, constraintType + ); + + return MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor, constraintLocation ); + } + + private static class MessageStaxBuilder extends AbstractOneLineStringStaxBuilder { + + private static final String MESSAGE_PACKAGE_QNAME = "message"; + + @Override + protected String getAcceptableQName() { + return MESSAGE_PACKAGE_QNAME; + } + } + + private static class ConstraintParameterStaxBuilder extends AnnotationParameterStaxBuilder { + + private static final String ELEMENT_QNAME_LOCAL_PART = "element"; + private static final QName NAME_QNAME = new QName( "name" ); + + public ConstraintParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + protected String getAcceptableQName() { + return ELEMENT_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + String name = readAttribute( xmlEvent.asStartElement(), NAME_QNAME ).get(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( ELEMENT_QNAME_LOCAL_PART ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + readElement( xmlEventReader, xmlEvent, name ); + } + } + + @Override + protected void checkNameIsValid(String name) { + if ( ConstraintHelper.MESSAGE.equals( name ) || ConstraintHelper.GROUPS.equals( name ) || ConstraintHelper.PAYLOAD.equals( name ) ) { + throw LOG.getReservedParameterNamesException( ConstraintHelper.MESSAGE, ConstraintHelper.GROUPS, ConstraintHelper.PAYLOAD ); + } + } + + public Map build(Class annotationClass) { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + Map builtParameters = new HashMap<>(); + for ( Map.Entry> parameter : parameters.entrySet() ) { + builtParameters.put( + parameter.getKey(), + getElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + for ( Map.Entry> parameter : annotationParameters.entrySet() ) { + builtParameters.put( + parameter.getKey(), + getAnnotationElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + + return builtParameters; + } + } + + private static class AnnotationParameterStaxBuilder extends AbstractStaxBuilder { + + private static final String ANNOTATION_QNAME_LOCAL_PART = "annotation"; + private static final String ELEMENT_QNAME_LOCAL_PART = "element"; + private static final String VALUE_QNAME_LOCAL_PART = "value"; + private static final QName NAME_QNAME = new QName( "name" ); + + private final ClassLoadingHelper classLoadingHelper; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + protected Map> parameters; + protected Map> annotationParameters; + + public AnnotationParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + + this.parameters = new HashMap<>(); + this.annotationParameters = new HashMap<>(); + } + + @Override + protected String getAcceptableQName() { + return ANNOTATION_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( ANNOTATION_QNAME_LOCAL_PART ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( xmlEvent.isStartElement() ) { + StartElement startElement = xmlEvent.asStartElement(); + if ( startElement.getName().getLocalPart().equals( ELEMENT_QNAME_LOCAL_PART ) ) { + String name = readAttribute( xmlEvent.asStartElement(), NAME_QNAME ).get(); + + // we put empty collection here in case the corresponding string element in xml is empty + // if there will be a value it will get merged in this#addParameterValue() + parameters.put( name, Collections.emptyList() ); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( ELEMENT_QNAME_LOCAL_PART ) ) ) { + readElement( xmlEventReader, xmlEvent, name ); + xmlEvent = xmlEventReader.nextEvent(); + } + } + } + } + } + + protected void readElement(XMLEventReader xmlEventReader, XMLEvent xmlEvent, String name) throws XMLStreamException { + // need to check the next element + if ( xmlEvent.isCharacters() && !xmlEvent.asCharacters().getData().trim().isEmpty() ) { + // in case it's a value - read it + StringBuilder stringBuilder = new StringBuilder( xmlEvent.asCharacters().getData() ); + while ( xmlEventReader.peek().isCharacters() ) { + xmlEvent = xmlEventReader.nextEvent(); + stringBuilder.append( xmlEvent.asCharacters().getData() ); + } + addParameterValue( name, stringBuilder.toString().trim() ); + } + else if ( xmlEvent.isStartElement() ) { + StartElement startElement = xmlEvent.asStartElement(); + // in case of multi-valued parameter read value + if ( startElement.getName().getLocalPart().equals( VALUE_QNAME_LOCAL_PART ) ) { + addParameterValue( name, readSingleElement( xmlEventReader ) ); + } + else if ( startElement.getName().getLocalPart().equals( ANNOTATION_QNAME_LOCAL_PART ) ) { + addAnnotationParameterValue( name, xmlEventReader, xmlEvent ); + } + } + } + + protected void addAnnotationParameterValue(String name, XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + checkNameIsValid( name ); + + AnnotationParameterStaxBuilder annotationParameterStaxBuilder = new AnnotationParameterStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + annotationParameterStaxBuilder.process( xmlEventReader, xmlEvent ); + + annotationParameters.merge( + name, + Collections.singletonList( annotationParameterStaxBuilder ), + (v1, v2) -> Stream.concat( v1.stream(), v2.stream() ).collect( Collectors.toList() ) + ); + } + + protected void addParameterValue(String name, String value) { + checkNameIsValid( name ); + parameters.merge( + name, + Collections.singletonList( value ), + (v1, v2) -> Stream.concat( v1.stream(), v2.stream() ).collect( Collectors.toList() ) + ); + } + + protected void checkNameIsValid(String name) { + // in case of simple annotation - any name is acceptable + } + + public Annotation build(Class annotationClass, String defaultPackage) { + AnnotationDescriptor.Builder annotationDescriptorBuilder = new AnnotationDescriptor.Builder<>( annotationClass ); + + for ( Map.Entry> parameter : parameters.entrySet() ) { + annotationDescriptorBuilder.setAttribute( + parameter.getKey(), + getElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + for ( Map.Entry> parameter : annotationParameters.entrySet() ) { + annotationDescriptorBuilder.setAttribute( + parameter.getKey(), + getAnnotationElementValue( parameter.getValue(), annotationClass, parameter.getKey(), defaultPackage ) + ); + } + + return annotationDescriptorBuilder.build().getAnnotation(); + } + + protected Object getElementValue(List parsedParameters, Class annotationClass, String name, String defaultPackage) { + List parameters = removeEmptyContentElements( parsedParameters ); + + Class returnType = getAnnotationParameterType( annotationClass, name ); + boolean isArray = returnType.isArray(); + if ( !isArray ) { + if ( parameters.size() == 0 ) { + return ""; + } + else if ( parameters.size() > 1 ) { + throw LOG.getAttemptToSpecifyAnArrayWhereSingleValueIsExpectedException(); + } + return convertStringToReturnType( parameters.get( 0 ), returnType, defaultPackage ); + } + else { + return parameters.stream().map( value -> convertStringToReturnType( value, returnType.getComponentType(), defaultPackage ) ) + .toArray( size -> (Object[]) Array.newInstance( returnType.getComponentType(), size ) ); + } + } + + @SuppressWarnings("unchecked") + protected Object getAnnotationElementValue(List parameters, Class annotationClass, String name, String defaultPackage) { + Class returnType = getAnnotationParameterType( annotationClass, name ); + boolean isArray = returnType.isArray(); + if ( !isArray ) { + if ( parameters.size() == 0 ) { + throw LOG.getEmptyElementOnlySupportedWhenCharSequenceIsExpectedExpection(); + } + else if ( parameters.size() > 1 ) { + throw LOG.getAttemptToSpecifyAnArrayWhereSingleValueIsExpectedException(); + } + return parameters.get( 0 ).build( (Class) returnType, defaultPackage ); + } + else { + return parameters.stream().map( value -> value.build( (Class) returnType.getComponentType(), defaultPackage ) ) + .toArray( size -> (Object[]) Array.newInstance( returnType.getComponentType(), size ) ); + } + } + + private static List removeEmptyContentElements(List params) { + return params.stream().filter( content -> !IS_ONLY_WHITESPACE.matcher( content ).matches() ) + .collect( Collectors.toList() ); + } + + private static Class getAnnotationParameterType(Class annotationClass, String name) { + Method m = run( GetMethod.action( annotationClass, name ) ); + if ( m == null ) { + throw LOG.getAnnotationDoesNotContainAParameterException( annotationClass, name ); + } + return m.getReturnType(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Object convertStringToReturnType(String value, Class returnType, String defaultPackage) { + Object returnValue; + if ( returnType == byte.class ) { + try { + returnValue = Byte.parseByte( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "byte", e ); + } + } + else if ( returnType == short.class ) { + try { + returnValue = Short.parseShort( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "short", e ); + } + } + else if ( returnType == int.class ) { + try { + returnValue = Integer.parseInt( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "int", e ); + } + } + else if ( returnType == long.class ) { + try { + returnValue = Long.parseLong( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "long", e ); + } + } + else if ( returnType == float.class ) { + try { + returnValue = Float.parseFloat( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "float", e ); + } + } + else if ( returnType == double.class ) { + try { + returnValue = Double.parseDouble( value ); + } + catch (NumberFormatException e) { + throw LOG.getInvalidNumberFormatException( "double", e ); + } + } + else if ( returnType == boolean.class ) { + returnValue = Boolean.parseBoolean( value ); + } + else if ( returnType == char.class ) { + if ( value.length() != 1 ) { + throw LOG.getInvalidCharValueException( value ); + } + returnValue = value.charAt( 0 ); + } + else if ( returnType == String.class ) { + returnValue = value; + } + else if ( returnType == Class.class ) { + returnValue = classLoadingHelper.loadClass( value, defaultPackage ); + } + else { + try { + Class enumClass = (Class) returnType; + returnValue = Enum.valueOf( enumClass, value ); + } + catch (ClassCastException e) { + throw LOG.getInvalidReturnTypeException( returnType, e ); + } + } + return returnValue; + } + + /** + * 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 static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } + } + + private static class GroupsStaxBuilder extends AbstractMultiValuedElementStaxBuilder { + + private static final String GROUPS_QNAME_LOCAL_PART = "groups"; + + private GroupsStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + public void verifyClass(Class clazz) { + // do nothing + } + + @Override + protected String getAcceptableQName() { + return GROUPS_QNAME_LOCAL_PART; + } + } + + private static class PayloadStaxBuilder extends AbstractMultiValuedElementStaxBuilder { + + private static final String PAYLOAD_QNAME_LOCAL_PART = "payload"; + + private PayloadStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + super( classLoadingHelper, defaultPackageStaxBuilder ); + } + + @Override + public void verifyClass(Class payload) { + if ( !Payload.class.isAssignableFrom( payload ) ) { + throw LOG.getWrongPayloadClassException( payload ); + } + } + + @Override + protected String getAcceptableQName() { + return PAYLOAD_QNAME_LOCAL_PART; + } + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.java new file mode 100644 index 0000000000..fd424cd664 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeConfigurationBuilder.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.internal.xml.mapping; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; + +/** + * Builds the aggregated cascading and type argument constraints configuration from the {@link ContainerElementTypeStaxBuilder} elements. + * + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ContainerElementTypeConfigurationBuilder { + + private final List containerElementTypeStaxBuilders; + private final Set configuredPaths; + + ContainerElementTypeConfigurationBuilder() { + this.containerElementTypeStaxBuilders = new ArrayList<>(); + this.configuredPaths = new HashSet<>(); + } + + public void add(ContainerElementTypeStaxBuilder containerElementTypeStaxBuilder) { + containerElementTypeStaxBuilders.add( containerElementTypeStaxBuilder ); + } + + ContainerElementTypeConfiguration build(ConstraintLocation parentConstraintLocation, Type enclosingType) { + return build( ContainerElementTypePath.root(), parentConstraintLocation, enclosingType ); + } + + private ContainerElementTypeConfiguration build(ContainerElementTypePath parentConstraintElementTypePath, + ConstraintLocation parentConstraintLocation, Type enclosingType) { + return containerElementTypeStaxBuilders.stream() + .map( builder -> builder.build( configuredPaths, parentConstraintElementTypePath, parentConstraintLocation, enclosingType ) ) + .reduce( ContainerElementTypeConfiguration.EMPTY_CONFIGURATION, ContainerElementTypeConfiguration::merge ); + } + + static class ContainerElementTypeConfiguration { + + public static final ContainerElementTypeConfiguration EMPTY_CONFIGURATION = new ContainerElementTypeConfiguration( Collections.emptySet(), Collections.emptyMap() ); + + private final Set> metaConstraints; + + private final Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder; + + ContainerElementTypeConfiguration(Set> metaConstraints, Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) { + this.metaConstraints = metaConstraints; + this.containerElementTypesCascadingMetaDataBuilder = containerElementTypesCascadingMetaData; + } + + public Set> getMetaConstraints() { + return metaConstraints; + } + + public Map, CascadingMetaDataBuilder> getTypeParametersCascadingMetaData() { + return containerElementTypesCascadingMetaDataBuilder; + } + + public static ContainerElementTypeConfiguration merge(ContainerElementTypeConfiguration l, ContainerElementTypeConfiguration r) { + return new ContainerElementTypeConfiguration( + Stream.concat( l.metaConstraints.stream(), r.metaConstraints.stream() ).collect( Collectors.toSet() ), + Stream.concat( l.containerElementTypesCascadingMetaDataBuilder.entrySet().stream(), r.containerElementTypesCascadingMetaDataBuilder.entrySet().stream() ) + .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) ) + ); + } + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypePath.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypePath.java similarity index 96% rename from engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypePath.java rename to engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypePath.java index 61333a6017..ce03e3d911 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/xml/ContainerElementTypePath.java +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypePath.java @@ -4,7 +4,7 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.xml; +package org.hibernate.validator.internal.xml.mapping; import java.util.ArrayList; import java.util.List; diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java new file mode 100644 index 0000000000..1c24591a22 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ContainerElementTypeStaxBuilder.java @@ -0,0 +1,220 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ArrayElement; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.CollectionHelper; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.TypeHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builds the cascading and type argument constraints configuration from the {@code } elements. + * + * @author Guillaume Smet + * @author Marko Bekhta + */ +class ContainerElementTypeStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String CONTAINER_ELEMENT_TYPE_QNAME_LOCAL_PART = "container-element-type"; + private static final QName TYPE_ARGUMENT_INDEX_QNAME = new QName( "type-argument-index" ); + + private final ClassLoadingHelper classLoadingHelper; + private final ConstraintHelper constraintHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private Integer typeArgumentIndex; + private final ValidStaxBuilder validStaxBuilder; + private final List constraintTypeStaxBuilders; + private final GroupConversionStaxBuilder groupConversionBuilder; + private final List containerElementTypeConfigurationStaxBuilders; + + ContainerElementTypeStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + this.groupConversionBuilder = new GroupConversionStaxBuilder( classLoadingHelper, defaultPackageStaxBuilder ); + this.validStaxBuilder = new ValidStaxBuilder(); + this.constraintTypeStaxBuilders = new ArrayList<>(); + this.containerElementTypeConfigurationStaxBuilders = new ArrayList<>(); + + } + + @Override + protected String getAcceptableQName() { + return CONTAINER_ELEMENT_TYPE_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + Optional typeArgumentIndex = readAttribute( xmlEvent.asStartElement(), TYPE_ARGUMENT_INDEX_QNAME ); + if ( typeArgumentIndex.isPresent() ) { + this.typeArgumentIndex = Integer.parseInt( typeArgumentIndex.get() ); + } + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + ContainerElementTypeStaxBuilder containerElementTypeConfigurationStaxBuilder = getNewContainerElementTypeConfigurationStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + validStaxBuilder.process( xmlEventReader, xmlEvent ); + groupConversionBuilder.process( xmlEventReader, xmlEvent ); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + if ( containerElementTypeConfigurationStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + containerElementTypeConfigurationStaxBuilders.add( containerElementTypeConfigurationStaxBuilder ); + containerElementTypeConfigurationStaxBuilder = getNewContainerElementTypeConfigurationStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder ); + } + + private ContainerElementTypeStaxBuilder getNewContainerElementTypeConfigurationStaxBuilder() { + return new ContainerElementTypeStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder ); + } + + public ContainerElementTypeConfiguration build(Set configuredPaths, + ContainerElementTypePath parentConstraintElementTypePath, + ConstraintLocation parentConstraintLocation, Type enclosingType) { + // HV-1428 Container element support is disabled for arrays + if ( TypeHelper.isArray( enclosingType ) ) { + throw LOG.getContainerElementConstraintsAndCascadedValidationNotSupportedOnArraysException( enclosingType ); + } + + if ( !( enclosingType instanceof ParameterizedType ) && !TypeHelper.isArray( enclosingType ) ) { + throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( enclosingType ); + } + + Map, CascadingMetaDataBuilder> containerElementTypesCascadingMetaDataBuilder = + CollectionHelper.newHashMap( containerElementTypeConfigurationStaxBuilders.size() ); + + boolean isArray = TypeHelper.isArray( enclosingType ); + TypeVariable[] typeParameters = isArray ? new TypeVariable[0] : ReflectionHelper.getClassFromType( enclosingType ).getTypeParameters(); + + Integer typeArgumentIndex = getTypeArgumentIndex( typeParameters, isArray, enclosingType ); + + ContainerElementTypePath constraintElementTypePath = ContainerElementTypePath.of( parentConstraintElementTypePath, typeArgumentIndex ); + boolean configuredBefore = !configuredPaths.add( constraintElementTypePath ); + if ( configuredBefore ) { + throw LOG.getContainerElementTypeHasAlreadyBeenConfiguredViaXmlMappingConfigurationException( parentConstraintLocation, constraintElementTypePath ); + } + + TypeVariable typeParameter = getTypeParameter( typeParameters, typeArgumentIndex, isArray, enclosingType ); + Type containerElementType = getContainerElementType( enclosingType, typeArgumentIndex, isArray ); + ConstraintLocation containerElementTypeConstraintLocation = ConstraintLocation.forTypeArgument( parentConstraintLocation, typeParameter, + containerElementType + ); + + ContainerElementTypeConfiguration nestedContainerElementTypeConfiguration = containerElementTypeConfigurationStaxBuilders.stream() + .map( nested -> nested.build( configuredPaths, constraintElementTypePath, containerElementTypeConstraintLocation, containerElementType ) ) + .reduce( ContainerElementTypeConfiguration.EMPTY_CONFIGURATION, ContainerElementTypeConfiguration::merge ); + + boolean isCascaded = validStaxBuilder.build(); + + containerElementTypesCascadingMetaDataBuilder.put( typeParameter, new CascadingMetaDataBuilder( enclosingType, typeParameter, isCascaded, + nestedContainerElementTypeConfiguration.getTypeParametersCascadingMetaData(), + groupConversionBuilder.build() + ) + ); + + return new ContainerElementTypeConfiguration( + Stream.concat( + constraintTypeStaxBuilders.stream() + .map( + builder -> builder.build( + containerElementTypeConstraintLocation, + java.lang.annotation.ElementType.TYPE_USE, + null + ) + ), + nestedContainerElementTypeConfiguration.getMetaConstraints().stream() + ).collect( Collectors.toSet() ), + containerElementTypesCascadingMetaDataBuilder + ); + } + + private Integer getTypeArgumentIndex(TypeVariable[] typeParameters, boolean isArray, Type enclosingType) { + if ( isArray ) { + return null; + } + + if ( typeArgumentIndex == null ) { + if ( typeParameters.length > 1 ) { + throw LOG.getNoTypeArgumentIndexIsGivenForTypeWithMultipleTypeArgumentsException( enclosingType ); + } + return 0; + } + + return typeArgumentIndex; + } + + private TypeVariable getTypeParameter(TypeVariable[] typeParameters, Integer typeArgumentIndex, boolean isArray, Type enclosingType) { + TypeVariable typeParameter; + if ( !isArray ) { + if ( typeArgumentIndex > typeParameters.length - 1 ) { + throw LOG.getInvalidTypeArgumentIndexException( enclosingType, typeArgumentIndex ); + } + + typeParameter = typeParameters[typeArgumentIndex]; + } + else { + typeParameter = new ArrayElement( enclosingType ); + } + return typeParameter; + } + + private Type getContainerElementType(Type enclosingType, Integer typeArgumentIndex, boolean isArray) { + Type containerElementType; + if ( !isArray ) { + containerElementType = ( (ParameterizedType) enclosingType ).getActualTypeArguments()[typeArgumentIndex]; + } + else { + containerElementType = TypeHelper.getComponentType( enclosingType ); + } + return containerElementType; + } + + public Integer getTypeArgumentIndex() { + return null; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/CrossParameterStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/CrossParameterStaxBuilder.java new file mode 100644 index 0000000000..30397e1009 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/CrossParameterStaxBuilder.java @@ -0,0 +1,104 @@ +/* + * 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.xml.mapping; + +import java.lang.reflect.Executable; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for cross parameters. + * + * @author Marko Bekhta + */ +class CrossParameterStaxBuilder extends AbstractStaxBuilder { + + private static final String CROSS_PARAMETER_QNAME_LOCAL_PART = "cross-parameter"; + private static final QName IGNORE_ANNOTATIONS_QNAME = new QName( "ignore-annotations" ); + + protected final ClassLoadingHelper classLoadingHelper; + protected final ConstraintHelper constraintHelper; + protected final TypeResolutionHelper typeResolutionHelper; + protected final ValueExtractorManager valueExtractorManager; + protected final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + protected final AnnotationProcessingOptionsImpl annotationProcessingOptions; + + protected Optional ignoreAnnotations; + protected final List constraintTypeStaxBuilders; + + CrossParameterStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + + this.annotationProcessingOptions = annotationProcessingOptions; + + this.constraintTypeStaxBuilders = new ArrayList<>(); + } + + @Override + protected String getAcceptableQName() { + return CROSS_PARAMETER_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) throws XMLStreamException { + ignoreAnnotations = readAttribute( xmlEvent.asStartElement(), IGNORE_ANNOTATIONS_QNAME ).map( Boolean::parseBoolean ); + ConstraintTypeStaxBuilder constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + while ( !( xmlEvent.isEndElement() && xmlEvent.asEndElement().getName().getLocalPart().equals( getAcceptableQName() ) ) ) { + xmlEvent = xmlEventReader.nextEvent(); + if ( constraintTypeStaxBuilder.process( xmlEventReader, xmlEvent ) ) { + constraintTypeStaxBuilders.add( constraintTypeStaxBuilder ); + constraintTypeStaxBuilder = getNewConstraintTypeStaxBuilder(); + } + } + } + + private ConstraintTypeStaxBuilder getNewConstraintTypeStaxBuilder() { + return new ConstraintTypeStaxBuilder( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder ); + } + + Set> build(Executable executable) { + + ConstraintLocation constraintLocation = ConstraintLocation.forCrossParameter( executable ); + + Set> crossParameterConstraints = constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, java.lang.annotation.ElementType.METHOD, ConstraintType.CROSS_PARAMETER ) ) + .collect( Collectors.toSet() ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsForCrossParameterConstraint( + executable, + ignoreAnnotations.get() + ); + } + + return crossParameterConstraints; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/DefaultPackageStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/DefaultPackageStaxBuilder.java new file mode 100644 index 0000000000..a676c0e5c4 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/DefaultPackageStaxBuilder.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.internal.xml.mapping; + +import java.util.Optional; + +/** + * Builder for default package. Provides a default package name as defined in the xml + * or {@link Optional#empty()} if none was provided. + * + * @author Marko Bekhta + */ +class DefaultPackageStaxBuilder extends AbstractOneLineStringStaxBuilder { + + private static final String DEFAULT_PACKAGE_QNAME = "default-package"; + + @Override + protected String getAcceptableQName() { + return DEFAULT_PACKAGE_QNAME; + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/GroupConversionStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/GroupConversionStaxBuilder.java new file mode 100644 index 0000000000..f3ca903f8a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/GroupConversionStaxBuilder.java @@ -0,0 +1,106 @@ +/* + * 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.xml.mapping; + +import java.lang.invoke.MethodHandles; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.groups.Default; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * Builder for group conversions. + * + * @author Marko Bekhta + */ +class GroupConversionStaxBuilder extends AbstractStaxBuilder { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private static final String GROUP_CONVERSION_TYPE_QNAME_LOCAL_PART = "convert-group"; + private static final QName FROM_QNAME = new QName( "from" ); + private static final QName TO_QNAME = new QName( "to" ); + + private static final String DEFAULT_GROUP_NAME = Default.class.getName(); + + private final ClassLoadingHelper classLoadingHelper; + private final DefaultPackageStaxBuilder defaultPackageStaxBuilder; + + private final Map> groupConversionRules; + + GroupConversionStaxBuilder(ClassLoadingHelper classLoadingHelper, DefaultPackageStaxBuilder defaultPackageStaxBuilder) { + this.classLoadingHelper = classLoadingHelper; + this.defaultPackageStaxBuilder = defaultPackageStaxBuilder; + this.groupConversionRules = new HashMap<>(); + } + + @Override + protected String getAcceptableQName() { + return GROUP_CONVERSION_TYPE_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + StartElement startElement = xmlEvent.asStartElement(); + String from = readAttribute( startElement, FROM_QNAME ).orElse( DEFAULT_GROUP_NAME ); + String to = readAttribute( startElement, TO_QNAME ).get(); + groupConversionRules.merge( + from, + Collections.singletonList( to ), + (v1, v2) -> Stream.concat( v1.stream(), v2.stream() ).collect( Collectors.toList() ) + ); + } + + Map, Class> build() { + String defaultPackage = defaultPackageStaxBuilder.build().orElse( "" ); + + Map, List>> resultingMapping = groupConversionRules.entrySet().stream() + .collect( + // Using groupingBy collector to prevent possible loss of information + // as a string value in `from` could possibly contain both qualified and non qualified + // version of a same class from the default package. + Collectors.groupingBy( + entry -> classLoadingHelper.loadClass( entry.getKey(), defaultPackage ), + Collectors.collectingAndThen( + Collectors.toList(), + entries -> entries.stream() + .flatMap( entry -> entry.getValue().stream() ) + .map( className -> classLoadingHelper.loadClass( className, defaultPackage ) ) + .collect( Collectors.toList() ) + ) + + ) + ); + // in case of any duplicates in conversion rules we need to throw an exception: + for ( Map.Entry, List>> entry : resultingMapping.entrySet() ) { + if ( entry.getValue().size() > 1 ) { + throw LOG.getMultipleGroupConversionsForSameSourceException( + entry.getKey(), + entry.getValue() + ); + } + } + + return resultingMapping.entrySet().stream() + .collect( Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().get( 0 ) + ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/MappingXmlParser.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/MappingXmlParser.java new file mode 100644 index 0000000000..6cd489a734 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/MappingXmlParser.java @@ -0,0 +1,188 @@ +/* + * 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.xml.mapping; + +import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; +import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandles; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.Validator; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; +import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; +import org.hibernate.validator.internal.xml.CloseIgnoringInputStream; +import org.hibernate.validator.internal.xml.XmlParserHelper; +import org.xml.sax.SAXException; + +/** + * XML parser for validation-mapping files. + * + * @author Hardy Ferentschik + * @author Marko Bekhta + */ +public class MappingXmlParser { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final Set> processedClasses = newHashSet(); + private final ConstraintHelper constraintHelper; + private final TypeResolutionHelper typeResolutionHelper; + private final ValueExtractorManager valueExtractorManager; + private final AnnotationProcessingOptionsImpl annotationProcessingOptions; + private final Map, List>> defaultSequences; + private final Map, Set> constrainedElements; + + private final XmlParserHelper xmlParserHelper; + + private final ClassLoadingHelper classLoadingHelper; + + private static final Map SCHEMAS_BY_VERSION = Collections.unmodifiableMap( getSchemasByVersion() ); + + private static Map getSchemasByVersion() { + Map schemasByVersion = new HashMap<>(); + + schemasByVersion.put( "1.0", "META-INF/validation-mapping-1.0.xsd" ); + schemasByVersion.put( "1.1", "META-INF/validation-mapping-1.1.xsd" ); + schemasByVersion.put( "2.0", "META-INF/validation-mapping-2.0.xsd" ); + + return schemasByVersion; + } + + public MappingXmlParser(ConstraintHelper constraintHelper, TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + ClassLoader externalClassLoader) { + this.constraintHelper = constraintHelper; + this.typeResolutionHelper = typeResolutionHelper; + this.valueExtractorManager = valueExtractorManager; + this.annotationProcessingOptions = new AnnotationProcessingOptionsImpl(); + this.defaultSequences = newHashMap(); + this.constrainedElements = newHashMap(); + this.xmlParserHelper = new XmlParserHelper(); + this.classLoadingHelper = new ClassLoadingHelper( externalClassLoader, run( GetClassLoader.fromContext() ) ); + } + + /** + * Parses the given set of input stream representing XML constraint + * mappings. + * + * @param mappingStreams The streams to parse. Must support the mark/reset contract. + */ + public final void parse(Set mappingStreams) { + ClassLoader previousTccl = run( GetClassLoader.fromContext() ); + + try { + run( SetContextClassLoader.action( MappingXmlParser.class.getClassLoader() ) ); + + Set alreadyProcessedConstraintDefinitions = newHashSet(); + for ( InputStream in : mappingStreams ) { + // the InputStreams passed in parameters support mark and reset + in.mark( Integer.MAX_VALUE ); + + XMLEventReader xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); + String schemaVersion = xmlParserHelper.getSchemaVersion( "constraint mapping file", xmlEventReader ); + xmlEventReader.close(); + + in.reset(); + + // The validation is done first as StAX builders used below are assuming that the XML file is correct and don't + // do any validation of the input. + String schemaResourceName = getSchemaResourceName( schemaVersion ); + Schema schema = xmlParserHelper.getSchema( schemaResourceName ); + if ( schema == null ) { + throw LOG.unableToGetXmlSchema( schemaResourceName ); + } + + Validator validator = schema.newValidator(); + validator.validate( new StreamSource( new CloseIgnoringInputStream( in ) ) ); + + in.reset(); + + ConstraintMappingsStaxBuilder constraintMappingsStaxBuilder = new ConstraintMappingsStaxBuilder( + classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, + annotationProcessingOptions, defaultSequences + ); + + xmlEventReader = xmlParserHelper.createXmlEventReader( "constraint mapping file", new CloseIgnoringInputStream( in ) ); + + while ( xmlEventReader.hasNext() ) { + constraintMappingsStaxBuilder.process( xmlEventReader, xmlEventReader.nextEvent() ); + } + constraintMappingsStaxBuilder.build( processedClasses, constrainedElements, alreadyProcessedConstraintDefinitions ); + xmlEventReader.close(); + in.reset(); + } + } + catch (IOException | XMLStreamException | SAXException e) { + throw LOG.getErrorParsingMappingFileException( e ); + } + finally { + run( SetContextClassLoader.action( previousTccl ) ); + } + } + + public final Set> getXmlConfiguredClasses() { + return processedClasses; + } + + public final AnnotationProcessingOptions getAnnotationProcessingOptions() { + return annotationProcessingOptions; + } + + public final Set getConstrainedElementsForClass(Class beanClass) { + if ( constrainedElements.containsKey( beanClass ) ) { + return constrainedElements.get( beanClass ); + } + else { + return Collections.emptySet(); + } + } + + public final List> getDefaultSequenceForClass(Class beanClass) { + return defaultSequences.get( beanClass ); + } + + private String getSchemaResourceName(String schemaVersion) { + String schemaResource = SCHEMAS_BY_VERSION.get( schemaVersion ); + + if ( schemaResource == null ) { + throw LOG.getUnsupportedSchemaVersionException( "constraint mapping file", schemaVersion ); + } + + return schemaResource; + } + + /** + * 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 static T run(PrivilegedAction action) { + return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java new file mode 100644 index 0000000000..85cae903e2 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ReturnValueStaxBuilder.java @@ -0,0 +1,77 @@ +/* + * 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.xml.mapping; + +import java.lang.reflect.Executable; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.namespace.QName; + +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaDataBuilder; +import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; +import org.hibernate.validator.internal.metadata.core.ConstraintHelper; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.util.ExecutableHelper; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.xml.mapping.ContainerElementTypeConfigurationBuilder.ContainerElementTypeConfiguration; + +/** + * Builder for constraints on return value. + * + * @author Marko Bekhta + */ +class ReturnValueStaxBuilder extends AbstractConstrainedElementStaxBuilder { + + private static final String RETURN_VALUE_QNAME_LOCAL_PART = "return-value"; + + ReturnValueStaxBuilder(ClassLoadingHelper classLoadingHelper, ConstraintHelper constraintHelper, + TypeResolutionHelper typeResolutionHelper, ValueExtractorManager valueExtractorManager, + DefaultPackageStaxBuilder defaultPackageStaxBuilder, AnnotationProcessingOptionsImpl annotationProcessingOptions) { + super( classLoadingHelper, constraintHelper, typeResolutionHelper, valueExtractorManager, defaultPackageStaxBuilder, annotationProcessingOptions ); + } + + @Override + Optional getMainAttributeValueQname() { + return Optional.empty(); + } + + @Override + protected String getAcceptableQName() { + return RETURN_VALUE_QNAME_LOCAL_PART; + } + + CascadingMetaDataBuilder build( + Executable executable, + Set> returnValueConstraints, + Set> returnValueTypeArgumentConstraints) { + + ConstraintLocation constraintLocation = ConstraintLocation.forReturnValue( executable ); + returnValueConstraints.addAll( constraintTypeStaxBuilders.stream() + .map( builder -> builder.build( constraintLocation, ExecutableHelper.getElementType( executable ), ConstraintDescriptorImpl.ConstraintType.GENERIC ) ) + .collect( Collectors.toSet() ) ); + + ContainerElementTypeConfiguration containerElementTypeConfiguration = getContainerElementTypeConfiguration( ReflectionHelper.typeOf( executable ), constraintLocation ); + + returnValueTypeArgumentConstraints.addAll( containerElementTypeConfiguration.getMetaConstraints() ); + + // ignore annotations + if ( ignoreAnnotations.isPresent() ) { + annotationProcessingOptions.ignoreConstraintAnnotationsForReturnValue( + executable, + ignoreAnnotations.get() + ); + } + + return getCascadingMetaData( containerElementTypeConfiguration.getTypeParametersCascadingMetaData(), ReflectionHelper.typeOf( executable ) ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ValidStaxBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ValidStaxBuilder.java new file mode 100644 index 0000000000..7310645fdd --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/xml/mapping/ValidStaxBuilder.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.internal.xml.mapping; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.XMLEvent; + +import org.hibernate.validator.internal.xml.AbstractStaxBuilder; + +/** + * @author Marko Bekhta + */ +class ValidStaxBuilder extends AbstractStaxBuilder { + + private static final String VALID_QNAME_LOCAL_PART = "valid"; + private Boolean cascading; + + @Override + protected String getAcceptableQName() { + return VALID_QNAME_LOCAL_PART; + } + + @Override + protected void add(XMLEventReader xmlEventReader, XMLEvent xmlEvent) { + cascading = true; + } + + public boolean build() { + return cascading == null ? false : true; + } +} 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 2143a4adfc..ffab864d6d 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java @@ -20,6 +20,7 @@ import javax.validation.MessageInterpolator; import javax.validation.ValidationException; +import org.hibernate.validator.internal.engine.MessageInterpolatorContext; import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; import org.hibernate.validator.internal.engine.messageinterpolation.LocalizedMessage; import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; @@ -278,11 +279,16 @@ private String interpolateMessage(String message, Context context, Locale locale ); // resolve EL expressions (step 3) - resolvedMessage = interpolateExpression( - new TokenIterator( getParameterTokens( resolvedMessage, tokenizedELMessages, InterpolationTermType.EL ) ), - context, - locale - ); + // in the standard Hibernate Validator execution flow, the context is always an instance of + // HibernateMessageInterpolatorContext + // but it can be a spec Context in the Jakarta Bean Validation TCK. + if ( !( context instanceof HibernateMessageInterpolatorContext ) + || ( (MessageInterpolatorContext) context ).isExpressionLanguageEnabled() ) { + resolvedMessage = interpolateExpression( + new TokenIterator( getParameterTokens( resolvedMessage, tokenizedELMessages, InterpolationTermType.EL ) ), + context, + locale ); + } } // last but not least we have to take care of escaped literals diff --git a/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java b/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java index b216ff5161..05fbba5213 100644 --- a/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java +++ b/engine/src/main/java/org/hibernate/validator/messageinterpolation/HibernateMessageInterpolatorContext.java @@ -9,6 +9,7 @@ import java.util.Map; import javax.validation.MessageInterpolator; +import javax.validation.Path; /** * Extension to {@code MessageInterpolator.Context} which provides functionality @@ -40,4 +41,11 @@ public interface HibernateMessageInterpolatorContext extends MessageInterpolator * @since 5.4.1 */ Map getExpressionVariables(); + + /** + * @return the path to the validated constraint starting from the root bean + * + * @since 6.0.21 + */ + Path getPropertyPath(); } diff --git a/engine/src/main/java/org/hibernate/validator/metadata/BeanMetaDataClassNormalizer.java b/engine/src/main/java/org/hibernate/validator/metadata/BeanMetaDataClassNormalizer.java new file mode 100644 index 0000000000..d102783f2d --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/metadata/BeanMetaDataClassNormalizer.java @@ -0,0 +1,39 @@ +/* + * 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.metadata; + +import org.hibernate.validator.Incubating; + +/** + * Define how the validated class is normalized before being used as the key to get the bean metadata. + *

    + * In the case of the predefined scope validator factory, we have to register all the classes that will ever be + * validated. To validate method calls, frameworks usually generate proxies to intercept the calls. Such proxies might + * be hard to register in the predefined scope validator factory as they are generated code. + *

    + * This contract allows to normalize the class before obtaining the metadata from the + * {@code BeanMetaDataManager} so that we only have to register the original bean class and not the proxy + * class. + *

    + * Apart from avoiding the need to register the class, it also avoids generating unnecessary metadata for the proxy + * classes. + * + * @author Guillaume Smet + * @since 6.1 + */ +@Incubating +public interface BeanMetaDataClassNormalizer { + + /** + * Normalizes the provided class as the key used to get the bean metadata from the + * {@code BeanMetaDataManager}. + * + * @param beanClass the original bean class + * @return the normalized class + */ + Class normalize(Class beanClass); +} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties index c9d3ac8536..c3064d0066 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties @@ -28,10 +28,10 @@ org.hibernate.validator.constraints.Email.message = not a well org.hibernate.validator.constraints.ISBN.message = invalid ISBN org.hibernate.validator.constraints.Length.message = length must be between {min} and {max} org.hibernate.validator.constraints.CodePointLength.message = length must be between {min} and {max} -org.hibernate.validator.constraints.LuhnCheck.message = The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed -org.hibernate.validator.constraints.Mod10Check.message = The check digit for ${validatedValue} is invalid, Modulo 10 checksum failed -org.hibernate.validator.constraints.Mod11Check.message = The check digit for ${validatedValue} is invalid, Modulo 11 checksum failed -org.hibernate.validator.constraints.ModCheck.message = The check digit for ${validatedValue} is invalid, ${modType} checksum failed +org.hibernate.validator.constraints.LuhnCheck.message = the check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed +org.hibernate.validator.constraints.Mod10Check.message = the check digit for ${validatedValue} is invalid, Modulo 10 checksum failed +org.hibernate.validator.constraints.Mod11Check.message = the check digit for ${validatedValue} is invalid, Modulo 11 checksum failed +org.hibernate.validator.constraints.ModCheck.message = the check digit for ${validatedValue} is invalid, {modType} checksum failed org.hibernate.validator.constraints.NotBlank.message = may not be empty org.hibernate.validator.constraints.NotEmpty.message = may not be empty org.hibernate.validator.constraints.ParametersScriptAssert.message = script expression "{script}" didn't evaluate to true @@ -45,9 +45,9 @@ org.hibernate.validator.constraints.br.CNPJ.message = invalid Br org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF) org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card number -org.hibernate.validator.constraints.pl.REGON.message = Invalid Polish Taxpayer Identification Number (REGON) -org.hibernate.validator.constraints.pl.NIP.message = Invalid VAT Identification Number (NIP) -org.hibernate.validator.constraints.pl.PESEL.message = Invalid Polish National Identification Number (PESEL) +org.hibernate.validator.constraints.pl.REGON.message = invalid Polish Taxpayer Identification Number (REGON) +org.hibernate.validator.constraints.pl.NIP.message = invalid VAT Identification Number (NIP) +org.hibernate.validator.constraints.pl.PESEL.message = invalid Polish National Identification Number (PESEL) org.hibernate.validator.constraints.time.DurationMax.message = must be shorter than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties index c802fec1a1..46d5fba48d 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_de.properties @@ -1,6 +1,6 @@ javax.validation.constraints.AssertFalse.message = muss falsch sein javax.validation.constraints.AssertTrue.message = muss wahr sein -javax.validation.constraints.DecimalMax.message = muss kleiner ${strict == false ? 'oder gleich ' : ''}{value} sein +javax.validation.constraints.DecimalMax.message = muss kleiner ${inclusive == true ? 'oder gleich ' : ''}{value} sein javax.validation.constraints.DecimalMin.message = muss gr\u00F6\u00DFer ${inclusive == true ? 'oder gleich ' : ''}{value} sein javax.validation.constraints.Digits.message = numerischer Wert au\u00DFerhalb erlaubten Wertebereichs (<{integer} Ziffern>,<{fraction} Ziffern> erwartet) javax.validation.constraints.Email.message = keine g\u00FCltige E-Mail-Adresse diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties index cea34f85f2..d65b4f79bc 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_es.properties @@ -31,7 +31,7 @@ org.hibernate.validator.constraints.CodePointLength.message = la longitu org.hibernate.validator.constraints.LuhnCheck.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n Luhn M\u00F3dulo 10 fall\u00F3 org.hibernate.validator.constraints.Mod10Check.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n M\u00F3dulo 10 fall\u00F3 org.hibernate.validator.constraints.Mod11Check.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n M\u00F3dulo 11 fall\u00F3 -org.hibernate.validator.constraints.ModCheck.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n ${modType} fall\u00F3 +org.hibernate.validator.constraints.ModCheck.message = el d\u00EDgito de verificaci\u00F3n para ${validatedValue} es inv\u00E1lido, la suma de verificaci\u00F3n {modType} fall\u00F3 org.hibernate.validator.constraints.NotBlank.message = no puede estar vac\u00EDo org.hibernate.validator.constraints.NotEmpty.message = no puede estar vac\u00EDo org.hibernate.validator.constraints.ParametersScriptAssert.message = la expresi\u00F3n "{script}" no se ha evaluado como verdadera @@ -49,5 +49,5 @@ org.hibernate.validator.constraints.pl.REGON.message = n\u00FAmer org.hibernate.validator.constraints.pl.NIP.message = n\u00FAmero de identificaci\u00F3n VAT (NIP) inv\u00E1lido org.hibernate.validator.constraints.pl.PESEL.message = n\u00FAmero de identificaci\u00F3n nacional Polaco (PESEL) inv\u00E1lido -org.hibernate.validator.constraints.time.DurationMax.message = tiene que durar menos {inclusive == true ? ' o igual que' : ''}${days == 0 ? '' : days == 1 ? ' 1 d\u00EDa' : ' ' += days += ' d\u00EDs'}${hours == 0 ? '' : hours == 1 ? ' 1 hora' : ' ' += hours += ' horas'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuto' : ' ' += minutes += ' minutos'}${seconds == 0 ? '' : seconds == 1 ? ' 1 segundo' : ' ' += seconds += ' segundos'}${millis == 0 ? '' : millis == 1 ? ' 1 milisegundo' : ' ' += millis += ' milisegundos'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosegundo' : ' ' += nanos += ' nanosegundos'} -org.hibernate.validator.constraints.time.DurationMin.message = tiene que durar mas {inclusive == true ? ' o igual que' : ''}${days == 0 ? '' : days == 1 ? ' 1 d\u00EDa' : ' ' += days += ' d\u00EDs'}${hours == 0 ? '' : hours == 1 ? ' 1 hora' : ' ' += hours += ' horas'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuto' : ' ' += minutes += ' minutos'}${seconds == 0 ? '' : seconds == 1 ? ' 1 segundo' : ' ' += seconds += ' segundos'}${millis == 0 ? '' : millis == 1 ? ' 1 milisegundo' : ' ' += millis += ' milisegundos'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosegundo' : ' ' += nanos += ' nanosegundos'} +org.hibernate.validator.constraints.time.DurationMax.message = tiene que durar menos${inclusive == true ? ' o igual que' : ''}${days == 0 ? '' : days == 1 ? ' 1 d\u00EDa' : ' ' += days += ' d\u00EDs'}${hours == 0 ? '' : hours == 1 ? ' 1 hora' : ' ' += hours += ' horas'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuto' : ' ' += minutes += ' minutos'}${seconds == 0 ? '' : seconds == 1 ? ' 1 segundo' : ' ' += seconds += ' segundos'}${millis == 0 ? '' : millis == 1 ? ' 1 milisegundo' : ' ' += millis += ' milisegundos'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosegundo' : ' ' += nanos += ' nanosegundos'} +org.hibernate.validator.constraints.time.DurationMin.message = tiene que durar mas${inclusive == true ? ' o igual que' : ''}${days == 0 ? '' : days == 1 ? ' 1 d\u00EDa' : ' ' += days += ' d\u00EDs'}${hours == 0 ? '' : hours == 1 ? ' 1 hora' : ' ' += hours += ' horas'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuto' : ' ' += minutes += ' minutos'}${seconds == 0 ? '' : seconds == 1 ? ' 1 segundo' : ' ' += seconds += ' segundos'}${millis == 0 ? '' : millis == 1 ? ' 1 milisegundo' : ' ' += millis += ' milisegundos'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanosegundo' : ' ' += nanos += ' nanosegundos'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fa.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fa.properties index f86a395bb1..373db2a5ae 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fa.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fa.properties @@ -30,7 +30,7 @@ org.hibernate.validator.constraints.CodePointLength.message = \u0637\u06 org.hibernate.validator.constraints.LuhnCheck.message = \u0645\u0642\u062F\u0627\u0631 ${validatedValue} \u0628\u0627 Luhn Modulo 10 \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u0646\u062F\u0627\u0631\u062F org.hibernate.validator.constraints.Mod10Check.message = \u0645\u0642\u062F\u0627\u0631 ${validatedValue} \u0628\u0627 Modulo 10 \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u0646\u062F\u0627\u0631\u062F org.hibernate.validator.constraints.Mod11Check.message = \u0645\u0642\u062F\u0627\u0631 ${validatedValue} \u0628\u0627 Modulo 11 \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u0646\u062F\u0627\u0631\u062F -org.hibernate.validator.constraints.ModCheck.message = \u0645\u0642\u062F\u0627\u0631 ${validatedValue} \u0628\u0627 ${modType} \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u0646\u062F\u0627\u0631\u062F +org.hibernate.validator.constraints.ModCheck.message = \u0645\u0642\u062F\u0627\u0631 ${validatedValue} \u0628\u0627 {modType} \u0647\u0645\u062E\u0648\u0627\u0646\u06CC \u0646\u062F\u0627\u0631\u062F org.hibernate.validator.constraints.NotBlank.message = \u0645\u0642\u062F\u0627\u0631 \u0646\u0628\u0627\u06CC\u062F \u062E\u0627\u0644\u06CC \u0628\u0627\u0634\u062F org.hibernate.validator.constraints.NotEmpty.message = \u0645\u0642\u062F\u0627\u0631 \u0646\u0628\u0627\u06CC\u062F \u062E\u0627\u0644\u06CC \u0628\u0627\u0634\u062F org.hibernate.validator.constraints.ParametersScriptAssert.message = \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A "{script}" \u0645\u0642\u062F\u0627\u0631 \u062F\u0631\u0633\u062A \u0628\u0631\u0646\u0645\u06CC\u06AF\u0631\u062F\u0627\u0646\u062F diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties index 5f9715ecd4..a5f327deb0 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_fr.properties @@ -9,7 +9,7 @@ javax.validation.constraints.FutureOrPresent.message = doit \u00EAtre dans le pr javax.validation.constraints.Max.message = doit \u00EAtre au maximum \u00E9gal \u00E0 {value} javax.validation.constraints.Min.message = doit \u00EAtre au minimum \u00E9gal \u00E0 {value} javax.validation.constraints.Negative.message = doit \u00EAtre strictement n\u00E9gatif -javax.validation.constraints.NegativeOrZero.message = doit \u00EAtre n\u00E9gatif ou \u00E9gal à 0 +javax.validation.constraints.NegativeOrZero.message = doit \u00EAtre n\u00E9gatif ou \u00E9gal \u00E0 0 javax.validation.constraints.NotBlank.message = ne peut pas \u00EAtre vide javax.validation.constraints.NotEmpty.message = ne peut pas \u00EAtre vide javax.validation.constraints.NotNull.message = ne peut pas \u00EAtre nul @@ -18,7 +18,7 @@ javax.validation.constraints.Past.message = doit \u00EAtre dans le pa javax.validation.constraints.PastOrPresent.message = doit \u00EAtre dans le pass\u00E9 ou dans le pr\u00E9sent javax.validation.constraints.Pattern.message = doit respecter "{regexp}" javax.validation.constraints.Positive.message = doit \u00EAtre strictement positif -javax.validation.constraints.PositiveOrZero.message = doit \u00EAtre positif ou \u00E9gal à 0 +javax.validation.constraints.PositiveOrZero.message = doit \u00EAtre positif ou \u00E9gal \u00E0 0 javax.validation.constraints.Size.message = la taille doit \u00EAtre comprise entre {min} et {max} org.hibernate.validator.constraints.CreditCardNumber.message = num\u00E9ro de carte de cr\u00E9dit invalide @@ -26,12 +26,12 @@ org.hibernate.validator.constraints.Currency.message = devise invalide ( org.hibernate.validator.constraints.EAN.message = code barre {type} invalide org.hibernate.validator.constraints.Email.message = adresse email mal form\u00E9e org.hibernate.validator.constraints.ISBN.message = ISBN invalide -org.hibernate.validator.constraints.Length.message = la longueur doit \u00EAtre comprise entre {min} et {max} caractères -org.hibernate.validator.constraints.CodePointLength.message = la longueur doit \u00EAtre comprise entre {min} et {max} caractères +org.hibernate.validator.constraints.Length.message = la longueur doit \u00EAtre comprise entre {min} et {max} caract\u00E8res +org.hibernate.validator.constraints.CodePointLength.message = la longueur doit \u00EAtre comprise entre {min} et {max} caract\u00E8res org.hibernate.validator.constraints.LuhnCheck.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le Luhn Modulo 10 a \u00E9chou\u00E9 org.hibernate.validator.constraints.Mod10Check.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le Modulo 10 a \u00E9chou\u00E9 org.hibernate.validator.constraints.Mod11Check.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le Modulo 11 a \u00E9chou\u00E9 -org.hibernate.validator.constraints.ModCheck.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le ${modType} a \u00E9chou\u00E9 +org.hibernate.validator.constraints.ModCheck.message = le chiffre de contr\u00F4le pour ${validatedValue} est invalide, le contr\u00F4le {modType} a \u00E9chou\u00E9 org.hibernate.validator.constraints.NotBlank.message = ne peut pas \u00EAtre vide org.hibernate.validator.constraints.NotEmpty.message = ne peut pas \u00EAtre vide org.hibernate.validator.constraints.ParametersScriptAssert.message = le script "{script}" n'a pas \u00E9t\u00E9 \u00E9valu\u00E9 \u00E0 vrai @@ -41,13 +41,13 @@ org.hibernate.validator.constraints.ScriptAssert.message = le script "{scrip org.hibernate.validator.constraints.UniqueElements.message = ne doit contenir que des \u00E9l\u00E9ments uniques org.hibernate.validator.constraints.URL.message = URL mal form\u00E9e -org.hibernate.validator.constraints.br.CNPJ.message = numéro d'enregistrement brésilien de société contribuable (CNPJ) invalide -org.hibernate.validator.constraints.br.CPF.message = numéro d'enregistrement brésilien de contribuable individuel (CPF) invalide -org.hibernate.validator.constraints.br.TituloEleitoral.message = numéro de carte d'électeur brésilienne invalide +org.hibernate.validator.constraints.br.CNPJ.message = num\u00E9ro d'enregistrement br\u00E9silien de soci\u00E9t\u00E9 contribuable (CNPJ) invalide +org.hibernate.validator.constraints.br.CPF.message = num\u00E9ro d'enregistrement br\u00E9silien de contribuable individuel (CPF) invalide +org.hibernate.validator.constraints.br.TituloEleitoral.message = num\u00E9ro de carte d'\u00E9lecteur br\u00E9silienne invalide -org.hibernate.validator.constraints.pl.REGON.message = numéro d'imposition polonais (REGON) invalide -org.hibernate.validator.constraints.pl.NIP.message = numéro d'identification de TVA polonais (NIP) invalide -org.hibernate.validator.constraints.pl.PESEL.message = numéro d'identification national polonais (PESEL) invalide +org.hibernate.validator.constraints.pl.REGON.message = num\u00E9ro d'imposition polonais (REGON) invalide +org.hibernate.validator.constraints.pl.NIP.message = num\u00E9ro d'identification de TVA polonais (NIP) invalide +org.hibernate.validator.constraints.pl.PESEL.message = num\u00E9ro d'identification national polonais (PESEL) invalide org.hibernate.validator.constraints.time.DurationMax.message = doit \u00EAtre plus court que${inclusive == true ? ' ou \u00E9gal \u00E0' : ''}${days == 0 ? '' : days == 1 ? ' 1 jour' : ' ' += days += ' jours'}${hours == 0 ? '' : hours == 1 ? ' 1 heure' : ' ' += hours += ' heures'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' secondes'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' millisecondes'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanosecondes'} org.hibernate.validator.constraints.time.DurationMin.message = doit \u00EAtre plus long que${inclusive == true ? ' ou \u00E9gal \u00E0' : ''}${days == 0 ? '' : days == 1 ? ' 1 jour' : ' ' += days += ' jours'}${hours == 0 ? '' : hours == 1 ? ' 1 heure' : ' ' += hours += ' heures'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' secondes'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' millisecondes'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanosecondes'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties index 022339af3b..05fa1151f3 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_hu.properties @@ -22,6 +22,6 @@ org.hibernate.validator.constraints.CodePointLength.message = a hossznak {min} org.hibernate.validator.constraints.NotBlank.message = nem lehet \u00FCres org.hibernate.validator.constraints.NotEmpty.message = nem lehet \u00FCres org.hibernate.validator.constraints.Range.message = az \u00E9rt\u00E9knek {min} \u00E9s {max} k\u00F6z\u00F6tt kell lennie -org.hibernate.validator.constraints.SafeHtml.message = Lehet, hogy nem biztons\u00E1gos html tartalom +org.hibernate.validator.constraints.SafeHtml.message = lehet, hogy nem biztons\u00E1gos html tartalom org.hibernate.validator.constraints.ScriptAssert.message = script kifejez\u00E9s "{script}" nem \u00E9rt\u00E9kel\u0151d\u00F6tt ki igazz\u00E1 org.hibernate.validator.constraints.URL.message = \u00E9rv\u00E9nyes URL-nek kell lennie 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 aa839b8366..2ad755c13a 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ko.properties @@ -20,10 +20,10 @@ org.hibernate.validator.constraints.EAN.message = {type} \ubc14\ucf54\ub4dc\uac0 org.hibernate.validator.constraints.Email.message = \uc774\uba54\uc77c \uc8fc\uc18c\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. org.hibernate.validator.constraints.Length.message = \ubc18\ub4dc\uc2dc \ucd5c\uc18c\uac12 {min}\uacfc(\uc640) \ucd5c\ub300\uac12 {max} \uc0ac\uc774\uc758 \uae38\uc774\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. org.hibernate.validator.constraints.CodePointLength.message = \ubc18\ub4dc\uc2dc \ucd5c\uc18c\uac12 {min}\uacfc(\uc640) \ucd5c\ub300\uac12 {max} \uc0ac\uc774\uc758 \uae38\uc774\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. -org.hibernate.validator.constraints.LuhnCheck.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Luhn Modulo 10 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.Mod10Check.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 10 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.Mod11Check.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 11 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. -org.hibernate.validator.constraints.ModCheck.message = ${value}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. ${modType} checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. +org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Luhn Modulo 10 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. +org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 10 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. +org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Modulo 11 checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. +org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}\uc758 check digit\uac00 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. {modType} checksum \uc624\ub958\uac00 \ubc1c\uc0dd\ud558\uc600\uc2b5\ub2c8\ub2e4. org.hibernate.validator.constraints.NotBlank.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc874\uc7ac\ud558\uace0 \uacf5\ubc31 \ubb38\uc790\ub97c \uc81c\uc678\ud55c \uae38\uc774\uac00 0\ubcf4\ub2e4 \ucee4\uc57c \ud569\ub2c8\ub2e4. org.hibernate.validator.constraints.NotEmpty.message = \ubc18\ub4dc\uc2dc \uac12\uc774 \uc874\uc7ac\ud558\uace0 \uae38\uc774 \ud639\uc740 \ud06c\uae30\uac00 0\ubcf4\ub2e4 \ucee4\uc57c \ud569\ub2c8\ub2e4. org.hibernate.validator.constraints.ParametersScriptAssert.message = \uc2a4\ud06c\ub9bd\ud2b8 \ud45c\ud604\uc2dd "{script}"\uc758 \uacb0\uacfc\uac00 \ucc38(true)\uc774 \uc544\ub2d9\ub2c8\ub2e4. diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_mn_MN.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_mn_MN.properties index 657ba34b99..0bc5e39713 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_mn_MN.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_mn_MN.properties @@ -2,7 +2,7 @@ javax.validation.constraints.AssertFalse.message = \u0425\u0443\u0434\u0430\u043 javax.validation.constraints.AssertTrue.message = \u04AE\u043D\u044D\u043D \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439 javax.validation.constraints.DecimalMax.message = {value}-\u0430\u0430\u0441 \u0431\u0430\u0433\u0430 \u0431\u0443\u044E\u0443 \u0442\u044D\u043D\u0446\u04AF\u04AF \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439 javax.validation.constraints.DecimalMin.message = {value}-\u0430\u0430\u0441 \u0438\u0445 \u0431\u0443\u044E\u0443 \u0442\u044D\u043D\u0446\u04AF\u04AF \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439 -javax.validation.constraints.Digits.message = \u0422\u043E\u043E\u043D \u0445\u044F\u0437\u0433\u0430\u0430\u0440\u0430\u0430\u0441 \u0445\u044D\u0442\u044D\u0440\u0441\u044D\u043D \u0431\u0430\u0439\u043D\u0430 (<{integerDigits} digits>.<{fractionalDigits} digits> \u0445\u043E\u043E\u0440\u043E\u043D\u0434 \u0431\u0430\u0439\u043D\u0430) +javax.validation.constraints.Digits.message = \u0422\u043E\u043E\u043D \u0445\u044F\u0437\u0433\u0430\u0430\u0440\u0430\u0430\u0441 \u0445\u044D\u0442\u044D\u0440\u0441\u044D\u043D \u0431\u0430\u0439\u043D\u0430 (<{integer} digits>.<{fraction} digits> \u0445\u043E\u043E\u0440\u043E\u043D\u0434 \u0431\u0430\u0439\u043D\u0430) javax.validation.constraints.Email.message = \u0411\u0443\u0440\u0443\u0443 \u0438-\u043C\u044D\u0439\u043B \u0445\u0430\u044F\u0433 \u0431\u0430\u0439\u043D\u0430 javax.validation.constraints.Future.message = \u0418\u0440\u044D\u044D\u0434\u04AF\u0439\u0434 \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439 javax.validation.constraints.Max.message = {value}-\u0430\u0430\u0441 \u0431\u0430\u0433\u0430 \u0431\u0443\u044E\u0443 \u0442\u044D\u043D\u0446\u04AF\u04AF \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439 diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_nl.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_nl.properties new file mode 100644 index 0000000000..d0cd17ff3a --- /dev/null +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_nl.properties @@ -0,0 +1,45 @@ +javax.validation.constraints.AssertFalse.message = moet onwaar zijn +javax.validation.constraints.AssertTrue.message = moet waar zijn +javax.validation.constraints.DecimalMax.message = moet kleiner dan ${inclusive == true ? 'of gelijk aan ' : ''}{value} zijn +javax.validation.constraints.DecimalMin.message = moet groter dan ${inclusive == true ? 'of gelijk aan ' : ''}{value} zijn +javax.validation.constraints.Digits.message = numerieke waarde ligt buiten het toegestane bereik (<{integer} cijfers>,<{fraction} cijfers> verwacht) +javax.validation.constraints.Email.message = het e-mailadres is ongeldig +javax.validation.constraints.Future.message = moet in de toekomst zijn +javax.validation.constraints.FutureOrPresent.message = moet in het heden of in de toekomst zijn +javax.validation.constraints.Max.message = moet kleiner of gelijk aan {value} zijn +javax.validation.constraints.Min.message = moet groter of gelijk aan {value} zijn +javax.validation.constraints.Negative.message = moet kleiner dan 0 zijn +javax.validation.constraints.NegativeOrZero.message = moet kleiner dan of gelijk aan 0 zijn +javax.validation.constraints.NotBlank.message = mag niet onbeschreven zijn +javax.validation.constraints.NotEmpty.message = mag niet leeg zijn +javax.validation.constraints.NotNull.message = mag niet null zijn +javax.validation.constraints.Null.message = moet null zijn +javax.validation.constraints.Past.message = moet in het verleden zijn +javax.validation.constraints.PastOrPresent.message = moet in het heden of in het verleden zijn +javax.validation.constraints.Pattern.message = moet overeenkomen met "{regexp}" +javax.validation.constraints.Positive.message = moet groter dan 0 zijn +javax.validation.constraints.PositiveOrZero.message = moet groter of gelijk zijn aan 0 +javax.validation.constraints.Size.message = grootte moet tussen {min} en {max} liggen + +org.hibernate.validator.constraints.CreditCardNumber.message = creditcard nummer ongeldig +org.hibernate.validator.constraints.Currency.message = ongeldige valuta (toegestane waarden: {value}) +org.hibernate.validator.constraints.EAN.message = ongeldige barcode +org.hibernate.validator.constraints.Email.message = geen geldig e-mailadres +org.hibernate.validator.constraints.ISBN.message = ongeldig ISBN +org.hibernate.validator.constraints.Length.message = moet tussen {min} en {max} tekens lang zijn +org.hibernate.validator.constraints.CodePointLength.message = moet tussen {min} en {max} tekens lang zijn +org.hibernate.validator.constraints.LuhnCheck.message = het controlecijfer voor ${validatedValue} is ongeldig, Luhn Modulo 10 checksum mislukt +org.hibernate.validator.constraints.Mod10Check.message = het controlecijfer voor ${validatedValue} is ongeldig, Modulo 10 checksum mislukt +org.hibernate.validator.constraints.Mod11Check.message = het controlecijfer voor ${validatedValue} is ongeldig, Modulo 11 checksum mislukt +org.hibernate.validator.constraints.ModCheck.message = het controlecijfer voor ${validatedValue} is ongeldig, {modType} checksum mislukt +org.hibernate.validator.constraints.NotBlank.message = mag niet onbeschreven zijn +org.hibernate.validator.constraints.NotEmpty.message = mag niet leeg zijn +org.hibernate.validator.constraints.ParametersScriptAssert.message = scriptexpressie "{script}" is ongeldig +org.hibernate.validator.constraints.Range.message = moet tussen {min} en {max} zijn +org.hibernate.validator.constraints.SafeHtml.message = kan onveilige HTML-inhoud bevatten +org.hibernate.validator.constraints.ScriptAssert.message = scriptexpressie "{script}" is ongeldig +org.hibernate.validator.constraints.UniqueElements.message = mag geen duplicaten bevatten +org.hibernate.validator.constraints.URL.message = moet een geldige URL zijn + +org.hibernate.validator.constraints.time.DurationMax.message = moet korter${inclusive == true ? ' of gelijk' : ' dan'}${days == 0 ? '' : days == 1 ? ' 1 dag' : ' ' += days += ' dagen'}${hours == 0 ? '' : hours == 1 ? ' 1 uur' : ' ' += hours += ' uren'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuut' : ' ' += minutes += ' Minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' seconden'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' milliseconden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanoseconden'} zijn +org.hibernate.validator.constraints.time.DurationMin.message = moet langer${inclusive == true ? ' of gelijk' : ' dan'}${days == 0 ? '' : days == 1 ? ' 1 dag' : ' ' += days += ' dagen'}${hours == 0 ? '' : hours == 1 ? ' 1 uur' : ' ' += hours += ' uren'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minuut' : ' ' += minutes += ' minuten'}${seconds == 0 ? '' : seconds == 1 ? ' 1 seconde' : ' ' += seconds += ' seconden'}${millis == 0 ? '' : millis == 1 ? ' 1 milliseconde' : ' ' += millis += ' milliseconden'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nanoseconde' : ' ' += nanos += ' nanoseconden'} zijn diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties index 0d6c0a89c7..9f5e4ce5b4 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_pt_BR.properties @@ -3,15 +3,15 @@ javax.validation.constraints.AssertTrue.message = deve ser verdadeiro javax.validation.constraints.DecimalMax.message = deve ser menor ou igual a {value} javax.validation.constraints.DecimalMin.message = deve ser maior ou igual a {value} javax.validation.constraints.Digits.message = valor num\u00E9rico fora do limite (<{integer} d\u00EDgitos>.<{fraction} d\u00EDgitos> esperado) -javax.validation.constraints.Email.message = N\u00E3o \u00E9 um endere\u00E7o de e-mail +javax.validation.constraints.Email.message = n\u00E3o \u00E9 um endere\u00E7o de e-mail javax.validation.constraints.Future.message = deve estar no futuro javax.validation.constraints.FutureOrPresent.message = deve de ser uma data em presente ou em futuro javax.validation.constraints.Max.message = deve ser menor ou igual a {value} javax.validation.constraints.Min.message = deve ser maior ou igual a {value} javax.validation.constraints.Negative.message = deve ser menor que 0 javax.validation.constraints.NegativeOrZero.message = deve ser menor ou igual a 0 -javax.validation.constraints.NotBlank.message = N\u00E3o pode estar em branco -javax.validation.constraints.NotEmpty.message = N\u00E3o pode estar vazio +javax.validation.constraints.NotBlank.message = n\u00E3o pode estar em branco +javax.validation.constraints.NotEmpty.message = n\u00E3o pode estar vazio javax.validation.constraints.NotNull.message = n\u00E3o pode ser nulo javax.validation.constraints.Null.message = deve ser nulo javax.validation.constraints.Past.message = deve estar no passado @@ -21,22 +21,22 @@ javax.validation.constraints.Positive.message = deve ser maior que 0 javax.validation.constraints.PositiveOrZero.message = deve ser maior ou igual a 0 javax.validation.constraints.Size.message = tamanho deve estar entre {min} e {max} -org.hibernate.validator.constraints.CreditCardNumber.message = N\u00FAmero de Cart\u00E3o de Cr\u00E9dito inv\u00E1lido -org.hibernate.validator.constraints.Email.message = N\u00E3o \u00E9 um endere\u00E7o de e-mail -org.hibernate.validator.constraints.EAN.message = C\u00F3digo de barras {type} inv\u00E1lido -org.hibernate.validator.constraints.Length.message = Tamanho deve estar entre {min} e {max} -org.hibernate.validator.constraints.CodePointLength.message = Tamanho deve estar entre {min} e {max} -org.hibernate.validator.constraints.LuhnCheck.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 de Luhn falhou -org.hibernate.validator.constraints.Mod10Check.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 falhou -org.hibernate.validator.constraints.Mod11Check.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 falhou -org.hibernate.validator.constraints.ModCheck.message = Digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o ${modType} falhou -org.hibernate.validator.constraints.NotBlank.message = N\u00E3o pode estar em branco -org.hibernate.validator.constraints.NotEmpty.message = N\u00E3o pode estar vazio -org.hibernate.validator.constraints.ParametersScriptAssert.message = O script "{script}" n\u00E3o retornou verdadeiro -org.hibernate.validator.constraints.Range.message = O valor precisa estar entre {min} e {max} -org.hibernate.validator.constraints.SafeHtml.message = Pode ter conte\u00FAdo inseguro no html -org.hibernate.validator.constraints.ScriptAssert.message = Express\u00E3o de script "{script}" n\u00E3o retornou verdadeiro -org.hibernate.validator.constraints.URL.message = Deve ser uma URL v\u00E1lida +org.hibernate.validator.constraints.CreditCardNumber.message = n\u00FAmero de cart\u00E3o de cr\u00E9dito inv\u00E1lido +org.hibernate.validator.constraints.Email.message = n\u00E3o \u00E9 um endere\u00E7o de e-mail +org.hibernate.validator.constraints.EAN.message = c\u00F3digo de barras {type} inv\u00E1lido +org.hibernate.validator.constraints.Length.message = tamanho deve estar entre {min} e {max} +org.hibernate.validator.constraints.CodePointLength.message = tamanho deve estar entre {min} e {max} +org.hibernate.validator.constraints.LuhnCheck.message = digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 de Luhn falhou +org.hibernate.validator.constraints.Mod10Check.message = digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 falhou +org.hibernate.validator.constraints.Mod11Check.message = digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o M\u00F3dulo 10 falhou +org.hibernate.validator.constraints.ModCheck.message = digito verificador para ${validatedValue} \u00E9 inv\u00E1lido, verifica\u00E7\u00E3o {modType} falhou +org.hibernate.validator.constraints.NotBlank.message = n\u00E3o pode estar em branco +org.hibernate.validator.constraints.NotEmpty.message = n\u00E3o pode estar vazio +org.hibernate.validator.constraints.ParametersScriptAssert.message = o script "{script}" n\u00E3o retornou verdadeiro +org.hibernate.validator.constraints.Range.message = o valor precisa estar entre {min} e {max} +org.hibernate.validator.constraints.SafeHtml.message = pode ter conte\u00FAdo inseguro no html +org.hibernate.validator.constraints.ScriptAssert.message = express\u00E3o de script "{script}" n\u00E3o retornou verdadeiro +org.hibernate.validator.constraints.URL.message = deve ser uma URL v\u00E1lida org.hibernate.validator.constraints.br.CNPJ.message = CNPJ inv\u00E1lido org.hibernate.validator.constraints.br.CPF.message = CPF inv\u00E1lido org.hibernate.validator.constraints.br.TituloEleitoral.message = T\u00EDtulo Eleitoral inv\u00E1lido diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties index 2886acb9d3..9d3f174e89 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_ru.properties @@ -9,7 +9,7 @@ javax.validation.constraints.Min.message = \u0434\u043E\u043B\u0436\u043 javax.validation.constraints.NotBlank.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E javax.validation.constraints.NotEmpty.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E javax.validation.constraints.NotNull.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0437\u0430\u0434\u0430\u043D\u043E -javax.validation.constraints.Null.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435\u0437\u0430\u0434\u0430\u043D\u043E +javax.validation.constraints.Null.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E javax.validation.constraints.Past.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0432 \u043F\u0440\u043E\u0448\u043B\u043E\u043C javax.validation.constraints.Pattern.message = \u0434\u043E\u043B\u0436\u043D\u043E \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043E\u0432\u0430\u0442\u044C \u0448\u0430\u0431\u043B\u043E\u043D\u0443 "{regexp}" javax.validation.constraints.Size.message = \u0440\u0430\u0437\u043C\u0435\u0440 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043C\u0435\u0436\u0434\u0443 {min} \u0438 {max} @@ -19,10 +19,10 @@ org.hibernate.validator.constraints.EAN.message = \u043D\u04 org.hibernate.validator.constraints.Email.message = email \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D \u0432 \u043D\u0435\u0432\u0435\u0440\u043D\u043E\u043C \u0444\u043E\u0440\u043C\u0430\u0442\u0435 org.hibernate.validator.constraints.Length.message = \u0434\u043B\u0438\u043D\u0430 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043C\u0435\u0436\u0434\u0443 {min} \u0438 {max} org.hibernate.validator.constraints.CodePointLength.message = \u0434\u043B\u0438\u043D\u0430 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043C\u0435\u0436\u0434\u0443 {min} \u0438 {max} -org.hibernate.validator.constraints.LuhnCheck.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 \u041B\u0443\u043D\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 -org.hibernate.validator.constraints.Mod10Check.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 Mod10 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 -org.hibernate.validator.constraints.Mod11Check.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 Mod11 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 -org.hibernate.validator.constraints.ModCheck.message = \u041A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E ${modType} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 +org.hibernate.validator.constraints.LuhnCheck.message = \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 \u041B\u0443\u043D\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 +org.hibernate.validator.constraints.Mod10Check.message = \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 Mod10 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 +org.hibernate.validator.constraints.Mod11Check.message = \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C\u0443 Mod11 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 +org.hibernate.validator.constraints.ModCheck.message = \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u0430\u044F \u0446\u0438\u0444\u0440\u0430 \u0434\u043B\u044F ${validatedValue} \u043D\u0435\u0432\u0435\u0440\u043D\u0430, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E {modType} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 org.hibernate.validator.constraints.NotBlank.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E org.hibernate.validator.constraints.NotEmpty.message = \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u043E org.hibernate.validator.constraints.ParametersScriptAssert.message = \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435 "{script}" \u043D\u0435 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0438\u0441\u0442\u0438\u043D\u043D\u044B\u043C diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties index 24dc9234ea..2380b475f8 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_sk.properties @@ -20,10 +20,10 @@ org.hibernate.validator.constraints.EAN.message = nespr\u00e org.hibernate.validator.constraints.Email.message = nespr\u00e1vny form\u00e1t emailovej adresy org.hibernate.validator.constraints.Length.message = d\u013a\u017eka mus\u00ed by\u0165 medzi {min} a {max} org.hibernate.validator.constraints.CodePointLength.message = d\u013a\u017eka mus\u00ed by\u0165 medzi {min} a {max} -org.hibernate.validator.constraints.LuhnCheck.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Luhn Modulo 10 zlyhal -org.hibernate.validator.constraints.Mod10Check.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 10 zlyhal -org.hibernate.validator.constraints.Mod11Check.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 11 zlyhal -org.hibernate.validator.constraints.ModCheck.message = Kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det ${modType} zlyhal +org.hibernate.validator.constraints.LuhnCheck.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Luhn Modulo 10 zlyhal +org.hibernate.validator.constraints.Mod10Check.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 10 zlyhal +org.hibernate.validator.constraints.Mod11Check.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det Modulo 11 zlyhal +org.hibernate.validator.constraints.ModCheck.message = kontroln\u00e1 \u010d\u00edslica pre ${validatedValue} nie je spr\u00e1vna, kontroln\u00fd s\u00fa\u010det {modType} zlyhal org.hibernate.validator.constraints.NotBlank.message = nem\u00f4\u017ee by\u0165 pr\u00e1zdne org.hibernate.validator.constraints.NotEmpty.message = nem\u00f4\u017ee by\u0165 pr\u00e1zdne org.hibernate.validator.constraints.ParametersScriptAssert.message = skriptovac\u00ed v\u00fdraz "{script}" nebol vyhodnoten\u00fd na \u00e1no diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_tr.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_tr.properties index 37e7223d66..9b7691e11d 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_tr.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_tr.properties @@ -2,7 +2,7 @@ javax.validation.constraints.AssertFalse.message = teyit ba\u015Far\u0131s\u0131 javax.validation.constraints.AssertTrue.message = teyit ba\u015Far\u0131s\u0131z javax.validation.constraints.DecimalMax.message = '{value}' de\u011Ferinden k\u00FC\u00E7\u00FCk yada e\u015Fit olmal\u0131 javax.validation.constraints.DecimalMin.message = '{value}' de\u011Ferinden b\u00FCy\u00FCk yada e\u015Fit olmal\u0131 -javax.validation.constraints.Digits.message = s\u0131n\u0131rlar\u0131n d\u0131\u015F\u0131nda say\u0131sal de\u011Fer (beklenen <{integerDigits} basamak>.<{fractionalDigits} basamak>) +javax.validation.constraints.Digits.message = s\u0131n\u0131rlar\u0131n d\u0131\u015F\u0131nda say\u0131sal de\u011Fer (beklenen <{integer} basamak>.<{fraction} basamak>) javax.validation.constraints.Email.message = d\u00FCzg\u00FCn bi\u00E7imli bir e-posta adresi de\u011Fil! javax.validation.constraints.Future.message = ileri bir tarih olmal\u0131 javax.validation.constraints.Max.message = '{value}' de\u011Ferinden k\u00FC\u00E7\u00FCk yada e\u015Fit olmal\u0131 diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_uk.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_uk.properties index fb4e559c68..18d1416159 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_uk.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_uk.properties @@ -31,7 +31,7 @@ org.hibernate.validator.constraints.CodePointLength.message = \u0434\u04 org.hibernate.validator.constraints.LuhnCheck.message = \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430 \u0446\u0438\u0444\u0440\u0430 \u0434\u043b\u044f ${validatedValue}, \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0437\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u043c \u041b\u0443\u043d\u0430 \u0437\u0430\u043a\u0456\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u0437 \u043f\u043e\u043c\u0438\u043b\u043a\u043e\u044e org.hibernate.validator.constraints.Mod10Check.message = \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430 \u0446\u0438\u0444\u0440\u0430 \u0434\u043b\u044f ${validatedValue}, \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0437\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u043c Mod10 \u0437\u0430\u043a\u0456\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u0437 \u043f\u043e\u043c\u0438\u043b\u043a\u043e\u044e org.hibernate.validator.constraints.Mod11Check.message = \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430 \u0446\u0438\u0444\u0440\u0430 \u0434\u043b\u044f ${validatedValue}, \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0437\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u043c Mod11 \u0437\u0430\u043a\u0456\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u0437 \u043f\u043e\u043c\u0438\u043b\u043a\u043e\u044e -org.hibernate.validator.constraints.ModCheck.message = \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430 \u0446\u0438\u0444\u0440\u0430 \u0434\u043b\u044f ${validatedValue}, \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0437\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u043c ${modType} \u0437\u0430\u043a\u0456\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u0437 \u043f\u043e\u043c\u0438\u043b\u043a\u043e\u044e +org.hibernate.validator.constraints.ModCheck.message = \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430 \u0446\u0438\u0444\u0440\u0430 \u0434\u043b\u044f ${validatedValue}, \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0437\u0430 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u043c {modType} \u0437\u0430\u043a\u0456\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u0437 \u043f\u043e\u043c\u0438\u043b\u043a\u043e\u044e org.hibernate.validator.constraints.NotBlank.message = \u043d\u0435 \u043c\u043e\u0436\u0435 \u0431\u0443\u0442\u0438 \u043f\u0443\u0441\u0442\u0438\u043c org.hibernate.validator.constraints.NotEmpty.message = \u043d\u0435 \u043c\u043e\u0436\u0435 \u0431\u0443\u0442\u0438 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c org.hibernate.validator.constraints.ParametersScriptAssert.message = \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u0438\u0439 \u0432\u0438\u0440\u0430\u0437 "{script}" \u043d\u0435 \u0454 \u0456\u0441\u0442\u0438\u043d\u043d\u0438\u043c @@ -50,4 +50,4 @@ org.hibernate.validator.constraints.pl.NIP.message = \u043d\u0435\u043f\u04 org.hibernate.validator.constraints.pl.PESEL.message = \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0438\u0439 \u043f\u043e\u043b\u044c\u0441\u043a\u0438\u0439 \u043d\u0430\u0446\u0456\u043e\u043d\u0430\u043b\u044c\u043d\u0438\u0439 \u0456\u0434\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0439\u043d\u0438\u0439 \u043d\u043e\u043c\u0435\u0440 (PESEL) org.hibernate.validator.constraints.time.DurationMax.message = \u043c\u0430\u0454 \u0431\u0443\u0442\u0438 \u043a\u043e\u0440\u043e\u0442\u0448\u0438\u0439${inclusive == true ? ' \u0430\u0431\u043e \u0440\u0456\u0432\u043d\u0438\u0439' : ''} \u0437\u0430${days == 0 ? '' : days == 1 ? ' 1 \u0434\u0435\u043d\u044c' : ' ' += days += ' \u0434\u043d\u0456\u0432'}${hours == 0 ? '' : hours == 1 ? ' 1 \u0433\u043e\u0434\u0438\u043d\u0430' : ' ' += hours += ' \u0433\u043e\u0434\u0438\u043d'}${minutes == 0 ? '' : minutes == 1 ? ' 1 \u0445\u0432\u0438\u043b\u0438\u043d\u0430' : ' ' += minutes += ' \u0445\u0432\u0438\u043b\u0438\u043d'}${seconds == 0 ? '' : seconds == 1 ? ' 1 \u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += seconds += ' \u0441\u0435\u043a\u0443\u043d\u0434'}${millis == 0 ? '' : millis == 1 ? ' 1 \u043c\u0456\u043b\u0456\u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += millis += ' \u043c\u0456\u043b\u0456\u0441\u0435\u043a\u0443\u043d\u0434'}${nanos == 0 ? '' : nanos == 1 ? ' 1 \u043d\u0430\u043d\u043e\u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += nanos += ' \u043d\u0430\u043d\u043e\u0441\u0435\u043a\u0443\u043d\u0434'} -org.hibernate.validator.constraints.time.DurationMin.message = \u043c\u0430\u0454 \u0431\u0443\u0442\u0438 \u0434\u043e\u0432\u0448\u0438\u0439{inclusive == true ? ' \u0430\u0431\u043e \u0440\u0456\u0432\u043d\u0438\u0439' : ''} \u0437\u0430${days == 0 ? '' : days == 1 ? ' 1 \u0434\u0435\u043d\u044c' : ' ' += days += ' \u0434\u043d\u0456\u0432'}${hours == 0 ? '' : hours == 1 ? ' 1 \u0433\u043e\u0434\u0438\u043d\u0430' : ' ' += hours += ' \u0433\u043e\u0434\u0438\u043d'}${minutes == 0 ? '' : minutes == 1 ? ' 1 \u0445\u0432\u0438\u043b\u0438\u043d\u0430' : ' ' += minutes += ' \u0445\u0432\u0438\u043b\u0438\u043d'}${seconds == 0 ? '' : seconds == 1 ? ' 1 \u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += seconds += ' \u0441\u0435\u043a\u0443\u043d\u0434'}${millis == 0 ? '' : millis == 1 ? ' 1 \u043c\u0456\u043b\u0456\u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += millis += ' \u043c\u0456\u043b\u0456\u0441\u0435\u043a\u0443\u043d\u0434'}${nanos == 0 ? '' : nanos == 1 ? ' 1 \u043d\u0430\u043d\u043e\u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += nanos += ' \u043d\u0430\u043d\u043e\u0441\u0435\u043a\u0443\u043d\u0434'} +org.hibernate.validator.constraints.time.DurationMin.message = \u043c\u0430\u0454 \u0431\u0443\u0442\u0438 \u0434\u043e\u0432\u0448\u0438\u0439${inclusive == true ? ' \u0430\u0431\u043e \u0440\u0456\u0432\u043d\u0438\u0439' : ''} \u0437\u0430${days == 0 ? '' : days == 1 ? ' 1 \u0434\u0435\u043d\u044c' : ' ' += days += ' \u0434\u043d\u0456\u0432'}${hours == 0 ? '' : hours == 1 ? ' 1 \u0433\u043e\u0434\u0438\u043d\u0430' : ' ' += hours += ' \u0433\u043e\u0434\u0438\u043d'}${minutes == 0 ? '' : minutes == 1 ? ' 1 \u0445\u0432\u0438\u043b\u0438\u043d\u0430' : ' ' += minutes += ' \u0445\u0432\u0438\u043b\u0438\u043d'}${seconds == 0 ? '' : seconds == 1 ? ' 1 \u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += seconds += ' \u0441\u0435\u043a\u0443\u043d\u0434'}${millis == 0 ? '' : millis == 1 ? ' 1 \u043c\u0456\u043b\u0456\u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += millis += ' \u043c\u0456\u043b\u0456\u0441\u0435\u043a\u0443\u043d\u0434'}${nanos == 0 ? '' : nanos == 1 ? ' 1 \u043d\u0430\u043d\u043e\u0441\u0435\u043a\u0443\u043d\u0434\u0430' : ' ' += nanos += ' \u043d\u0430\u043d\u043e\u0441\u0435\u043a\u0443\u043d\u0434'} diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh_CN.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh_CN.properties index dba87b580d..b2a75318fe 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh_CN.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages_zh_CN.properties @@ -30,7 +30,7 @@ org.hibernate.validator.constraints.CodePointLength.message = \u957f\u5e org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}\u7684\u6821\u9a8c\u7801\u4e0d\u5408\u6cd5, Luhn\u6a2110\u6821\u9a8c\u548c\u4e0d\u5339\u914d org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}\u7684\u6821\u9a8c\u7801\u4e0d\u5408\u6cd5, \u6a2110\u6821\u9a8c\u548c\u4e0d\u5339\u914d org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}\u7684\u6821\u9a8c\u7801\u4e0d\u5408\u6cd5, \u6a2111\u6821\u9a8c\u548c\u4e0d\u5339\u914d -org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}\u7684\u6821\u9a8c\u7801\u4e0d\u5408\u6cd5, ${modType}\u6821\u9a8c\u548c\u4e0d\u5339\u914d +org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}\u7684\u6821\u9a8c\u7801\u4e0d\u5408\u6cd5, {modType}\u6821\u9a8c\u548c\u4e0d\u5339\u914d org.hibernate.validator.constraints.NotBlank.message = \u4e0d\u80fd\u4e3a\u7a7a org.hibernate.validator.constraints.NotEmpty.message = \u4e0d\u80fd\u4e3a\u7a7a org.hibernate.validator.constraints.ParametersScriptAssert.message = \u6267\u884c\u811a\u672c\u8868\u8fbe\u5f0f"{script}"\u6ca1\u6709\u8fd4\u56de\u671f\u671b\u7ed3\u679c diff --git a/engine/src/main/xjb/binding-customization.xjb b/engine/src/main/xjb/binding-customization.xjb deleted file mode 100644 index 8dc5132c86..0000000000 --- a/engine/src/main/xjb/binding-customization.xjb +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/engine/src/test/java/org/hibernate/validator/ValidationMessages.java b/engine/src/test/java/org/hibernate/validator/ValidationMessages.java index 8da94713cd..91281345d5 100644 --- a/engine/src/test/java/org/hibernate/validator/ValidationMessages.java +++ b/engine/src/test/java/org/hibernate/validator/ValidationMessages.java @@ -34,9 +34,9 @@ public class ValidationMessages extends ResourceBundle { public ValidationMessages() throws Exception { - log.info( "For test purposes are we proxying the built-in messages!" ); + log.debug( "For test purposes we are proxying the built-in messages!" ); addTestPropertiesToBundle(); - log.infof( "Adding the following properties to default properties %s", messages ); + log.debugf( "Adding the following properties to default properties %s", messages ); loadDefaultValidationProperties(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/NonHibernateValidatorConfigTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/NonHibernateValidatorConfigTest.java new file mode 100644 index 0000000000..02d81a6ded --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/NonHibernateValidatorConfigTest.java @@ -0,0 +1,222 @@ +/* + * 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 org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.io.InputStream; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import javax.validation.BootstrapConfiguration; +import javax.validation.ClockProvider; +import javax.validation.Configuration; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.MessageInterpolator; +import javax.validation.ParameterNameProvider; +import javax.validation.TraversableResolver; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotNull; +import javax.validation.spi.BootstrapState; +import javax.validation.spi.ConfigurationState; +import javax.validation.spi.ValidationProvider; +import javax.validation.valueextraction.ValueExtractor; + +import org.hibernate.validator.internal.engine.DefaultClockProvider; +import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.ValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +public class NonHibernateValidatorConfigTest { + + @Test + @TestForIssue(jiraKey = "HV-1821") + public void testNonHibernateValidatorConfig() { + ValidatorFactory validatorFactory = Validation.byProvider( NonHibernateValidatorProvider.class ) + .configure() + .buildValidatorFactory(); + Validator validator = validatorFactory.getValidator(); + assertThat( validator.validate( new Bean() ) ).containsOnlyViolations( violationOf( NotNull.class ) ); + } + + public static final class NonHibernateValidatorProvider implements ValidationProvider { + + @Override + public NonHibernateValidatorConfiguration createSpecializedConfiguration(BootstrapState state) { + return new NonHibernateValidatorConfiguration(); + } + + @Override + public Configuration createGenericConfiguration(BootstrapState state) { + return new NonHibernateValidatorConfiguration(); + } + + @Override + public ValidatorFactory buildValidatorFactory(ConfigurationState configurationState) { + return new ValidatorFactoryImpl( configurationState ); + } + } + + public static final class NonHibernateValidatorConfiguration implements Configuration, ConfigurationState { + + private final MessageInterpolator defaultMessageInterpolator; + private final TraversableResolver defaultTraversableResolver; + private final ConstraintValidatorFactory defaultConstraintValidatorFactory; + private final ParameterNameProvider defaultParameterNameProvider; + private final ClockProvider defaultClockProvider; + + public NonHibernateValidatorConfiguration() { + this.defaultMessageInterpolator = new ParameterMessageInterpolator(); + this.defaultTraversableResolver = TraversableResolvers.getDefault(); + this.defaultConstraintValidatorFactory = new ConstraintValidatorFactoryImpl(); + this.defaultParameterNameProvider = new DefaultParameterNameProvider(); + this.defaultClockProvider = DefaultClockProvider.INSTANCE; + } + + @Override + public NonHibernateValidatorConfiguration ignoreXmlConfiguration() { + return this; + } + + @Override + public NonHibernateValidatorConfiguration messageInterpolator(MessageInterpolator interpolator) { + return this; + } + + @Override + public NonHibernateValidatorConfiguration traversableResolver(TraversableResolver resolver) { + return this; + } + + @Override + public NonHibernateValidatorConfiguration constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) { + return this; + } + + @Override + public NonHibernateValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider) { + return this; + } + + @Override + public NonHibernateValidatorConfiguration clockProvider(ClockProvider clockProvider) { + return this; + } + + @Override + public NonHibernateValidatorConfiguration addValueExtractor(ValueExtractor extractor) { + return this; + } + + @Override + public NonHibernateValidatorConfiguration addMapping(InputStream stream) { + return this; + } + + @Override + public NonHibernateValidatorConfiguration addProperty(String name, String value) { + return this; + } + + @Override + public MessageInterpolator getDefaultMessageInterpolator() { + return defaultMessageInterpolator; + } + + @Override + public TraversableResolver getDefaultTraversableResolver() { + return defaultTraversableResolver; + } + + @Override + public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() { + return defaultConstraintValidatorFactory; + } + + @Override + public ParameterNameProvider getDefaultParameterNameProvider() { + return defaultParameterNameProvider; + } + + @Override + public ClockProvider getDefaultClockProvider() { + return defaultClockProvider; + } + + @Override + public BootstrapConfiguration getBootstrapConfiguration() { + return null; + } + + @Override + public ValidatorFactory buildValidatorFactory() { + return new NonHibernateValidatorProvider().buildValidatorFactory( this ); + } + + @Override + public boolean isIgnoreXmlConfiguration() { + return true; + } + + @Override + public MessageInterpolator getMessageInterpolator() { + return defaultMessageInterpolator; + } + + @Override + public Set getMappingStreams() { + return Collections.emptySet(); + } + + @Override + public Set> getValueExtractors() { + return Collections.emptySet(); + } + + @Override + public ConstraintValidatorFactory getConstraintValidatorFactory() { + return defaultConstraintValidatorFactory; + } + + @Override + public TraversableResolver getTraversableResolver() { + return defaultTraversableResolver; + } + + @Override + public ParameterNameProvider getParameterNameProvider() { + return defaultParameterNameProvider; + } + + @Override + public ClockProvider getClockProvider() { + return defaultClockProvider; + } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } + } + + public static final class Bean { + + @NotNull + public String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java index 7c56d67a25..d45c5b1579 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticConstraintDefinitionsTest.java @@ -61,10 +61,10 @@ public void countrySpecificProgrammaticDefinition() { "invalid Brazilian corporate taxpayer registry number (CNPJ)" ); - doProgrammaticTest( REGON.class, new REGONDef(), "49905531368510", "49905531368512", "Invalid Polish Taxpayer Identification Number (REGON)" ); - doProgrammaticTest( REGON.class, new REGONDef(), "858336997", "691657185", "Invalid Polish Taxpayer Identification Number (REGON)" ); - doProgrammaticTest( PESEL.class, new PESELDef(), "12252918020", "44051401358", "Invalid Polish National Identification Number (PESEL)" ); - doProgrammaticTest( NIP.class, new NIPDef(), "1786052059", "2596048505", "Invalid VAT Identification Number (NIP)" ); + doProgrammaticTest( REGON.class, new REGONDef(), "49905531368510", "49905531368512", "invalid Polish Taxpayer Identification Number (REGON)" ); + doProgrammaticTest( REGON.class, new REGONDef(), "858336997", "691657185", "invalid Polish Taxpayer Identification Number (REGON)" ); + doProgrammaticTest( PESEL.class, new PESELDef(), "12252918020", "44051401358", "invalid Polish National Identification Number (PESEL)" ); + doProgrammaticTest( NIP.class, new NIPDef(), "1786052059", "2596048505", "invalid VAT Identification Number (NIP)" ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java index cb9aa67121..60a8953a37 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java @@ -30,9 +30,11 @@ import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.defs.LengthDef; import org.hibernate.validator.cfg.defs.MinDef; import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.cfg.defs.SizeDef; +import org.hibernate.validator.constraints.Length; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeMethod; @@ -142,6 +144,29 @@ public void canDeclareContainerElementCascadesForFieldProgrammatically() { ); } + @Test + @TestForIssue(jiraKey = "HV-1614") + public void canDeclareDeeplyNestedContainerElementConstraintsOnMultipleDifferentTypeArgumentsForFieldProgrammatically() { + ConstraintMapping newMapping = config.createConstraintMapping(); + newMapping + .type( FishTank.class ) + .property( "tagsOfFishOfTheMonth", FIELD ) + .containerElementType( 0, 0 ) + .constraint( new LengthDef().min( 10 ).max( 20 ) ) + .containerElementType( 0, 1, 0 ) + .constraint( new NotNullDef() ); + + config.addMapping( newMapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new FishTank() ); + + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withMessage( "must not be null" ), + violationOf( Length.class ).withMessage( "length must be between 10 and 20" ) + ); + } + // HV-1428 Container element support is disabled for arrays @Test(expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000226:.*") @TestForIssue(jiraKey = "HV-1239") diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java index 74031960a0..c9b92b1b29 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextImplTest.java @@ -226,7 +226,7 @@ private ConstraintValidatorContextImpl createEmptyConstraintValidatorContextImpl PathImpl path = PathImpl.createRootPath(); path.addBeanNode(); - ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl( null, null, path, null, null ); + ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl( null, null, path, null, null, true ); context.disableDefaultConstraintViolation(); return context; } diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java index 141b9aa4e3..85a311e30f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/ConstraintValidatorContextTest.java @@ -208,6 +208,20 @@ public void testAddParameterNodeForFieldLevelConstraintCausesException() throws } } + @Test + public void testInjectionCausedByRecklessConcatenation() { + String maliciousPayload = "$\\A{1 + 1}"; + + // Simulate user entry, through a web form for example + MyObjectWithELInjectionRiskCausedByRecklessConcatenation object = new MyObjectWithELInjectionRiskCausedByRecklessConcatenation(); + object.field1 = maliciousPayload; + Set> constraintViolations = validator.validate( object ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( ValidationWithELInjectionRiskCausedByRecklessConcatenation.class ) + .withMessage( "Value '" + maliciousPayload + "' is invalid" ) + ); + } + @MyClassLevelValidation private static class MyObject { @NotNull @@ -278,6 +292,13 @@ public String getName() { } } + @ValidationWithELInjectionRiskCausedByRecklessConcatenation + private static class MyObjectWithELInjectionRiskCausedByRecklessConcatenation { + + String field1; + + } + @Retention(RUNTIME) @Constraint(validatedBy = MyClassLevelValidation.Validator.class) public @interface MyClassLevelValidation { @@ -486,4 +507,34 @@ public boolean isValid(String value, ConstraintValidatorContext context) { } } } + + @Retention(RUNTIME) + @Constraint(validatedBy = ValidationWithELInjectionRiskCausedByRecklessConcatenation.Validator.class) + public @interface ValidationWithELInjectionRiskCausedByRecklessConcatenation { + String message() default "failed"; + + Class[] groups() default { }; + + Class[] payload() default { }; + + class Validator + implements ConstraintValidator { + + @Override + public boolean isValid(MyObjectWithELInjectionRiskCausedByRecklessConcatenation value, ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + + // This is bad practice: message parameters should be used instead. + // Regardless, it can happen and should work as well as possible. + context.buildConstraintViolationWithTemplate( "Value '" + escape( value.field1 ) + "' is invalid" ) + .addConstraintViolation(); + + return false; + } + + private String escape(String value) { + return value.replaceAll( "\\$+\\{", "{" ); + } + } + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java index 59680961d2..678ec69422 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/NIPValidatorTest.java @@ -24,10 +24,26 @@ public class NIPValidatorTest extends AbstractConstrainedTest { @Test - public void testCorrectNipNumber() { + public void testAdditionalCharactersAreAllowed() { assertNoViolations( validator.validate( new Person( "123-456-78-19" ) ) ); assertNoViolations( validator.validate( new Person( "123-45-67-819" ) ) ); assertNoViolations( validator.validate( new Person( "123-456-32-18" ) ) ); + } + + @Test + public void testIncorrectLength() { + assertThat( validator.validate( new Person( "123-456-78-14113-312-310" ) ) ) + .containsOnlyViolations( + violationOf( NIP.class ).withProperty( "nip" ) + ); + assertThat( validator.validate( new Person( "123-45-62" ) ) ) + .containsOnlyViolations( + violationOf( NIP.class ).withProperty( "nip" ) + ); + } + + @Test + public void testCorrectNipNumber() { assertNoViolations( validator.validate( new Person( "5931423811" ) ) ); assertNoViolations( validator.validate( new Person( "2596048500" ) ) ); assertNoViolations( validator.validate( new Person( "4163450312" ) ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java index 352344df31..7b7752df0f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/PESELValidatorTest.java @@ -23,6 +23,30 @@ */ public class PESELValidatorTest extends AbstractConstrainedTest { + @Test + public void testAdditionalCharactersNotAllowed() { + assertThat( validator.validate( new Person( "9204-190-37-90" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + assertThat( validator.validate( new Person( "44-0-5-1-4-01359" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + } + + @Test + public void testIncorrectLength() { + assertThat( validator.validate( new Person( "920419795" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + assertThat( validator.validate( new Person( "92041903790123" ) ) ) + .containsOnlyViolations( + violationOf( PESEL.class ).withProperty( "pesel" ) + ); + } + @Test public void testCorrectPESELNumber() { assertNoViolations( validator.validate( new Person( "92041903790" ) ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java index 50ea4b4c08..f54e4bf041 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/pl/REGONValidatorTest.java @@ -23,6 +23,30 @@ */ public class REGONValidatorTest extends AbstractConstrainedTest { + @Test + public void testAdditionalCharactersNotAllowed() { + assertThat( validator.validate( new Company( "123-456-785" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + assertThat( validator.validate( new Company( "6-9-1-6-5-7-1-8-2" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + } + + @Test + public void testIncorrectLength() { + assertThat( validator.validate( new Company( "1234567845" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + assertThat( validator.validate( new Company( "12345673" ) ) ) + .containsOnlyViolations( + violationOf( REGON.class ).withProperty( "regon" ) + ); + } + @Test public void testCorrectRegon9Number() { assertNoViolations( validator.validate( new Company( "123456785" ) ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionTypeMismatchTest.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionTypeMismatchTest.java new file mode 100644 index 0000000000..56ea9c5d48 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/ConstraintDefinitionTypeMismatchTest.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.constraintvalidator; + +import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; + +import javax.validation.ConstraintDefinitionException; +import javax.validation.Validator; + +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1592") +public class ConstraintDefinitionTypeMismatchTest { + + private Validator validator; + + @BeforeMethod + public void setUp() { + validator = getValidator(); + } + + @Test(expectedExceptions = ConstraintDefinitionException.class, expectedExceptionsMessageRegExp = "^HV000243:.*") + public void constraint_validator_constraint_type_mismatch_causes_exception() { + validator.validate( new TypeMismatchBean() ); + } + + public class TypeMismatchBean { + + @TypeMismatchConstraint + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/TypeMismatchConstraint.java b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/TypeMismatchConstraint.java new file mode 100644 index 0000000000..98237a2b14 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraintvalidator/TypeMismatchConstraint.java @@ -0,0 +1,31 @@ +/* + * 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 static java.lang.annotation.ElementType.FIELD; +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; + + +@Documented +@Constraint(validatedBy = MustNotMatchValidator.class) +@Target({ METHOD, FIELD }) +@Retention(RUNTIME) +public @interface TypeMismatchConstraint { + String message() default "{org.hibernate.validator.test.constraintvalidator.TypeMismatchConstraint.message}"; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/ConfigurationReuseHibernateValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/ConfigurationReuseHibernateValidatorTest.java new file mode 100644 index 0000000000..16b9b4ca2d --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/ConfigurationReuseHibernateValidatorTest.java @@ -0,0 +1,59 @@ +/* + * 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.bootstrap; + +import static org.testng.Assert.assertSame; + +import java.util.Locale; + +import javax.validation.Configuration; +import javax.validation.MessageInterpolator; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; + +import org.testng.annotations.Test; + +/** + * @author Steven Walters + * @author Guillaume Smet + */ +public class ConfigurationReuseHibernateValidatorTest { + + public static class MessageInterpolatorImpl implements MessageInterpolator { + + private final String prefix; + + public MessageInterpolatorImpl(String prefix) { + this.prefix = prefix; + } + + @Override + public String interpolate(String messageTemplate, Context context) { + return prefix + ": " + messageTemplate; + } + + @Override + public String interpolate(String messageTemplate, Context context, Locale locale) { + return prefix + ": " + messageTemplate + locale.toLanguageTag(); + } + + public String toString() { + return getClass().getSimpleName() + prefix; + } + } + + @Test + public void testMessageInterpolatorChange() { + Configuration config = Validation.byDefaultProvider().configure(); + MessageInterpolator interpolator1 = new MessageInterpolatorImpl( "One" ); + MessageInterpolator interpolator2 = new MessageInterpolatorImpl( "Two" ); + ValidatorFactory factory1 = config.messageInterpolator( interpolator1 ).buildValidatorFactory(); + ValidatorFactory factory2 = config.messageInterpolator( interpolator2 ).buildValidatorFactory(); + assertSame( factory1.getMessageInterpolator(), interpolator1 ); + assertSame( factory2.getMessageInterpolator(), interpolator2 ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java new file mode 100644 index 0000000000..38d1aee63f --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java @@ -0,0 +1,372 @@ +/* + * 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.constraintvalidators; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import javax.money.MonetaryAmount; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.AssertFalse; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Digits; +import javax.validation.constraints.Email; +import javax.validation.constraints.Future; +import javax.validation.constraints.FutureOrPresent; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.Negative; +import javax.validation.constraints.NegativeOrZero; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import javax.validation.constraints.Past; +import javax.validation.constraints.PastOrPresent; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; +import javax.validation.constraints.Size; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.constraints.CodePointLength; +import org.hibernate.validator.constraints.CreditCardNumber; +import org.hibernate.validator.constraints.Currency; +import org.hibernate.validator.constraints.EAN; +import org.hibernate.validator.constraints.ISBN; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.LuhnCheck; +import org.hibernate.validator.constraints.Mod10Check; +import org.hibernate.validator.constraints.Mod11Check; +import org.hibernate.validator.constraints.ModCheck; +import org.hibernate.validator.constraints.ParameterScriptAssert; +import org.hibernate.validator.constraints.Range; +import org.hibernate.validator.constraints.SafeHtml; +import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.constraints.URL; +import org.hibernate.validator.constraints.UniqueElements; +import org.hibernate.validator.constraints.br.CNPJ; +import org.hibernate.validator.constraints.br.CPF; +import org.hibernate.validator.constraints.br.TituloEleitoral; +import org.hibernate.validator.constraints.pl.NIP; +import org.hibernate.validator.constraints.pl.PESEL; +import org.hibernate.validator.constraints.pl.REGON; +import org.hibernate.validator.constraints.time.DurationMax; +import org.hibernate.validator.constraints.time.DurationMin; +import org.hibernate.validator.testutil.ConstraintViolationAssert; +import org.javamoney.moneta.Money; +import org.testng.annotations.Test; + +/** + * Test that all the messages of all the constraints are properly interpolated for all the supported locales. + * + * @author Guillaume Smet + */ +@SuppressWarnings("deprecation") +public class MessagePropertiesTest { + + private static final List ALL_SUPPORTED_LOCALES = Arrays.asList( + Locale.forLanguageTag( "ar" ), + Locale.forLanguageTag( "cs" ), + Locale.forLanguageTag( "da" ), + Locale.forLanguageTag( "de" ), + Locale.forLanguageTag( "en" ), + Locale.forLanguageTag( "es" ), + Locale.forLanguageTag( "fa" ), + Locale.forLanguageTag( "fr" ), + Locale.forLanguageTag( "hu" ), + Locale.forLanguageTag( "it" ), + Locale.forLanguageTag( "ja" ), + Locale.forLanguageTag( "ko" ), + Locale.forLanguageTag( "mn-MN" ), + Locale.forLanguageTag( "nl" ), + Locale.forLanguageTag( "pl" ), + Locale.forLanguageTag( "pt-BR" ), + Locale.forLanguageTag( "ro" ), + Locale.forLanguageTag( "ru" ), + Locale.forLanguageTag( "sk" ), + Locale.forLanguageTag( "tr" ), + Locale.forLanguageTag( "uk" ), + Locale.forLanguageTag( "zh-CN" ), + Locale.forLanguageTag( "zh-TW" ), + Locale.forLanguageTag( "zh" ) + ); + + @Test + public void testMessageProperties() throws NoSuchMethodException, SecurityException { + List invalidMessages = new ArrayList<>(); + + Locale defaultLocale = Locale.getDefault(); + + try { + for ( Locale locale : ALL_SUPPORTED_LOCALES ) { + Locale.setDefault( locale ); + + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory() + .getValidator(); + + Set> violations = validator.validate( new Bean() ); + + ConstraintViolationAssert.assertThat( violations ) + .containsOnlyViolations( + violationOf( AssertFalse.class ), + violationOf( AssertTrue.class ), + violationOf( DecimalMax.class ), + violationOf( DecimalMin.class ), + violationOf( Digits.class ), + violationOf( Email.class ), + violationOf( Future.class ), + violationOf( FutureOrPresent.class ), + violationOf( Max.class ), + violationOf( Min.class ), + violationOf( Negative.class ), + violationOf( NegativeOrZero.class ), + violationOf( NotBlank.class ), + violationOf( NotEmpty.class ), + violationOf( NotNull.class ), + violationOf( Null.class ), + violationOf( Past.class ), + violationOf( PastOrPresent.class ), + violationOf( Pattern.class ), + violationOf( Positive.class ), + violationOf( PositiveOrZero.class ), + violationOf( Size.class ), + violationOf( CreditCardNumber.class ), + violationOf( Currency.class ), + violationOf( EAN.class ), + violationOf( org.hibernate.validator.constraints.Email.class ), + violationOf( ISBN.class ), + violationOf( Length.class ), + violationOf( CodePointLength.class ), + violationOf( LuhnCheck.class ), + violationOf( Mod10Check.class ), + violationOf( Mod11Check.class ), + violationOf( ModCheck.class ), + violationOf( org.hibernate.validator.constraints.NotBlank.class ), + violationOf( org.hibernate.validator.constraints.NotEmpty.class ), + violationOf( Range.class ), + violationOf( SafeHtml.class ), + violationOf( UniqueElements.class ), + violationOf( URL.class ), + violationOf( CNPJ.class ), + violationOf( CPF.class ), + violationOf( TituloEleitoral.class ), + violationOf( REGON.class ), + violationOf( NIP.class ), + violationOf( PESEL.class ), + violationOf( DurationMax.class ), + violationOf( DurationMin.class ), + violationOf( ScriptAssert.class ) + ); + + collectInvalidMessages( locale, invalidMessages, violations ); + + Set> parameterScriptAssertBeanViolations = validator.forExecutables().validateParameters( + new ParameterScriptAssertBean(), ParameterScriptAssertBean.class.getDeclaredMethod( "doTest", boolean.class ), new Object[]{ false } ); + + ConstraintViolationAssert.assertThat( parameterScriptAssertBeanViolations ) + .containsOnlyViolations( + violationOf( ParameterScriptAssert.class ) ); + + collectInvalidMessages( locale, invalidMessages, parameterScriptAssertBeanViolations ); + } + + if ( !invalidMessages.isEmpty() ) { + throw new IllegalStateException( "Some messages are invalid:\n\t- " + String.join( "\n\t- ", invalidMessages ) + "\n" ); + } + } + finally { + Locale.setDefault( defaultLocale ); + } + } + + private void collectInvalidMessages(Locale locale, List invalidMessages, Set> violations) { + for ( ConstraintViolation violation : violations ) { + if ( violation.getMessage().contains( "{" ) ) { + invalidMessages.add( + "Message for constraint " + violation.getConstraintDescriptor().getAnnotation().annotationType() + " and locale " + locale + + " contains a curly brace: " + violation.getMessage() ); + } + if ( violation.getMessage().contains( "$" ) ) { + invalidMessages.add( + "Message for constraint " + violation.getConstraintDescriptor().getAnnotation().annotationType() + " and locale " + locale + + " contains a dollar sign: " + violation.getMessage() ); + } + } + } + + @ScriptAssert(lang = "groovy", script = "_this.scriptAssert") + private static class Bean { + + @AssertFalse + private boolean assertFalse = true; + + @AssertTrue + private boolean assertTrue = false; + + @DecimalMax("3") + private double decimalMax = 4; + + @DecimalMin("3") + private double decimalMin = 2; + + @Digits(integer = 1, fraction = 3) + private BigDecimal digits = BigDecimal.valueOf( 13333.3333f ); + + @Email + private String email = "invalid"; + + @Future + private LocalDate future = LocalDate.of( 2010, 10, 4 ); + + @FutureOrPresent + private LocalDate futureOrPresent = LocalDate.of( 2010, 10, 4 ); + + @Max(4) + private int max = 6; + + @Min(4) + private int min = 2; + + @Negative + private int negative = 4; + + @NegativeOrZero + private int negativeOrZero = 4; + + @NotBlank + private String notBlank = ""; + + @NotEmpty + private List notEmpty = Collections.emptyList(); + + @NotNull + private String notNull = null; + + @Null + private String nullConstraint = "not null"; + + @Past + private LocalDate past = LocalDate.of( 2890, 10, 4 ); + + @PastOrPresent + private LocalDate pastOrPresent = LocalDate.of( 2890, 10, 4 ); + + @Pattern(regexp = "[0-9]+") + private String pattern = "invalid"; + + @Positive + private int positive = -4; + + @PositiveOrZero + private int positiveOrZero = -4; + + @Size(min = 2, max = 4) + private String size = "666666"; + + @CreditCardNumber + private String creditCardNumber = "invalid"; + + @Currency("EUR") + private MonetaryAmount currency = Money.of( 1000f, "USD" ); + + @EAN + private String ean = "invalid"; + + @org.hibernate.validator.constraints.Email + private String hvEmail = "invalid"; + + @ISBN + private String isbn = "invalid"; + + @Length(min = 2, max = 4) + private String length = "666666"; + + @CodePointLength(min = 2, max = 4) + private String codePointLength = "666666"; + + @LuhnCheck + private String luhnCheck = "4"; + + @Mod10Check + private String mod10Check = "4"; + + @Mod11Check + private String mod11Check = "4"; + + @ModCheck(multiplier = 2, modType = ModCheck.ModType.MOD10) + private String modCheck = "4"; + + @org.hibernate.validator.constraints.NotBlank + private String hvNotBlank = ""; + + @org.hibernate.validator.constraints.NotEmpty + private List hvNotEmpty = Collections.emptyList(); + + @Range(min = 2, max = 4) + private int range = 6; + + @SafeHtml + private String safeHtml = ""; + + @UniqueElements + private List uniqueElements = Arrays.asList( "a", "a" ); + + @URL + private String url = "invalid"; + + @CNPJ + private String cnpj = "invalid"; + + @CPF + private String cpf = "invalid"; + + @TituloEleitoral + private String tituloEleitoral = "invalid"; + + @REGON + private String regon = "invalid"; + + @NIP + private String nip = "invalid"; + + @PESEL + private String pesel = "invalid"; + + @DurationMax(days = 4, hours = 4, minutes = 4, millis = 4, nanos = 4) + private Duration durationMax = Duration.ofDays( 8 ); + + @DurationMin(days = 4, hours = 4, minutes = 4, millis = 4, nanos = 4) + private Duration durationMin = Duration.ofDays( 2 ); + + @SuppressWarnings("unused") + private boolean scriptAssert = false; + } + + private static class ParameterScriptAssertBean { + + @ParameterScriptAssert(lang = "groovy", script = "test") + public boolean doTest(boolean test) { + return test; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java index 9e826882af..f0c016aefd 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java @@ -97,6 +97,7 @@ protected void testValidatorDouble(ConstraintValidator constraint, bo assertTrue( constraint.isValid( null, null ) ); assertEquals( constraint.isValid( 14.99, null ), isMax ); + assertEquals( constraint.isValid( 15.001, null ), !isMax ); assertEquals( constraint.isValid( -14.99, null ), isMax ); assertEquals( constraint.isValid( -1560000000D, null ), isMax ); assertEquals( constraint.isValid( Double.NEGATIVE_INFINITY, null ), isMax ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java index d75b087855..cf718aabc6 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java @@ -6,6 +6,11 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.bv; +import static org.testng.Assert.assertFalse; + +import java.math.BigDecimal; +import java.math.BigInteger; + import javax.validation.constraints.DecimalMax; import javax.validation.constraints.Max; @@ -79,6 +84,23 @@ public void testIsValidDecimalMaxExclusive() { } + @Test + @TestForIssue(jiraKey = "HV-1699") + public void testIsValidNumberForFloatingPointOrBigNumbersStoredAsNumber() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Max.class ); + descriptorBuilder.setAttribute( "value", 1L ); + Max m = descriptorBuilder.build().getAnnotation(); + MaxValidatorForNumber validator = new MaxValidatorForNumber(); + validator.initialize( m ); + + assertFalse( validator.isValid( 1.01, null ) ); + assertFalse( validator.isValid( 1.01F, null ) ); + assertFalse( validator.isValid( new BigDecimal( "1.01" ), null ) ); + assertFalse( validator.isValid( new BigInteger( "2" ), null ) ); + assertFalse( validator.isValid( Double.POSITIVE_INFINITY, null ) ); + assertFalse( validator.isValid( Float.POSITIVE_INFINITY, null ) ); + } + private void testDecimalMax(DecimalMax m, boolean inclusive) { AbstractDecimalMaxValidator constraint = new DecimalMaxValidatorForNumber(); constraint.initialize( m ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java index 09e3da8900..5a841e602b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/size/SizeValidatorTest.java @@ -177,7 +177,7 @@ private ConstraintValidator getValidatorMin1Max2(Class validator descriptorBuilder.setMessage( "{validator.max}" ); Size m = descriptorBuilder.build().getAnnotation(); @SuppressWarnings("unchecked") - ConstraintValidator validator = (ConstraintValidator) validatorClass.newInstance(); + ConstraintValidator validator = (ConstraintValidator) validatorClass.getConstructor().newInstance(); validator.initialize( m ); return validator; } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderFutureTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderFutureTest.java index b42e0ffca5..ea7fc2c178 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderFutureTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderFutureTest.java @@ -10,11 +10,9 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; -import java.time.Clock; import java.time.ZoneId; import java.time.ZonedDateTime; -import javax.validation.ClockProvider; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.validation.constraints.Future; @@ -89,19 +87,4 @@ private static class Order { private ReadablePartial shipmentDateAsReadablePartial; } - - private static class FixedClockProvider implements ClockProvider { - - private Clock clock; - - public FixedClockProvider(ZonedDateTime dateTime) { - clock = Clock.fixed( dateTime.toInstant(), dateTime.getZone() ); - } - - @Override - public Clock getClock() { - return clock; - } - - } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderPastTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderPastTest.java index 356da61578..5f75be83e0 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderPastTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/ClockProviderPastTest.java @@ -10,11 +10,9 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; -import java.time.Clock; import java.time.ZoneId; import java.time.ZonedDateTime; -import javax.validation.ClockProvider; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.validation.constraints.Past; @@ -88,19 +86,4 @@ private static class Order { private ReadablePartial orderDateAsReadablePartial; } - - private static class FixedClockProvider implements ClockProvider { - - private Clock clock; - - public FixedClockProvider(ZonedDateTime dateTime) { - clock = Clock.fixed( dateTime.toInstant(), dateTime.getZone() ); - } - - @Override - public Clock getClock() { - return clock; - } - - } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/FixedClockProvider.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/FixedClockProvider.java new file mode 100644 index 0000000000..a2127c5e48 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/FixedClockProvider.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.constraintvalidators.bv.time; + +import java.time.Clock; +import java.time.ZonedDateTime; + +import javax.validation.ClockProvider; + +class FixedClockProvider implements ClockProvider { + + private Clock clock; + + public FixedClockProvider(ZonedDateTime dateTime) { + clock = Clock.fixed( dateTime.toInstant(), dateTime.getZone() ); + } + + @Override + public Clock getClock() { + return clock; + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/JavaSqlDateTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/JavaSqlDateTest.java new file mode 100644 index 0000000000..29bb6214c6 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/time/JavaSqlDateTest.java @@ -0,0 +1,94 @@ +/* + * 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.constraintvalidators.bv.time; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Future; +import javax.validation.constraints.FutureOrPresent; +import javax.validation.constraints.Past; +import javax.validation.constraints.PastOrPresent; + +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1878") +public class JavaSqlDateTest { + + private static final ZoneId TZ_BERLIN = ZoneId.of( "Europe/Berlin" ); + + private Validator validator; + + @BeforeMethod + public void setupValidator() { + FixedClockProvider clockProvider = new FixedClockProvider( + ZonedDateTime.of( + 2000, 2, 15, 4, 0, 0, 0, + TZ_BERLIN ) ); + ValidatorFactory validatorFactory = getConfiguration() + .clockProvider( clockProvider ) + .buildValidatorFactory(); + + validator = validatorFactory.getValidator(); + } + + @Test + public void testFuture() { + JavaSqlDateHolder javaSqlDateHolder = new JavaSqlDateHolder( + new java.sql.Date( LocalDate.of( 2010, 2, 2 ).atStartOfDay( ZoneId.systemDefault() ).toInstant().toEpochMilli() ) ); + Set> constraintViolations = validator.validate( javaSqlDateHolder ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Past.class ), + violationOf( PastOrPresent.class ) ); + } + + @Test + public void testPast() { + JavaSqlDateHolder javaSqlDateHolder = new JavaSqlDateHolder( + new java.sql.Date( LocalDate.of( 1990, 2, 2 ).atStartOfDay( ZoneId.systemDefault() ).toInstant().toEpochMilli() ) ); + Set> constraintViolations = validator.validate( javaSqlDateHolder ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Future.class ), + violationOf( FutureOrPresent.class ) ); + } + + private static class JavaSqlDateHolder { + + @Future + private java.sql.Date future; + + @FutureOrPresent + private java.sql.Date futureOrPresent; + + @Past + private java.sql.Date past; + + @PastOrPresent + private java.sql.Date pastOrPresent; + + JavaSqlDateHolder(java.sql.Date date) { + this.future = date; + this.futureOrPresent = date; + this.past = date; + this.pastOrPresent = date; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java index 804e36658c..f4c0182b79 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EmailValidatorTest.java @@ -25,10 +25,10 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.cfg.defs.EmailDef; import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; +import org.hibernate.validator.internal.util.DomainNameUtil; import org.hibernate.validator.testutil.MyCustomStringImpl; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ValidatorUtil; - import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -222,6 +222,12 @@ public void testEmailWith256CharacterDomainPartIsInvalid() { isInvalidEmail( "foo@" + domainOfLength( 252 ) + ".com" ); } + @Test + @TestForIssue(jiraKey = "HV-1833") + public void testLongEmail() { + assertEquals( false, DomainNameUtil.isValidEmailDomainAddress( stringOfLength( 5000 ) + ".com" ) ); + } + private String stringOfLength(int length) { StringBuilder builder = new StringBuilder(); for ( int i = 0; i < length; i++ ) { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ISBNValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ISBNValidatorTest.java index 3a3a355650..543f6d4a20 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ISBNValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ISBNValidatorTest.java @@ -58,6 +58,106 @@ public void validISBN10() throws Exception { assertValidISBN( "0-85131-041-9" ); assertValidISBN( "0-684-84328-5" ); assertValidISBN( "1-84356-028-3" ); + assertValidISBN( "3-598-21500-2" ); + assertValidISBN( "3-598-21501-0" ); + assertValidISBN( "3-598-21502-9" ); + assertValidISBN( "3-598-21503-7" ); + assertValidISBN( "3-598-21504-5" ); + assertValidISBN( "3-598-21505-3" ); + assertValidISBN( "3-598-21506-1" ); + assertValidISBN( "3-598-21507-X" ); + assertValidISBN( "3-598-21508-8" ); + assertValidISBN( "3-598-21509-6" ); + assertValidISBN( "3-598-21510-X" ); + assertValidISBN( "3-598-21511-8" ); + assertValidISBN( "3-598-21512-6" ); + assertValidISBN( "3-598-21513-4" ); + assertValidISBN( "3-598-21514-2" ); + assertValidISBN( "3-598-21515-0" ); + assertValidISBN( "3-598-21516-9" ); + assertValidISBN( "3-598-21517-7" ); + assertValidISBN( "3-598-21518-5" ); + assertValidISBN( "3-598-21519-3" ); + assertValidISBN( "3-598-21520-7" ); + assertValidISBN( "3-598-21521-5" ); + assertValidISBN( "3-598-21522-3" ); + assertValidISBN( "3-598-21523-1" ); + assertValidISBN( "3-598-21524-X" ); + assertValidISBN( "3-598-21525-8" ); + assertValidISBN( "3-598-21526-6" ); + assertValidISBN( "3-598-21527-4" ); + assertValidISBN( "3-598-21528-2" ); + assertValidISBN( "3-598-21529-0" ); + assertValidISBN( "3-598-21530-4" ); + assertValidISBN( "3-598-21531-2" ); + assertValidISBN( "3-598-21532-0" ); + assertValidISBN( "3-598-21533-9" ); + assertValidISBN( "3-598-21534-7" ); + assertValidISBN( "3-598-21535-5" ); + assertValidISBN( "3-598-21536-3" ); + assertValidISBN( "3-598-21537-1" ); + assertValidISBN( "3-598-21538-X" ); + assertValidISBN( "3-598-21539-8" ); + assertValidISBN( "3-598-21540-1" ); + assertValidISBN( "3-598-21541-X" ); + assertValidISBN( "3-598-21542-8" ); + assertValidISBN( "3-598-21543-6" ); + assertValidISBN( "3-598-21544-4" ); + assertValidISBN( "3-598-21545-2" ); + assertValidISBN( "3-598-21546-0" ); + assertValidISBN( "3-598-21547-9" ); + assertValidISBN( "3-598-21548-7" ); + assertValidISBN( "3-598-21549-5" ); + assertValidISBN( "3-598-21550-9" ); + assertValidISBN( "3-598-21551-7" ); + assertValidISBN( "3-598-21552-5" ); + assertValidISBN( "3-598-21553-3" ); + assertValidISBN( "3-598-21554-1" ); + assertValidISBN( "3-598-21555-X" ); + assertValidISBN( "3-598-21556-8" ); + assertValidISBN( "3-598-21557-6" ); + assertValidISBN( "3-598-21558-4" ); + assertValidISBN( "3-598-21559-2" ); + assertValidISBN( "3-598-21560-6" ); + assertValidISBN( "3-598-21561-4" ); + assertValidISBN( "3-598-21562-2" ); + assertValidISBN( "3-598-21563-0" ); + assertValidISBN( "3-598-21564-9" ); + assertValidISBN( "3-598-21565-7" ); + assertValidISBN( "3-598-21566-5" ); + assertValidISBN( "3-598-21567-3" ); + assertValidISBN( "3-598-21568-1" ); + assertValidISBN( "3-598-21569-X" ); + assertValidISBN( "3-598-21570-3" ); + assertValidISBN( "3-598-21571-1" ); + assertValidISBN( "3-598-21572-X" ); + assertValidISBN( "3-598-21573-8" ); + assertValidISBN( "3-598-21574-6" ); + assertValidISBN( "3-598-21575-4" ); + assertValidISBN( "3-598-21576-2" ); + assertValidISBN( "3-598-21577-0" ); + assertValidISBN( "3-598-21578-9" ); + assertValidISBN( "3-598-21579-7" ); + assertValidISBN( "3-598-21580-0" ); + assertValidISBN( "3-598-21581-9" ); + assertValidISBN( "3-598-21582-7" ); + assertValidISBN( "3-598-21583-5" ); + assertValidISBN( "3-598-21584-3" ); + assertValidISBN( "3-598-21585-1" ); + assertValidISBN( "3-598-21586-X" ); + assertValidISBN( "3-598-21587-8" ); + assertValidISBN( "3-598-21588-6" ); + assertValidISBN( "3-598-21589-4" ); + assertValidISBN( "3-598-21590-8" ); + assertValidISBN( "3-598-21591-6" ); + assertValidISBN( "3-598-21592-4" ); + assertValidISBN( "3-598-21593-2" ); + assertValidISBN( "3-598-21594-0" ); + assertValidISBN( "3-598-21595-9" ); + assertValidISBN( "3-598-21596-7" ); + assertValidISBN( "3-598-21597-5" ); + assertValidISBN( "3-598-21598-3" ); + assertValidISBN( "3-598-21599-1" ); } @Test @@ -94,6 +194,106 @@ public void validISBN13() throws Exception { assertValidISBN( "978-0-684-84328-5" ); assertValidISBN( "978-1-84356-028-9" ); assertValidISBN( "978-0-54560-495-6" ); + assertValidISBN( "978-3-598-21500-1" ); + assertValidISBN( "978-3-598-21501-8" ); + assertValidISBN( "978-3-598-21502-5" ); + assertValidISBN( "978-3-598-21503-2" ); + assertValidISBN( "978-3-598-21504-9" ); + assertValidISBN( "978-3-598-21505-6" ); + assertValidISBN( "978-3-598-21506-3" ); + assertValidISBN( "978-3-598-21507-0" ); + assertValidISBN( "978-3-598-21508-7" ); + assertValidISBN( "978-3-598-21509-4" ); + assertValidISBN( "978-3-598-21510-0" ); + assertValidISBN( "978-3-598-21511-7" ); + assertValidISBN( "978-3-598-21512-4" ); + assertValidISBN( "978-3-598-21513-1" ); + assertValidISBN( "978-3-598-21514-8" ); + assertValidISBN( "978-3-598-21515-5" ); + assertValidISBN( "978-3-598-21516-2" ); + assertValidISBN( "978-3-598-21517-9" ); + assertValidISBN( "978-3-598-21518-6" ); + assertValidISBN( "978-3-598-21519-3" ); + assertValidISBN( "978-3-598-21520-9" ); + assertValidISBN( "978-3-598-21521-6" ); + assertValidISBN( "978-3-598-21522-3" ); + assertValidISBN( "978-3-598-21523-0" ); + assertValidISBN( "978-3-598-21524-7" ); + assertValidISBN( "978-3-598-21525-4" ); + assertValidISBN( "978-3-598-21526-1" ); + assertValidISBN( "978-3-598-21527-8" ); + assertValidISBN( "978-3-598-21528-5" ); + assertValidISBN( "978-3-598-21529-2" ); + assertValidISBN( "978-3-598-21530-8" ); + assertValidISBN( "978-3-598-21531-5" ); + assertValidISBN( "978-3-598-21532-2" ); + assertValidISBN( "978-3-598-21533-9" ); + assertValidISBN( "978-3-598-21534-6" ); + assertValidISBN( "978-3-598-21535-3" ); + assertValidISBN( "978-3-598-21536-0" ); + assertValidISBN( "978-3-598-21537-7" ); + assertValidISBN( "978-3-598-21538-4" ); + assertValidISBN( "978-3-598-21539-1" ); + assertValidISBN( "978-3-598-21540-7" ); + assertValidISBN( "978-3-598-21541-4" ); + assertValidISBN( "978-3-598-21542-1" ); + assertValidISBN( "978-3-598-21543-8" ); + assertValidISBN( "978-3-598-21544-5" ); + assertValidISBN( "978-3-598-21545-2" ); + assertValidISBN( "978-3-598-21546-9" ); + assertValidISBN( "978-3-598-21547-6" ); + assertValidISBN( "978-3-598-21548-3" ); + assertValidISBN( "978-3-598-21549-0" ); + assertValidISBN( "978-3-598-21550-6" ); + assertValidISBN( "978-3-598-21551-3" ); + assertValidISBN( "978-3-598-21552-0" ); + assertValidISBN( "978-3-598-21553-7" ); + assertValidISBN( "978-3-598-21554-4" ); + assertValidISBN( "978-3-598-21555-1" ); + assertValidISBN( "978-3-598-21556-8" ); + assertValidISBN( "978-3-598-21557-5" ); + assertValidISBN( "978-3-598-21558-2" ); + assertValidISBN( "978-3-598-21559-9" ); + assertValidISBN( "978-3-598-21560-5" ); + assertValidISBN( "978-3-598-21561-2" ); + assertValidISBN( "978-3-598-21562-9" ); + assertValidISBN( "978-3-598-21563-6" ); + assertValidISBN( "978-3-598-21564-3" ); + assertValidISBN( "978-3-598-21565-0" ); + assertValidISBN( "978-3-598-21566-7" ); + assertValidISBN( "978-3-598-21567-4" ); + assertValidISBN( "978-3-598-21568-1" ); + assertValidISBN( "978-3-598-21569-8" ); + assertValidISBN( "978-3-598-21570-4" ); + assertValidISBN( "978-3-598-21571-1" ); + assertValidISBN( "978-3-598-21572-8" ); + assertValidISBN( "978-3-598-21573-5" ); + assertValidISBN( "978-3-598-21574-2" ); + assertValidISBN( "978-3-598-21575-9" ); + assertValidISBN( "978-3-598-21576-6" ); + assertValidISBN( "978-3-598-21577-3" ); + assertValidISBN( "978-3-598-21578-0" ); + assertValidISBN( "978-3-598-21579-7" ); + assertValidISBN( "978-3-598-21580-3" ); + assertValidISBN( "978-3-598-21581-0" ); + assertValidISBN( "978-3-598-21582-7" ); + assertValidISBN( "978-3-598-21583-4" ); + assertValidISBN( "978-3-598-21584-1" ); + assertValidISBN( "978-3-598-21585-8" ); + assertValidISBN( "978-3-598-21586-5" ); + assertValidISBN( "978-3-598-21587-2" ); + assertValidISBN( "978-3-598-21588-9" ); + assertValidISBN( "978-3-598-21589-6" ); + assertValidISBN( "978-3-598-21590-2" ); + assertValidISBN( "978-3-598-21591-9" ); + assertValidISBN( "978-3-598-21592-6" ); + assertValidISBN( "978-3-598-21593-3" ); + assertValidISBN( "978-3-598-21594-0" ); + assertValidISBN( "978-3-598-21595-7" ); + assertValidISBN( "978-3-598-21596-4" ); + assertValidISBN( "978-3-598-21597-1" ); + assertValidISBN( "978-3-598-21598-8" ); + assertValidISBN( "978-3-598-21599-5" ); } @Test diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java index 7eb9e31737..45e6f031da 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java @@ -57,6 +57,44 @@ public void testInvalidScriptTagIncluded() throws Exception { assertFalse( getSafeHtmlValidator().isValid( "HelloWorld !", null ) ); } + @Test + // A "downlevel revealed" conditional 'comment' is not an (X)HTML comment at all, + // despite the misleading name, it is default Microsoft syntax. + // The tag is unrecognized by therefore executed + public void testDownlevelRevealedConditionalComment() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "\n\n", null ) ); + } + + @Test + public void testDownlevelHiddenConditionalComment() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "", null ) ); + } + + @Test + public void testSimpleComment() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "", null ) ); + } + + @Test + public void testServerSideIncludesSSI() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "alert{\"XSS\"}'}; ?>", null ) ); + } + + @Test + public void testPHPScript() throws Exception { + descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); + + assertFalse( getSafeHtmlValidator().isValid( "alert{\"XSS\"}'}; ?>", null ) ); + } + @Test public void testInvalidIncompleteImgTagWithScriptIncluded() { descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryBeanMetadataClassNormalizerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryBeanMetadataClassNormalizerTest.java new file mode 100644 index 0000000000..33e930c256 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ValidatorFactoryBeanMetadataClassNormalizerTest.java @@ -0,0 +1,120 @@ +/* + * 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; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Email; +import javax.validation.valueextraction.Unwrapping; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.internal.engine.ValidatorFactoryImpl; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; + +import org.testng.annotations.Test; + +/** + * Test for {@link ValidatorFactoryImpl}. + * + * @author Gunnar Morling + */ +public class ValidatorFactoryBeanMetadataClassNormalizerTest { + + @Test(expectedExceptions = ValidationException.class, + expectedExceptionsMessageRegExp = ".*No suitable value extractor found for type interface java.util.List.*") + public void testBeanMetaDataClassNormalizerNoNormalizer() throws NoSuchMethodException { + ValidatorFactory validatorFactory = Validation.byDefaultProvider() + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + // As the proxy defines invalid constraints (see BeanProxy), we expect this to fail + validator.forExecutables().validateParameters( + new BeanProxy(), BeanProxy.class.getMethod( "setEmails", List.class ), + new Object[] { Arrays.asList( "notAnEmail" ) } + ); + } + + @Test + public void testBeanMetaDataClassNormalizer() throws NoSuchMethodException { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .beanMetaDataClassNormalizer( new MyProxyInterfaceBeanMetaDataClassNormalizer() ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + Set> violations = validator.forExecutables().validateParameters( + new BeanProxy(), BeanProxy.class.getMethod( "setEmails", List.class ), + new Object[] { Arrays.asList( "notAnEmail" ) } + ); + + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ).withPropertyPath( + pathWith().method( "setEmails" ) + .parameter( "emails", 0 ) + .containerElement( "", true, null, 0, List.class, 0 ) + ) + ); + } + + private static class Bean { + + private List emails; + + public Bean() { + } + + public Bean(List emails) { + this.emails = emails; + } + + public List getEmails() { + return emails; + } + + public void setEmails(@Email(payload = Unwrapping.Unwrap.class) List emails) { + this.emails = emails; + } + } + + private interface MyProxyInterface { + } + + private static class MyProxyInterfaceBeanMetaDataClassNormalizer implements BeanMetaDataClassNormalizer { + + @Override + public Class normalize(Class beanClass) { + if ( MyProxyInterface.class.isAssignableFrom( beanClass ) ) { + return beanClass.getSuperclass(); + } + + return beanClass; + } + } + + private static class BeanProxy extends Bean implements MyProxyInterface { + // The proxy dropped the generics, but kept constraint annotations, + // which will cause trouble unless its metadata is ignored. + @Override + @SuppressWarnings("unchecked") + public void setEmails(@Email(payload = Unwrapping.Unwrap.class) List emails) { + super.setEmails( emails ); + } + } +} 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 2efac1fdcf..869915b1b9 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 @@ -27,6 +27,7 @@ import javax.validation.Validator; import javax.validation.constraints.Min; +import org.hibernate.validator.internal.IgnoreForbiddenApisErrors; import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader; import org.hibernate.validator.internal.util.privilegedactions.SetContextClassLoader; import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; @@ -143,6 +144,7 @@ public ELIgnoringClassLoader( String packageMissing ) { } @Override + @IgnoreForbiddenApisErrors(reason = "getPackage() is deprecated but getDefinedPackage() is only available from JDK 9.") public Class loadClass(String className) throws ClassNotFoundException { // This is what we in the end want to achieve. Throw ClassNotFoundException for javax.el classes if ( className.startsWith( packageMissing ) ) { @@ -230,7 +232,7 @@ private void runWithoutElLibs(Class delegateType, String packageMissing) thro ClassLoader classLoader = new ELIgnoringClassLoader( packageMissing ); run( SetContextClassLoader.action( classLoader ) ); - Object test = classLoader.loadClass( delegateType.getName() ).newInstance(); + Object test = classLoader.loadClass( delegateType.getName() ).getConstructor().newInstance(); test.getClass().getMethod( "run" ).invoke( test ); } finally { 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 fa9c670e7e..136158099e 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 @@ -32,6 +32,8 @@ import org.hibernate.validator.testutil.TestForIssue; import org.testng.Assert; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import org.testng.collections.Lists; @@ -48,6 +50,16 @@ public class HibernateConstraintValidatorContextTest { private static final List INVALID_KEYWORDS = Lists.newArrayList( "foo", "bar", "baz" ); + @BeforeTest + public void before() { + System.setProperty( "org.hibernate.validator.expressionLanguageEnabled", "true" ); + } + + @AfterTest + public void after() { + System.clearProperty( "org.hibernate.validator.expressionLanguageEnabled" ); + } + // Message parameters @Test diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java index 5075439f51..c78abcee4c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/conversion/AbstractGroupConversionTest.java @@ -23,6 +23,7 @@ import javax.validation.GroupSequence; import javax.validation.Valid; import javax.validation.Validator; +import javax.validation.constraints.AssertTrue; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.validation.groups.ConvertGroup; @@ -30,6 +31,7 @@ import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.testutils.CandidateForTck; import org.testng.annotations.Test; /** @@ -214,6 +216,19 @@ public void conversionFromSequenceCausesException() { validator.validate( new User8() ); } + @Test + @CandidateForTck + public void sameBeanDifferentGroups() { + Set> violations = validator.validate( new User9() ); + assertThat( violations ).containsOnlyViolations( + violationOf( AssertTrue.class ).withPropertyPath( pathWith() + .property( "a" ) + .property( "b" ) + ) + ); + } + + public interface Complete extends Default { } @@ -341,4 +356,12 @@ private static class User8 { @ConvertGroup(from = PostalSequence.class, to = BasicPostal.class) private final List

    addresses = Arrays.asList( new Address() ); } + + private static class User9 { + @Valid + @ConvertGroup(from = Default.class, to = BasicNumber.class) + User9 a = this; + @AssertTrue(groups = BasicNumber.class) + boolean b; + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOnObjectsWithCycles.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOnObjectsWithCycles.java new file mode 100644 index 0000000000..97e8439ee4 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceOnObjectsWithCycles.java @@ -0,0 +1,64 @@ +/* + * 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.Valid; +import javax.validation.Validator; + +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; + +import org.testng.Assert; +import org.testng.annotations.Test; + +@TestForIssue(jiraKey = "HV-1692") +public class SequenceOnObjectsWithCycles { + + @Test + public void groupSequenceOfGroupSequences() { + Validator validator = ValidatorUtil.getValidator(); + + YourAnnotatedBean yourEntity1 = new YourAnnotatedBean(); + AnotherBean anotherBean = new AnotherBean(); + anotherBean.setYourAnnotatedBean( yourEntity1 ); + yourEntity1.setBean( anotherBean ); + + Set> constraintViolations = validator.validate( yourEntity1 ); + Assert.assertEquals( 0, constraintViolations.size() ); + + } + + @GroupSequence({ AnotherBean.class, Magic.class }) + public class AnotherBean { + + @Valid + private YourAnnotatedBean yourAnnotatedBean; + + + public void setYourAnnotatedBean(YourAnnotatedBean yourAnnotatedBean) { + this.yourAnnotatedBean = yourAnnotatedBean; + } + } + + @GroupSequence({ YourAnnotatedBean.class, Magic.class }) + public class YourAnnotatedBean { + + @Valid + private AnotherBean bean; + + public void setBean(AnotherBean bean) { + this.bean = bean; + } + } + + public interface Magic { + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceTest.java new file mode 100644 index 0000000000..e1d11295f6 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/sequence/SequenceTest.java @@ -0,0 +1,89 @@ +/* + * 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 static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +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.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +@TestForIssue( jiraKey = "HV-1715") +public class SequenceTest { + + @Test + public void groupSequenceOfGroupSequences() { + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new Foo( null, "", null ) ); + + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "str1" ) + ); + } + + interface Group1 extends Group11, Group12, Group13, Group14, Group15, Group16, Group17, Group18, Group19 { + } + + interface Group11 { + } + + interface Group12 { + } + + interface Group13 { + } + + interface Group14 { + } + + interface Group15 { + } + + interface Group16 { + } + + interface Group17 { + } + + interface Group18 { + } + + interface Group19 { + } + + interface Group2 { + } + + @GroupSequence({ Foo.class, SequenceTest.Group1.class, SequenceTest.Group2.class }) + private static class Foo { + @NotNull(groups = SequenceTest.Group11.class) + private String str1; + @NotNull(groups = SequenceTest.Group12.class) + private String str2; + @NotNull(groups = SequenceTest.Group2.class) + private String str3; + + public Foo(String str1, String str2, String str3) { + this.str1 = str1; + this.str2 = str2; + this.str3 = str3; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/AbstractTokenCollectorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/AbstractTokenCollectorTest.java new file mode 100644 index 0000000000..004d5c9bd2 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/AbstractTokenCollectorTest.java @@ -0,0 +1,186 @@ +/* + * 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.messageinterpolation; + +import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.Token; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; + +import org.assertj.core.api.Assertions; +import org.testng.annotations.Test; + +/** + * Abstract base for {@code TokenCollector} tests. + * + * @author Hardy Ferentschik + */ +public abstract class AbstractTokenCollectorTest { + + protected abstract InterpolationTermType getInterpolationTermType(); + + @Test + public void testLiteral() { + Assertions.assertThat( + new TokenCollector( "foo bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "foo bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000169.*") + public void testNestedParametersThrowException() { + new TokenCollector( "#{foo {}", getInterpolationTermType() ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testClosingBraceWithoutOpeningBraceThrowsException() { + new TokenCollector( "foo}", getInterpolationTermType() ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testOpeningBraceWithoutClosingBraceThrowsException() { + new TokenCollector( "{foo", getInterpolationTermType() ); + } + + @Test + public void testBackslashEscapesNonMetaCharacter() { + Assertions.assertThat( + new TokenCollector( "foo \\bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesDollar() { + Assertions.assertThat( + new TokenCollector( "foo \\$ bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\$ bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesOpeningBrace() { + Assertions.assertThat( + new TokenCollector( "foo \\{ bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\{ bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesClosingBrace() { + Assertions.assertThat( + new TokenCollector( "foo \\} bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\} bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesBackslash() { + Assertions.assertThat( + new TokenCollector( "foo \\\\ bar", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\\\ bar", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesEL() { + Assertions.assertThat( + new TokenCollector( "foo \\$\\{bar\\}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\$\\{bar\\}", Token::getTokenValue ) + // What's important is that we did NOT detect the expression + .returns( false, Token::isParameter ); + } + + @Test + public void testBackslashEscapesParameter() { + Assertions.assertThat( + new TokenCollector( "foo \\{bar\\}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + // Backslashes are removed later, in AbstractMessageInterpolator.replaceEscapedLiterals + .returns( "foo \\{bar\\}", Token::getTokenValue ) + // What's important is that we did NOT detect the parameter + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testTrailingClosingBraceThrowsException() { + new TokenCollector( "this message contains a invalid parameter start token {", getInterpolationTermType() ); + } + + @Test + public void testDollarThenNonMetaCharacterInterpretedAsLiteral() { + Assertions.assertThat( + new TokenCollector( "$a", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "$a", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testTrailingDollarInterpretedAsLiteral() { + Assertions.assertThat( + new TokenCollector( "foo $", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "foo $", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testTrailingBackslashInterpretedAsLiteral() { + Assertions.assertThat( + new TokenCollector( "foo \\", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ) + .element( 0 ) + .returns( "foo \\", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java index b9515ff476..ce90ba0795 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ExpressionLanguageMessageInterpolationTest.java @@ -66,8 +66,10 @@ public void testExpressionLanguageGraphNavigation() { notNullDescriptor, user, null, + null, + Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyMap() + true ); String expected = "18"; @@ -81,9 +83,10 @@ public void testUnknownPropertyInExpressionLanguageGraphNavigation() { notNullDescriptor, new User(), null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + true ); String expected = "${validatedValue.foo}"; String actual = interpolatorUnderTest.interpolate( "${validatedValue.foo}", context ); @@ -171,9 +174,10 @@ public void testLocaleBasedFormatting() { notNullDescriptor, 42.00000d, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + true ); // german locale String expected = "42,00"; @@ -231,9 +235,10 @@ public void testCallingWrongFormatterMethod() { notNullDescriptor, 42.00000d, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + true ); String expected = "${formatter.foo('%1$.2f', validatedValue)}"; String actual = interpolatorUnderTest.interpolate( @@ -307,8 +312,9 @@ private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDe descriptor, null, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + true ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java index 8635bc1eda..9f5d057534 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolatorContextTest.java @@ -7,35 +7,46 @@ package org.hibernate.validator.test.internal.engine.messageinterpolation; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; - -import java.util.Collections; -import java.util.Set; +import org.hibernate.validator.internal.engine.MessageInterpolatorContext; +import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; import javax.validation.Configuration; import javax.validation.ConstraintViolation; import javax.validation.MessageInterpolator; import javax.validation.MessageInterpolator.Context; +import javax.validation.Path; +import javax.validation.Valid; import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.constraints.Size; import javax.validation.metadata.BeanDescriptor; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.PropertyDescriptor; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.ResourceBundle; +import java.util.Set; -import org.hibernate.validator.internal.engine.MessageInterpolatorContext; -import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext; -import org.hibernate.validator.testutil.TestForIssue; -import org.hibernate.validator.testutils.ValidatorUtil; - -import org.testng.annotations.Test; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; /** * @author Hardy Ferentschik @@ -44,6 +55,16 @@ public class MessageInterpolatorContextTest { private static final String MESSAGE = "{foo}"; + Validator validator; + + @BeforeTest + public void setUp() { + validator = getConfiguration() + .messageInterpolator( new PathResourceBundleMessageInterpolator( new TestResourceBundleLocator() ) ) + .buildValidatorFactory() + .getValidator(); + } + @Test @TestForIssue(jiraKey = "HV-333") public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMessageInterpolator() { @@ -69,8 +90,10 @@ public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMess constraintDescriptors.iterator().next(), validatedValue, TestBean.class, + null, + Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyMap() + false ) ) ) @@ -88,13 +111,13 @@ public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMess @Test(expectedExceptions = ValidationException.class) public void testUnwrapToImplementationCausesValidationException() { - Context context = new MessageInterpolatorContext( null, null, null, Collections.emptyMap(), Collections.emptyMap() ); + Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), Collections.emptyMap(), false ); context.unwrap( MessageInterpolatorContext.class ); } @Test public void testUnwrapToInterfaceTypesSucceeds() { - Context context = new MessageInterpolatorContext( null, null, null, Collections.emptyMap(), Collections.emptyMap() ); + Context context = new MessageInterpolatorContext( null, null, null, null, Collections.emptyMap(), Collections.emptyMap(), false ); MessageInterpolator.Context asMessageInterpolatorContext = context.unwrap( MessageInterpolator.Context.class ); assertSame( asMessageInterpolatorContext, context ); @@ -115,13 +138,48 @@ public void testGetRootBeanType() { null, null, rootBeanType, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + false ); assertSame( context.unwrap( HibernateMessageInterpolatorContext.class ).getRootBeanType(), rootBeanType ); } + @Test + @TestForIssue(jiraKey = "HV-1657") + public void testGetPropertyPath() { + Path pathMock = createMock( Path.class ); + MessageInterpolator.Context context = new MessageInterpolatorContext( + null, + null, + null, + pathMock, + Collections.emptyMap(), + Collections.emptyMap(), + false ); + + assertSame( context.unwrap( HibernateMessageInterpolatorContext.class ).getPropertyPath(), pathMock ); + } + + @Test + @TestForIssue(jiraKey = "HV-1657") + public void testUsageOfPathInInterpolation() { + Employee employee = createEmployee( "farTooLongStreet", "workPlaza" ); + Set> constraintViolations = validator.validate( employee ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Size.class ) + .withMessage( "Employee Street should be smaller than 15" ) + ); + + employee = createEmployee( "mySquare", "farTooLongStreet" ); + constraintViolations = validator.validate( employee ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Size.class ) + .withMessage( "Company Street should be smaller than 15" ) + ); + } + private static class TestBean { @Size(min = 10, message = MESSAGE) private final String test; @@ -130,4 +188,144 @@ public TestBean(String test) { this.test = test; } } + + /** + * Interpolator demonstrator for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public class PathResourceBundleMessageInterpolator extends ResourceBundleMessageInterpolator { + + public PathResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) { + super( userResourceBundleLocator ); + } + + @Override + public String interpolate(String message, Context context) { + String newMessage = super.interpolate( message, context ); + newMessage = newMessage.replace( "#path#", "{" + pathToString( context ) + "}" ); + return super.interpolate( newMessage, context ); + } + + private String pathToString(Context context) { + HibernateMessageInterpolatorContext hContext = context.unwrap( HibernateMessageInterpolatorContext.class ); + StringBuilder baseNodeBuilder = new StringBuilder( hContext.getRootBeanType().getSimpleName() ); + for ( Path.Node node : hContext.getPropertyPath() ) { + if ( node.getName() != null ) { + baseNodeBuilder.append( "." ).append( node.getName() ); + } + } + return baseNodeBuilder.toString(); + } + + } + + /** + * creating a test employee with 2 properties of the same type (same annotation). + * + * @param employeeStreet + * @param employerStreet + * @return + */ + public static Employee createEmployee(String employeeStreet, String employerStreet) { + Employee employee = new Employee(); + employee.address = new Address(); + employee.address.street = employeeStreet; + employee.employer = new Employer(); + employee.employer.address = new Address(); + employee.employer.address.street = employerStreet; + return employee; + } + + /** + * Test bean for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public static class Address { + + @Size(max = 15) + private String street; + + } + + /** + * Test bean for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public static class Employee { + + @Valid + private Address address; + + @Valid + private Employer employer; + } + + /** + * Test bean for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + public static class Employer { + + @Valid + private Address address; + } + + /** + * A dummy locator for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + private static class TestResourceBundleLocator implements ResourceBundleLocator { + + private final ResourceBundle resourceBundle; + + public TestResourceBundleLocator() { + this( new TestResourceBundle() ); + } + + public TestResourceBundleLocator(ResourceBundle bundle) { + resourceBundle = bundle; + } + + @Override + public ResourceBundle getResourceBundle(Locale locale) { + return resourceBundle; + } + } + + /** + * A dummy resource bundle for {@link MessageInterpolatorContextTest#testUsageOfPathInInterpolation} + */ + private static class TestResourceBundle extends ResourceBundle implements Enumeration { + private final Map testResources; + Iterator iter; + + public TestResourceBundle() { + testResources = new HashMap(); + // add some test messages + testResources.put( "Employee.address.street", "Employee Street" ); + testResources.put( "Employee.employer.address.street", "Company Street" ); + testResources.put( "javax.validation.constraints.Size.message", "#path# should be smaller than {max}" ); + iter = testResources.keySet().iterator(); + } + + @Override + public Object handleGetObject(String key) { + return testResources.get( key ); + } + + @Override + public Enumeration getKeys() { + return this; + } + + @Override + public boolean hasMoreElements() { + return iter.hasNext(); + } + + @Override + public String nextElement() { + if ( hasMoreElements() ) { + return iter.next(); + } + else { + throw new NoSuchElementException(); + } + } + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java index 210fe9867b..9557f87236 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterMessageInterpolatorTest.java @@ -9,6 +9,7 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.assertTrue; import java.util.Set; @@ -16,11 +17,13 @@ import javax.validation.Validator; import javax.validation.constraints.Size; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.test.appender.ListAppender; import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; -import org.hibernate.validator.testutil.MessageLoggedAssertionLogger; import org.hibernate.validator.testutil.TestForIssue; - -import org.apache.log4j.Logger; +import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -34,14 +37,26 @@ public class ParameterMessageInterpolatorTest { Validator validator; + ListAppender listAppender; + @BeforeTest public void setUp() { + LoggerContext context = LoggerContext.getContext( false ); + Logger logger = context.getLogger( ParameterMessageInterpolator.class.getName() ); + listAppender = (ListAppender) logger.getAppenders().get( "List" ); + listAppender.clear(); + validator = getConfiguration() .messageInterpolator( new ParameterMessageInterpolator() ) .buildValidatorFactory() .getValidator(); } + @AfterTest + public void tearDown() { + listAppender.clear(); + } + @Test public void testParameterMessageInterpolatorInterpolatesParameters() { Foo foo = new Foo(); @@ -55,20 +70,17 @@ public void testParameterMessageInterpolatorInterpolatesParameters() { @Test public void testParameterMessageInterpolatorIgnoresELExpressions() { - Logger log4jRootLogger = Logger.getRootLogger(); - MessageLoggedAssertionLogger assertingLogger = new MessageLoggedAssertionLogger( "HV000185" ); - log4jRootLogger.addAppender( assertingLogger ); - Foo foo = new Foo(); Set> constraintViolations = validator.validateProperty( foo, "bar" ); assertThat( constraintViolations ).containsOnlyViolations( violationOf( Size.class ) .withProperty( "bar" ) - .withMessage( "${validatedValue}" ) - ); + .withMessage( "${validatedValue}" ) ); - assertingLogger.assertMessageLogged(); - log4jRootLogger.removeAppender( assertingLogger ); + assertTrue( listAppender.getEvents().stream() + .filter( event -> event.getLevel().equals( Level.WARN ) ) + .map( event -> event.getMessage().getFormattedMessage() ) + .anyMatch( m -> m.startsWith( "HV000185" ) ) ); } public static class Foo { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterTermResolverTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterTermResolverTest.java new file mode 100644 index 0000000000..8f6feb5f42 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ParameterTermResolverTest.java @@ -0,0 +1,160 @@ +/* + * 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.messageinterpolation; + +import static org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import javax.validation.MessageInterpolator; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver; +import org.hibernate.validator.messageinterpolation.HibernateMessageInterpolatorContext; +import org.hibernate.validator.testutil.TestForIssue; + +import org.easymock.EasyMock; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test for {@link org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver} + * + * @author Alexander Gatsenko + */ +public class ParameterTermResolverTest { + + private final ParameterTermResolver resolver = new ParameterTermResolver(); + + @DataProvider(name = "interpolateByNotArrayValueArgs") + public static Object[][] interpolateByNotArrayValueArgs() { + // lines of (String variableName, Object variableValue, String expectedResolvedExpression) + return new Object[][] { + { "value", null, "{value}" }, + { "value", true, "true" }, + { "value", false, "false" }, + { "value", 'a', "a" }, + { "value", (byte) 10, "10" }, + { "value", (short) 10, "10" }, + { "value", 10, "10" }, + { "value", 10L, "10" }, + { "value", 10.1, "10.1" }, + { "value", 10.1f, "10.1" }, + { "value", "string value", "string value" }, + }; + } + + @DataProvider(name = "interpolateByArrayValueArgs") + public static Object[][] interpolateByArrayValueArgs() { + // lines of (String variableName, variableValueArray, String expectedResolvedExpression) + return new Object[][] { + { "value", new boolean[] { true, false }, Arrays.toString( new boolean[] { true, false } ) }, + { "value", new char[] { 'a', 'b' }, Arrays.toString( new char[] { 'a', 'b' } ) }, + { "value", new byte[] { 1, 2 }, Arrays.toString( new byte[] { 1, 2 } ) }, + { "value", new short[] { 1, 2 }, Arrays.toString( new short[] { 1, 2 } ) }, + { "value", new int[] { 1, 2 }, Arrays.toString( new int[] { 1, 2 } ) }, + { "value", new long[] { 1, 2 }, Arrays.toString( new long[] { 1, 2 } ) }, + { "value", new double[] { 1.2, 3.4 }, Arrays.toString( new double[] { 1.2, 3.4 } ) }, + { "value", new float[] { 1.2F, 3.4F }, Arrays.toString( new float[] { 1.2F, 3.4F } ) }, + { "value", new String[] { "one", "two" }, Arrays.toString( new String[] { "one", "two" } ) }, + }; + } + + @Test(dataProvider = "interpolateByNotArrayValueArgs") + public void testInterpolateShouldResolveExpressionByNotArrayValue( + String variableName, + Object variableValue, + String expectedResolvedExpression) { + final MessageInterpolator.Context context = createHibernateContextWithConstraintDescriptorAttr( + variableName, + variableValue + ); + final String srcExpression = createVariableExpression( variableName ); + + final String actualResolvedExpression = resolver.interpolate( context, srcExpression ); + assertEquals( actualResolvedExpression, expectedResolvedExpression ); + } + + @Test(dataProvider = "interpolateByArrayValueArgs") + @TestForIssue(jiraKey = "HV-1761") + public void testInterpolateShouldResolveExpressionByArrayValue( + String variableName, + Object variableValueArray, + String expectedResolvedExpression) { + final MessageInterpolator.Context context = createHibernateContextWithConstraintDescriptorAttr( + variableName, + variableValueArray + ); + final String srcExpression = createVariableExpression( variableName ); + + final String actualResolvedExpression = resolver.interpolate( context, srcExpression ); + assertEquals( actualResolvedExpression, expectedResolvedExpression ); + } + + @Test(dataProvider = "interpolateByNotArrayValueArgs") + public void testInterpolateShouldAllowUseNotHibernateContext( + String variableName, + Object variableValue, + String expectedResolvedExpression) { + final MessageInterpolator.Context context = createNotHibernateContextWithConstraintDescriptorAttr( + variableName, + variableValue + ); + final String srcExpression = createVariableExpression( variableName ); + + final String actualResolvedExpression = resolver.interpolate( context, srcExpression ); + assertEquals( actualResolvedExpression, expectedResolvedExpression ); + } + + private static String createVariableExpression(String variableName) { + return String.format( "{%s}", variableName ); + } + + private static Map createConstraintDescriptorAttr(String attrName, Object attrValue) { + Map map = new HashMap<>( 1 ); + map.put( attrName, attrValue ); + return map; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static MessageInterpolator.Context createNotHibernateContextWithConstraintDescriptorAttr( + String attrName, + Object attrValue) { + final Map attrs = createConstraintDescriptorAttr( attrName, attrValue ); + + final MessageInterpolator.Context context = EasyMock.mock( MessageInterpolator.Context.class ); + final ConstraintDescriptor constraintDescriptor = EasyMock.mock( ConstraintDescriptor.class ); + + EasyMock.expect( context.getConstraintDescriptor() ).andStubReturn( constraintDescriptor ); + EasyMock.expect( constraintDescriptor.getAttributes() ).andStubReturn( attrs ); + + EasyMock.replay( context ); + EasyMock.replay( constraintDescriptor ); + + return context; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static MessageInterpolator.Context createHibernateContextWithConstraintDescriptorAttr( + String attrName, + Object attrValue) { + final Map attrs = createConstraintDescriptorAttr( attrName, attrValue ); + + final HibernateMessageInterpolatorContext context = EasyMock.mock( HibernateMessageInterpolatorContext.class ); + final ConstraintDescriptor constraintDescriptor = EasyMock.mock( ConstraintDescriptor.class ); + + EasyMock.expect( context.getMessageParameters() ).andStubReturn( attrs ); + EasyMock.expect( context.getConstraintDescriptor() ).andStubReturn( constraintDescriptor ); + EasyMock.expect( constraintDescriptor.getAttributes() ).andStubReturn( attrs ); + + EasyMock.replay( context ); + EasyMock.replay( constraintDescriptor ); + + return context; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java index 889120f615..5942f8e9cd 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java @@ -278,9 +278,10 @@ private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDe descriptor, null, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + true ); } private void runInterpolation(boolean cachingEnabled) { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageExpressionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageExpressionTest.java new file mode 100644 index 0000000000..229e34174e --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageExpressionTest.java @@ -0,0 +1,110 @@ +/* + * 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.messageinterpolation; + +import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.Token; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ListAssert; +import org.testng.annotations.Test; + +/** + * Tests for {@code TokenCollector} in message expression mode. + * + * @author Hardy Ferentschik + */ +public class TokenCollectorMessageExpressionTest extends AbstractTokenCollectorTest { + @Override + protected InterpolationTermType getInterpolationTermType() { + return InterpolationTermType.EL; + } + + // Several tests inherited from the abstract class + + @Test + public void testMessageParameter() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo {bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "foo ", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{bar}", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testMessageExpression() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo ${bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "foo ", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "${bar}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenParameterInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$${1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$$", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenLiteralsInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$$foo", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$$", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "foo", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testDollarThenClosingBraceThrowsException() { + new TokenCollector( "$}", getInterpolationTermType() ); + } + + @Test + public void testDollarThenEscapeInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$\\A{1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$\\A", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageParameterTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageParameterTest.java new file mode 100644 index 0000000000..9189f496b3 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorMessageParameterTest.java @@ -0,0 +1,115 @@ +/* + * 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.messageinterpolation; + +import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.Token; +import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ListAssert; +import org.testng.annotations.Test; + +/** + * Tests for {@code TokenCollector} in message parameter mode. + * + * @author Hardy Ferentschik + */ +public class TokenCollectorMessageParameterTest extends AbstractTokenCollectorTest { + @Override + protected InterpolationTermType getInterpolationTermType() { + return InterpolationTermType.PARAMETER; + } + + // Several tests inherited from the abstract class + + @Test + public void testMessageParameter() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo {bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "foo ", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{bar}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testMessageExpression() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "foo ${bar}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + /* + * 6.3.1.1: + * Parameter interpolation has precedence over message expressions. + * For example for the message descriptor ${value}, + * trying to evaluate {value} as message parameter has precedence + * over evaluating ${value} as message expression. + */ + assertion.element( 0 ) + .returns( "foo $", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{bar}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenParameterInterpretedAsLiteralAndParameter() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$${1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$$", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } + + @Test + public void testDollarThenDollarThenLiteralsInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$$foo", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 1 ); + assertion.element( 0 ) + .returns( "$$foo", Token::getTokenValue ) + .returns( false, Token::isParameter ); + } + + @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") + public void testDollarThenClosingBraceThrowsException() { + // Fails because of the dangling closing brace; the dollar sign is irrelevant + new TokenCollector( "$}", getInterpolationTermType() ); + } + + @Test + public void testDollarThenEscapeInterpretedAsLiterals() { + ListAssert assertion = Assertions.assertThat( + new TokenCollector( "$\\A{1+1}", getInterpolationTermType() ) + .getTokenList() + ) + .hasSize( 2 ); + assertion.element( 0 ) + .returns( "$\\A", Token::getTokenValue ) + .returns( false, Token::isParameter ); + assertion.element( 1 ) + .returns( "{1+1}", Token::getTokenValue ) + .returns( true, Token::isParameter ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorTest.java deleted file mode 100644 index 972a9e0513..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/TokenCollectorTest.java +++ /dev/null @@ -1,45 +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.internal.engine.messageinterpolation; - -import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType; -import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException; -import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector; -import org.testng.annotations.Test; - -/** - * Tests for {@code TokenCollector}. - * - * @author Hardy Ferentschik - */ -public class TokenCollectorTest { - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000169.*") - public void testNestedParametersThrowException() throws Exception { - new TokenCollector( "#{foo {}", InterpolationTermType.PARAMETER ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testParameterWithoutOpeningBraceThrowsException() throws Exception { - new TokenCollector( "foo}", InterpolationTermType.PARAMETER ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testELExpressionWithoutOpeningBraceThrowsException() throws Exception { - new TokenCollector( "$}", InterpolationTermType.EL ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testTermWithoutClosingBraceThrowsException() throws Exception { - new TokenCollector( "{foo", InterpolationTermType.PARAMETER ); - } - - @Test(expectedExceptions = MessageDescriptorFormatException.class, expectedExceptionsMessageRegExp = "HV000168.*") - public void testSingleClosingBraceThrowsException() throws Exception { - new TokenCollector( "this message contains a invalid parameter start token {", InterpolationTermType.EL ); - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java index 79927132d4..3e42d87920 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AbstractMethodValidationTest.java @@ -52,7 +52,8 @@ */ @Test public abstract class AbstractMethodValidationTest { - protected CustomerRepository customerRepository; + protected CustomerRepository customerRepositoryOriginalBean; + protected CustomerRepository customerRepositoryValidatingProxy; protected RepositoryBase repositoryBase; protected Validator validator; @@ -61,16 +62,17 @@ public abstract class AbstractMethodValidationTest { protected abstract String messagePrefix(); protected void createProxy(Class... groups) { - customerRepository = getValidatingProxy( - new CustomerRepositoryImpl(), validator, groups + customerRepositoryOriginalBean = new CustomerRepositoryImpl(); + customerRepositoryValidatingProxy = getValidatingProxy( + customerRepositoryOriginalBean, validator, groups ); - repositoryBase = customerRepository; + repositoryBase = customerRepositoryValidatingProxy; } @Test public void methodValidationYieldsConstraintViolation() { try { - customerRepository.findCustomerByName( null ); + customerRepositoryValidatingProxy.findCustomerByName( null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -92,13 +94,13 @@ public void methodValidationYieldsConstraintViolation() { assertMethod( constraintViolation, "findCustomerByName", String.class ); assertParameterIndex( constraintViolation, 0 ); assertMethodValidationType( constraintViolation, ElementKind.PARAMETER ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), "findCustomerByName.name" ); - assertEquals( constraintViolation.getLeafBean(), customerRepository ); + assertEquals( constraintViolation.getLeafBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { null } ); assertEquals( constraintViolation.getExecutableReturnValue(), null ); @@ -108,7 +110,7 @@ public void methodValidationYieldsConstraintViolation() { @Test public void validationOfMethodWithMultipleParameters() { try { - customerRepository.findCustomerByAgeAndName( 30, null ); + customerRepositoryValidatingProxy.findCustomerByAgeAndName( 30, null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -141,7 +143,7 @@ public void validationOfMethodWithMultipleParameters() { @Test public void constraintViolationsAtMultipleParameters() { try { - customerRepository.findCustomerByAgeAndName( 1, null ); + customerRepositoryValidatingProxy.findCustomerByAgeAndName( 1, null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -170,7 +172,7 @@ public void constraintViolationsAtMultipleParameters() { public void methodValidationWithCascadingParameter() { Customer customer = new Customer( null, null ); try { - customerRepository.persistCustomer( customer ); + customerRepositoryValidatingProxy.persistCustomer( customer ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -195,7 +197,7 @@ public void methodValidationWithCascadingParameter() { constraintViolation.getPropertyPath().toString(), "persistCustomer.customer.name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customer } ); @@ -209,7 +211,7 @@ public void methodValidationWithCascadingParameterAndCascadingConstraint() { Customer customer = new Customer( "Bob", address ); try { - customerRepository.persistCustomer( customer ); + customerRepositoryValidatingProxy.persistCustomer( customer ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -236,7 +238,7 @@ public void methodValidationWithCascadingParameterAndCascadingConstraint() { "persistCustomer.customer.address.city" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), address ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customer } ); @@ -251,7 +253,7 @@ public void cascadingMapParameter() { customers.put( "Bob", bob ); try { - customerRepository.cascadingMapParameter( customers ); + customerRepositoryValidatingProxy.cascadingMapParameter( customers ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -277,7 +279,7 @@ public void cascadingMapParameter() { "cascadingMapParameter.customer[Bob].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), bob ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customers } ); @@ -291,7 +293,7 @@ public void cascadingIterableParameter() { List customers = Arrays.asList( null, customer ); try { - customerRepository.cascadingIterableParameter( customers ); + customerRepositoryValidatingProxy.cascadingIterableParameter( customers ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -317,7 +319,7 @@ public void cascadingIterableParameter() { "cascadingIterableParameter.customer[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customers } ); @@ -331,7 +333,7 @@ public void cascadingArrayParameter() { Customer customer = new Customer( null ); try { - customerRepository.cascadingArrayParameter( null, customer ); + customerRepositoryValidatingProxy.cascadingArrayParameter( null, customer ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -357,7 +359,7 @@ public void cascadingArrayParameter() { "cascadingArrayParameter.customer[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( @@ -371,7 +373,7 @@ public void cascadingArrayParameter() { @Test public void constraintsAtMethodFromBaseClassAreEvaluated() { try { - customerRepository.findById( null ); + customerRepositoryValidatingProxy.findById( null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -398,7 +400,7 @@ public void constraintsAtMethodFromBaseClassAreEvaluated() { @Test public void constraintsAtOverriddenMethodAreEvaluated() { try { - customerRepository.foo( null ); + customerRepositoryValidatingProxy.foo( null ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -425,7 +427,7 @@ public void constraintsAtOverriddenMethodAreEvaluated() { @Test public void validFromOverriddenMethodIsEvaluated() { try { - customerRepository.bar( new Customer( null, null ) ); + customerRepositoryValidatingProxy.bar( new Customer( null, null ) ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -453,13 +455,13 @@ public void validFromOverriddenMethodIsEvaluated() { @Test public void parameterValidationOfParameterlessMethod() { - customerRepository.boz(); + customerRepositoryValidatingProxy.boz(); } @Test public void returnValueValidationYieldsConstraintViolation() { try { - customerRepository.baz(); + customerRepositoryValidatingProxy.baz(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -478,10 +480,10 @@ public void returnValueValidationYieldsConstraintViolation() { assertEquals( constraintViolation.getMessage(), messagePrefix() + "must be greater than or equal to 10" ); assertMethod( constraintViolation, "baz" ); assertMethodValidationType( constraintViolation, ElementKind.RETURN_VALUE ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), "baz." ); - assertEquals( constraintViolation.getLeafBean(), customerRepository ); + assertEquals( constraintViolation.getLeafBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getInvalidValue(), 9 ); assertEquals( constraintViolation.getExecutableParameters(), null ); assertEquals( constraintViolation.getExecutableReturnValue(), 9 ); @@ -491,7 +493,7 @@ public void returnValueValidationYieldsConstraintViolation() { @Test public void cascadingReturnValue() { try { - customerRepository.cascadingReturnValue(); + customerRepositoryValidatingProxy.cascadingReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -510,7 +512,7 @@ public void cascadingReturnValue() { assertEquals( constraintViolation.getMessage(), messagePrefix() + "must not be null" ); assertMethod( constraintViolation, "cascadingReturnValue" ); assertMethodValidationType( constraintViolation, ElementKind.RETURN_VALUE ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), @@ -526,7 +528,7 @@ public void cascadingReturnValue() { @Test public void cascadingReturnValueFromSuperType() { try { - customerRepository.overriddenMethodWithCascadingReturnValue(); + customerRepositoryValidatingProxy.overriddenMethodWithCascadingReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -546,7 +548,7 @@ public void cascadingReturnValueFromSuperType() { assertMethod( constraintViolation, "overriddenMethodWithCascadingReturnValue" ); assertMethodValidationType( constraintViolation, ElementKind.RETURN_VALUE ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getPropertyPath().toString(), @@ -562,7 +564,7 @@ public void cascadingReturnValueFromSuperType() { @Test public void cascadingIterableReturnValue() { try { - customerRepository.cascadingIterableReturnValue(); + customerRepositoryValidatingProxy.cascadingIterableReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -587,7 +589,7 @@ public void cascadingIterableReturnValue() { "cascadingIterableReturnValue.[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), new Customer( null ) ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), null ); @@ -598,7 +600,7 @@ public void cascadingIterableReturnValue() { @Test public void cascadingMapReturnValue() { try { - customerRepository.cascadingMapReturnValue(); + customerRepositoryValidatingProxy.cascadingMapReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -627,7 +629,7 @@ public void cascadingMapReturnValue() { "cascadingMapReturnValue.[Bob].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), null ); @@ -638,7 +640,7 @@ public void cascadingMapReturnValue() { @Test public void cascadingArrayReturnValue() { try { - customerRepository.cascadingArrayReturnValue(); + customerRepositoryValidatingProxy.cascadingArrayReturnValue(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -663,7 +665,7 @@ public void cascadingArrayReturnValue() { "cascadingArrayReturnValue.[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), new Customer( null ) ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), null ); @@ -674,7 +676,7 @@ public void cascadingArrayReturnValue() { @Test public void overridingMethodStrengthensReturnValueConstraint() { try { - customerRepository.overriddenMethodWithReturnValueConstraint(); + customerRepositoryValidatingProxy.overriddenMethodWithReturnValueConstraint(); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -729,13 +731,13 @@ public void runtimeTypeDefinesConstraintsToApply() { @Test public void methodValidationSucceedsAsNoConstraintOfValidatedGroupAreViolated() { - customerRepository.parameterConstraintInGroup( null ); + customerRepositoryValidatingProxy.parameterConstraintInGroup( null ); } @Test(expectedExceptions = ConstraintViolationException.class) public void methodValidationFailsAsConstraintOfValidatedGroupIsViolated() { createProxy( CustomerRepository.ValidationGroup.class ); - customerRepository.parameterConstraintInGroup( null ); + customerRepositoryValidatingProxy.parameterConstraintInGroup( null ); } @Test(expectedExceptions = ConstraintDeclarationException.class, expectedExceptionsMessageRegExp = "HV000132.*") @@ -750,7 +752,7 @@ public void voidMethodWithReturnValueConstraintCausesConstraintDeclarationExcept @TestForIssue(jiraKey = "HV-601") @Test(expectedExceptions = ConstraintViolationException.class) public void shouldValidateGetterLikeNamedMethodWithParameter() { - customerRepository.getFoo( "" ); + customerRepositoryValidatingProxy.getFoo( "" ); } @Test @@ -761,7 +763,7 @@ public void validationOfCrossParameterConstraint() { try { //when - customerRepository.methodWithCrossParameterConstraint( startDate, endDate ); + customerRepositoryValidatingProxy.methodWithCrossParameterConstraint( startDate, endDate ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -778,8 +780,8 @@ public void validationOfCrossParameterConstraint() { ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next(); assertEquals( constraintViolation.getConstraintDescriptor().getAnnotation().annotationType(), ConsistentDateParameters.class ); assertEquals( constraintViolation.getInvalidValue(), new Object[] { startDate, endDate } ); - assertEquals( constraintViolation.getLeafBean(), customerRepository ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getLeafBean(), customerRepositoryOriginalBean ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { startDate, endDate } ); assertEquals( constraintViolation.getExecutableReturnValue(), null ); @@ -795,7 +797,7 @@ public void validationOfCrossParameterConstraint() { @Test public void methodValidationSucceeds() { - customerRepository.findCustomerByName( "Bob" ); + customerRepositoryValidatingProxy.findCustomerByName( "Bob" ); } protected void assertMethod(ConstraintViolation constraintViolation, String methodName, Class... parameterTypes) { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java index 4e4575e3d1..a700db52d6 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/AnnotationBasedMethodValidationTest.java @@ -46,7 +46,7 @@ public void iterableParameterWithCascadingTypeParameter() { List customers = Arrays.asList( null, customer ); try { - customerRepository.iterableParameterWithCascadingTypeParameter( customers ); + customerRepositoryValidatingProxy.iterableParameterWithCascadingTypeParameter( customers ); fail( "Expected ConstraintViolationException wasn't thrown." ); } catch (ConstraintViolationException e) { @@ -62,7 +62,7 @@ public void iterableParameterWithCascadingTypeParameter() { "iterableParameterWithCascadingTypeParameter.customer[1].name" ); assertEquals( constraintViolation.getRootBeanClass(), CustomerRepositoryImpl.class ); - assertEquals( constraintViolation.getRootBean(), customerRepository ); + assertEquals( constraintViolation.getRootBean(), customerRepositoryOriginalBean ); assertEquals( constraintViolation.getLeafBean(), customer ); assertEquals( constraintViolation.getInvalidValue(), null ); assertEquals( constraintViolation.getExecutableParameters(), new Object[] { customers } ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java index 837a37d379..c23db0079e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraint.java @@ -6,24 +6,25 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.crossparameter; +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.TYPE; + import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; 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.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; - /** * @author Hardy Ferentschik */ @Target({ TYPE, FIELD, METHOD, ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = { CrossParameterValidator1.class }) +@Constraint(validatedBy = { DodgyConstraintValidator.class }) @Documented public @interface DodgyConstraint { String message() default "{ConsistentDateParameters.message}"; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraintValidator.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraintValidator.java new file mode 100644 index 0000000000..a1d3cb4edd --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/crossparameter/DodgyConstraintValidator.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.test.internal.engine.methodvalidation.crossparameter; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +/** + * @author Hardy Ferentschik + */ +@SupportedValidationTarget( value = ValidationTarget.PARAMETERS) +public class DodgyConstraintValidator implements ConstraintValidator { + + @Override + public boolean isValid(Object[] value, ConstraintValidatorContext context) { + return false; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java index 1153454aa4..86f3217fc8 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java @@ -33,6 +33,7 @@ import org.hibernate.validator.internal.engine.path.PathImpl; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; @@ -40,7 +41,6 @@ import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.testutils.ValidatorUtil; - import org.testng.annotations.Test; /** @@ -65,7 +65,7 @@ public void testParsing() { elem = propIter.next(); assertEquals( elem.getName(), "deliveryAddress" ); assertTrue( elem.isInIterable() ); - assertEquals( elem.getIndex(), new Integer( 3 ) ); + assertEquals( elem.getIndex(), Integer.valueOf( 3 ) ); assertTrue( propIter.hasNext() ); elem = propIter.next(); @@ -76,7 +76,7 @@ public void testParsing() { elem = propIter.next(); assertEquals( elem.getName(), null ); assertTrue( elem.isInIterable() ); - assertEquals( elem.getIndex(), new Integer( 1 ) ); + assertEquals( elem.getIndex(), Integer.valueOf( 1 ) ); assertFalse( propIter.hasNext() ); @@ -197,6 +197,7 @@ public void testCreationOfExecutablePath() throws Exception { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java index 200d5836e3..12871b2080 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/traversableresolver/JpaTraversableResolverTest.java @@ -14,7 +14,7 @@ import javax.validation.ConstraintViolation; import javax.validation.Validator; -import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.hibernate.validator.internal.engine.resolver.JPATraversableResolver; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ValidatorUtil; import org.testng.annotations.BeforeTest; @@ -32,7 +32,7 @@ public class JpaTraversableResolverTest { @BeforeTest public void setUp() { Configuration configuration = ValidatorUtil.getConfiguration(); - configuration.traversableResolver( TraversableResolvers.getDefault() ); + configuration.traversableResolver( new JPATraversableResolver() ); validator = configuration.buildValidatorFactory().getValidator(); } @@ -55,5 +55,3 @@ public void testWithoutBooks() { assertTrue( results.isEmpty() ); } } - - diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/ContainerWithWildcardTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/ContainerWithWildcardTest.java new file mode 100644 index 0000000000..e33456e2e8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/ContainerWithWildcardTest.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.test.internal.engine.valueextraction; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +@TestForIssue(jiraKey = "HV-1720") +public class ContainerWithWildcardTest { + + @Test + public void containerWithUpperBoundWildcard() { + Set> constraintViolations = getValidator().validate( new Foo( Arrays.asList( null, null ) ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( NotNull.class ), + violationOf( NotNull.class ) + ); + } + + @Test + public void containerWithWildcard() { + Set> constraintViolations = getValidator().validate( new Bar( Arrays.asList( null, null ) ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( NotNull.class ), + violationOf( NotNull.class ) + ); + } + + @Test + public void containerWithLowerBoundWildcard() { + Set> constraintViolations = getValidator().validate( new FooBar( Arrays.asList( null, null ) ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( NotNull.class ), + violationOf( NotNull.class ) + ); + } + + private static class Foo { + private final List<@NotNull ? extends BigDecimal> list; + + private Foo(List list) { + this.list = list; + } + } + + private static class Bar { + private final List<@NotNull ?> list; + + private Bar(List list) { + this.list = list; + } + } + + private static class FooBar { + private final List<@NotNull ? super BigDecimal> list; + + private FooBar(List list) { + this.list = list; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java index 4aef341306..d9e95139cb 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/JavaFXClassLoadingTest.java @@ -27,9 +27,9 @@ public class JavaFXClassLoadingTest { /** - * This class will be present in the TCCL because is part of JDK 8 + * This class will be present in the TCCL because it is either part of the JDK (JDK 10-) or in the classpath (JDK 11+). */ - private static final String JAVAFX_APPLICATION_CLASS = "javafx.application.Application"; + private static final String JAVAFX_APPLICATION_CLASS = "javafx.beans.value.ObservableValue"; @Test public void shouldBeAbleToFindTheClassInTCCL() throws Exception { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java index 9b402186b8..8df70c9eff 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java @@ -25,6 +25,7 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -56,6 +57,7 @@ public void setUpBeanMetaDataManager() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java index e1a80396f4..e3a32774df 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java @@ -26,6 +26,7 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -61,6 +62,7 @@ public void setupBeanMetaData() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java index bccf0c208b..b18cdec1b0 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java @@ -28,6 +28,7 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; import org.hibernate.validator.internal.metadata.aggregated.ParameterMetaData; @@ -61,6 +62,7 @@ public void setupBeanMetaData() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() @@ -131,6 +133,7 @@ public void parameterNameInInheritanceHierarchy() throws Exception { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new SkewedParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java index fe2577ea2e..a595ed9c69 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java @@ -21,6 +21,7 @@ import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; +import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; @@ -45,6 +46,7 @@ public void setupBeanMetaDataManager() { new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), new ValueExtractorManager( Collections.emptySet() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() 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 a37d99ef0b..efe128ad6a 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 @@ -858,7 +858,7 @@ public void isArrayWithParameterizedType() { @Test public void testTypeDiscovery() { List> validatorDescriptors = new ArrayList<>(); - validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( PositiveConstraintValidator.class ) ); + validatorDescriptors.add( ConstraintValidatorDescriptor.forClass( PositiveConstraintValidator.class, Positive.class ) ); Map> validatorsTypes = TypeHelper .getValidatorTypes( Positive.class, validatorDescriptors ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/Child.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Child.java new file mode 100644 index 0000000000..9d12cbfd83 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Child.java @@ -0,0 +1,19 @@ +/* + * 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.xml; + +/** + * Test class for HV-1534. + * + * @author Rob Dickinson + */ +public class Child extends Parent { + + public Child( String parentAttribute ) { + super( parentAttribute ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java index 517980d41d..0ef2a724f0 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/MappingXmlParserTest.java @@ -26,7 +26,7 @@ import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.util.TypeResolutionHelper; -import org.hibernate.validator.internal.xml.MappingXmlParser; +import org.hibernate.validator.internal.xml.mapping.MappingXmlParser; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/Parent.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Parent.java new file mode 100644 index 0000000000..6b662e1286 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/Parent.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.xml; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test class for HV-1534. + * + * @author Rob Dickinson + */ +public class Parent { + + private String parentAttribute = null; + private List parentListAttribute = new ArrayList<>(); + + public Parent( String parentAttribute ) { + this.parentAttribute = parentAttribute; + } + + public final String getParentAttribute() { + return parentAttribute; + } + + public void addToListAttribute(String element) { + parentListAttribute.add( element ); + } + + public final List getParentListAttribute() { + return parentListAttribute; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java index f62ee1f70b..f70307f3b5 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingTest.java @@ -9,6 +9,7 @@ import static org.hibernate.validator.internal.util.CollectionHelper.asSet; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.testng.Assert.assertEquals; @@ -376,4 +377,59 @@ public void testCascadedValidation() { violationOf( NotNull.class ) ); } + + @Test + @TestForIssue(jiraKey = "HV-1534") + public void test_constraint_is_applied_to_inherited_getter() { + final Configuration configuration = ValidatorUtil.getConfiguration(); + configuration.addMapping( XmlMappingTest.class.getResourceAsStream( "hv-1534-mapping.xml" ) ); + + final ValidatorFactory validatorFactory = configuration.buildValidatorFactory(); + final Validator validator = validatorFactory.getValidator(); + + Parent parent = new Parent( null ); + + Set> parentViolations = validator.validate( parent ); + + assertNoViolations( parentViolations ); + + Child child = new Child( null ); + + Set> childViolations = validator.validate( child ); + + assertThat( childViolations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "parentAttribute" ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-1534") + public void test_constraint_is_applied_to_type_argument_of_inherited_getter() { + final Configuration configuration = ValidatorUtil.getConfiguration(); + configuration.addMapping( XmlMappingTest.class.getResourceAsStream( "hv-1534-mapping.xml" ) ); + + final ValidatorFactory validatorFactory = configuration.buildValidatorFactory(); + final Validator validator = validatorFactory.getValidator(); + + Parent parent = new Parent( "someValue" ); + parent.addToListAttribute( null ); + + Set> parentViolations = validator.validate( parent ); + + assertNoViolations( parentViolations ); + + Child child = new Child( "someValue" ); + child.addToListAttribute( null ); + + Set> childViolations = validator.validate( child ); + + assertThat( childViolations ).containsOnlyViolations( + violationOf( NotNull.class ) + .withPropertyPath( + pathWith() + .property( "parentListAttribute" ) + .containerElement( "", true, null, 0, List.class, 0 ) ) + ); + } + } diff --git a/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java b/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java index 529e28ae75..fcdea96823 100644 --- a/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java +++ b/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java @@ -235,6 +235,6 @@ public static T getValidatingProxy(I implementor, Validator exe } public static HibernateConstraintValidatorContext getConstraintValidatorContext() { - return new ConstraintValidatorContextImpl( null, DefaultClockProvider.INSTANCE, null, null, null ); + return new ConstraintValidatorContextImpl( null, DefaultClockProvider.INSTANCE, null, null, null, true ); } } diff --git a/engine/src/test/resources/log4j.properties b/engine/src/test/resources/log4j.properties deleted file mode 100644 index 6e7112b594..0000000000 --- a/engine/src/test/resources/log4j.properties +++ /dev/null @@ -1,26 +0,0 @@ -### 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 - -### direct messages to file hibernate.log ### -log4j.appender.file=org.apache.log4j.FileAppender -log4j.appender.file.File=hibernate.log -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -### direct messages to socket - chainsaw ### -log4j.appender.socket=org.apache.log4j.net.SocketAppender -log4j.appender.socket.remoteHost=localhost -log4j.appender.socket.port=4560 -log4j.appender.socket.locationInfo=true - - -### set log levels - for more verbose logging change 'info' to 'debug' ### -log4j.rootLogger=debug, stdout - -log4j.logger.org.hibernate.validator.internal.engine.ValidatorImpl=trace -#log4j.logger.org.hibernate.validator.internal.engine.resolver.JPATraversableResolver=trace -#log4j.logger.org.hibernate.validatorengine.ConstraintTree=trace -log4j.logger.org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator=info diff --git a/engine/src/test/resources/log4j2.properties b/engine/src/test/resources/log4j2.properties new file mode 100644 index 0000000000..3a80eeb2c1 --- /dev/null +++ b/engine/src/test/resources/log4j2.properties @@ -0,0 +1,22 @@ +# License: Apache License, Version 2.0 +# See the LICENSE file in the root directory or . + +status = error + +# Direct log messages to stdout +appender.console.type = Console +appender.console.name = console +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n + +appender.list.type = List +appender.list.name = List + +# Root logger option +rootLogger.level = info +rootLogger.appenderRef.console.ref = console + +# Specific loggers options +logger.parametermessageinterpolator.name = org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator +logger.parametermessageinterpolator.level = info +logger.parametermessageinterpolator.appenderRef.list.ref = List \ No newline at end of file diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml index eb78ab7370..96016dd950 100644 --- a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-1589-mapping.xml @@ -18,7 +18,7 @@ 5 - 10 + 10 diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml index eb78ab7370..96016dd950 100644 --- a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/constraintvalidation/hv-662-mapping.xml @@ -18,7 +18,7 @@ 5 - 10 + 10 diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/xml/hv-1534-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/xml/hv-1534-mapping.xml new file mode 100644 index 0000000000..58fac1a395 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/xml/hv-1534-mapping.xml @@ -0,0 +1,28 @@ + + + + + org.hibernate.validator.internal.xml + + + + + + + + + + + + + diff --git a/integration/pom.xml b/integration/pom.xml index adb617610a..b8c34143c3 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -42,11 +42,6 @@ assertj-core test - - log4j - log4j - test - ${project.groupId} hibernate-validator-test-utils @@ -93,8 +88,8 @@ test - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + javax.persistence + javax.persistence-api test @@ -148,6 +143,12 @@ true + + maven-gpg-plugin + + true + + org.apache.maven.plugins maven-failsafe-plugin @@ -157,6 +158,7 @@ wildfly-current-integration-test integration-test + verify @@ -165,38 +167,13 @@ target/failsafe-reports/failsafe-summary-wildfly-current.xml - - - wildfly-secondary-integration-test - - integration-test - - - - wildfly-secondary - - target/failsafe-reports/failsafe-summary-wildfly-secondary.xml - - - - verify - - verify - - - - target/failsafe-reports/failsafe-summary-wildfly-current.xml - target/failsafe-reports/failsafe-summary-wildfly-secondary.xml - - - maven-dependency-plugin - unpack-wildfly + unpack-wildfly-current pre-integration-test unpack @@ -212,20 +189,11 @@ false ${project.build.directory} - - - org.wildfly - wildfly-dist - ${wildfly-secondary.version} - tar.gz - false - ${project.build.directory} - - copy-patch + copy-patch-current pre-integration-test copy @@ -241,20 +209,11 @@ zip ${project.build.directory} - - - ${project.groupId} - hibernate-validator-modules - ${project.version} - wildfly-${wildfly-secondary.version}-patch - zip - ${project.build.directory} - - copy-javamoney + copy-javamoney-current pre-integration-test copy @@ -274,19 +233,6 @@ ${moneta.version} ${wildfly.modules-dir}/org/javamoney/moneta/main/ - - - javax.money - money-api - ${javax-money.version} - ${wildfly-secondary.modules-dir}/javax/money/api/main/ - - - org.javamoney - moneta - ${moneta.version} - ${wildfly-secondary.modules-dir}/org/javamoney/moneta/main/ - @@ -312,23 +258,6 @@ - - - copy-wildfly-secondary-resources - pre-integration-test - - copy-resources - - - ${wildfly-secondary.modules-dir} - - - src/test/modules - true - - - - @@ -351,22 +280,6 @@ - - - apply-wildfly-secondary-patch-file - pre-integration-test - - execute-commands - - - true - ${project.build.directory}/wildfly-${wildfly-secondary.version}/ - false - - patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly-secondary.version}-patch.zip - - - @@ -382,5 +295,149 @@ --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED + + + jdk10- + + (,11) + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + wildfly-secondary-integration-test + + integration-test + verify + + + + wildfly-secondary + + target/failsafe-reports/failsafe-summary-wildfly-secondary.xml + + + + + + maven-dependency-plugin + + + unpack-wildfly-secondary + pre-integration-test + + unpack + + + + + + org.wildfly + wildfly-dist + ${wildfly-secondary.version} + tar.gz + false + ${project.build.directory} + + + + + + copy-patch-secondary + pre-integration-test + + copy + + + + + + ${project.groupId} + hibernate-validator-modules + ${project.version} + wildfly-${wildfly-secondary.version}-patch + zip + ${project.build.directory} + + + + + + copy-javamoney-secondary + pre-integration-test + + copy + + + + + + javax.money + money-api + ${version.javax.money} + ${wildfly-secondary.modules-dir}/javax/money/api/main/ + + + org.javamoney + moneta + ${version.org.javamoney.moneta} + ${wildfly-secondary.modules-dir}/org/javamoney/moneta/main/ + + + + + + + + maven-resources-plugin + + + + copy-wildfly-secondary-resources + pre-integration-test + + copy-resources + + + ${wildfly-secondary.modules-dir} + + + src/test/modules + true + + + + + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + apply-wildfly-secondary-patch-file + pre-integration-test + + execute-commands + + + true + ${project.build.directory}/wildfly-${wildfly-secondary.version}/ + false + + patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly-secondary.version}-patch.zip + + + + + + + + diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java index d71ed060d5..833d2021a0 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/CustomValidationProviderInDeploymentUnitIT.java @@ -12,7 +12,6 @@ import javax.validation.Validator; import javax.validation.ValidatorFactory; -import org.apache.log4j.Logger; import org.hibernate.validator.integration.AbstractArquillianIT; import org.hibernate.validator.integration.util.IntegrationTestUtil; import org.hibernate.validator.integration.util.MyValidator; @@ -31,7 +30,6 @@ public class CustomValidationProviderInDeploymentUnitIT extends AbstractArquillianIT { private static final String WAR_FILE_NAME = CustomValidationProviderInDeploymentUnitIT.class.getSimpleName() + ".war"; - private static final Logger log = Logger.getLogger( CustomValidationProviderInDeploymentUnitIT.class ); @Deployment public static Archive createTestArchive() { @@ -49,8 +47,6 @@ public static Archive createTestArchive() { @Test public void testValidatorFactoryFromCustomValidationProvider() throws Exception { - log.debug( "Running testValidatorFactoryFromCustomValidationProvider..." ); - Validator validator = validatorFactory.getValidator(); // Asserting the validator type as the VF is the wrapper type used within WildFly (LazyValidatorFactory) @@ -58,7 +54,5 @@ public void testValidatorFactoryFromCustomValidationProvider() throws Exception .as( "The custom validator implementation as retrieved from the default provider configured in META-INF/validation.xml should be used but actually " + validator + " is used" ) .isInstanceOf( MyValidator.class ); - - log.debug( "testValidatorFactoryFromCustomValidationProvider completed" ); } } diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java index 785bc28747..d0881e6af4 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/JndiLookupOfValidatorFactoryIT.java @@ -14,7 +14,6 @@ import javax.naming.NamingException; import javax.validation.ValidatorFactory; -import org.apache.log4j.Logger; import org.hibernate.validator.integration.AbstractArquillianIT; import org.hibernate.validator.internal.engine.ValidatorImpl; import org.jboss.arquillian.container.test.api.Deployment; @@ -28,7 +27,6 @@ */ public class JndiLookupOfValidatorFactoryIT extends AbstractArquillianIT { private static final String WAR_FILE_NAME = JndiLookupOfValidatorFactoryIT.class.getSimpleName() + ".war"; - private static final Logger log = Logger.getLogger( JndiLookupOfValidatorFactoryIT.class ); private static final String DEFAULT_JNDI_NAME_OF_VALIDATOR_FACTORY = "java:comp/ValidatorFactory"; @Deployment @@ -38,7 +36,6 @@ public static Archive createTestArchive() { @Test public void testDefaultValidatorFactoryLookup() throws Exception { - log.debug( "Running testDefaultValidatorFactoryLookup..." ); try { Context ctx = new InitialContext(); Object obj = ctx.lookup( DEFAULT_JNDI_NAME_OF_VALIDATOR_FACTORY ); @@ -51,6 +48,5 @@ public void testDefaultValidatorFactoryLookup() throws Exception { catch (NamingException e) { fail( "The default validator factory should be bound" ); } - log.debug( "testDefaultValidatorFactoryLookup completed" ); } } diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/CustomValidatorFactoryInPersistenceUnitIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/CustomValidatorFactoryInPersistenceUnitIT.java index 7d28896f77..151d421ed6 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/CustomValidatorFactoryInPersistenceUnitIT.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/CustomValidatorFactoryInPersistenceUnitIT.java @@ -12,7 +12,6 @@ import javax.inject.Inject; import javax.validation.ConstraintViolationException; -import org.apache.log4j.Logger; import org.hibernate.validator.integration.AbstractArquillianIT; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.shrinkwrap.api.Archive; @@ -32,7 +31,6 @@ public class CustomValidatorFactoryInPersistenceUnitIT extends AbstractArquillianIT { private static final String WAR_FILE_NAME = CustomValidatorFactoryInPersistenceUnitIT.class.getSimpleName() + ".war"; - private static final Logger log = Logger.getLogger( CustomValidatorFactoryInPersistenceUnitIT.class ); @Deployment public static Archive createTestArchive() { @@ -64,8 +62,6 @@ private static Asset persistenceXml() { @Test public void testValidatorFactoryPassedToPersistenceUnitIsCorrectlyConfigured() throws Exception { - log.debug( "Running testValidatorFactoryPassedToPersistenceUnitIsCorrectlyConfigured..." ); - try { magicianService.storeMagician(); fail( "Expected exception wasn't raised" ); @@ -79,8 +75,6 @@ public void testValidatorFactoryPassedToPersistenceUnitIsCorrectlyConfigured() t assertThat( constraintViolationException.getConstraintViolations().iterator().next().getMessage() ) .isEqualTo( "Invalid magician name" ); } - - log.debug( "testValidatorFactoryPassedToPersistenceUnitIsCorrectlyConfigured completed" ); } /** @@ -90,8 +84,6 @@ public void testValidatorFactoryPassedToPersistenceUnitIsCorrectlyConfigured() t // TODO How to make that work reliably also after a HV upgrade within WF? @Test public void testValidatorFactoryPassedToPersistenceUnitIsContributedFromPortableExtensionOfCurrentModuleZip() throws Exception { - log.debug( "Running testValidatorFactoryPassedToPersistenceUnitIsContributedFromPortableExtensionOfCurrentModuleZip..." ); - try { magicianService.storeWand(); fail( "Expected exception wasn't raised" ); @@ -105,8 +97,6 @@ public void testValidatorFactoryPassedToPersistenceUnitIsContributedFromPortable assertThat( constraintViolationException.getConstraintViolations().iterator().next().getMessage() ) .isEqualTo( "size must be between 5 and 2147483647" ); } - - log.debug( "testValidatorFactoryPassedToPersistenceUnitIsContributedFromPortableExtensionOfCurrentModuleZip completed" ); } private Throwable getRootException(Throwable throwable) { diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/JPATraversableResolverIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/JPATraversableResolverIT.java new file mode 100644 index 0000000000..a07d7ee8df --- /dev/null +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/jpa/JPATraversableResolverIT.java @@ -0,0 +1,56 @@ +/* + * 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.integration.wildfly.jpa; + +import static org.assertj.core.api.Assertions.assertThat; + +import javax.validation.TraversableResolver; + +import org.hibernate.validator.integration.AbstractArquillianIT; +import org.hibernate.validator.internal.engine.resolver.JPATraversableResolver; +import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.asset.Asset; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; +import org.jboss.shrinkwrap.descriptor.api.persistence20.PersistenceDescriptor; +import org.testng.annotations.Test; + +/** + * Tests that the default {@link TraversableResolver} for a JPA environment is {@code JPATraversableResolver}. + * + * @author Guillaume Smet + */ +public class JPATraversableResolverIT extends AbstractArquillianIT { + + private static final String WAR_FILE_NAME = JPATraversableResolverIT.class.getSimpleName() + ".war"; + + @Deployment + public static Archive createTestArchive() { + return buildTestArchive( WAR_FILE_NAME ) + .addAsResource( persistenceXml(), "META-INF/persistence.xml" ) + .addAsWebInfResource( EmptyAsset.INSTANCE, "beans.xml" ); + } + + private static Asset persistenceXml() { + String persistenceXml = Descriptors.create( PersistenceDescriptor.class ) + .version( "2.0" ) + .createPersistenceUnit() + .name( "default" ) + .jtaDataSource( "java:jboss/datasources/ExampleDS" ) + .up() + .exportAsString(); + return new StringAsset( persistenceXml ); + } + + @Test + public void testDefaultTraversableResolverInJPAEnvironmentIsJPATraversableResolver() throws Exception { + assertThat( TraversableResolvers.getDefault() ).isInstanceOf( JPATraversableResolver.class ); + } +} diff --git a/integration/src/test/resources/arquillian.xml b/integration/src/test/resources/arquillian.xml index 0802cd63a1..069649b467 100644 --- a/integration/src/test/resources/arquillian.xml +++ b/integration/src/test/resources/arquillian.xml @@ -24,7 +24,7 @@ ${basedir}/target/wildfly-${wildfly.version} - -Dee8.preview.mode=true ${arquillian.javaVmArguments.add-opens} + -Dee8.preview.mode=true ${arquillian.wildfly.jvm.args} javax.validation @@ -267,6 +278,14 @@ ${wildfly-secondary.patched.target-dir}/modules/system/layers/base/org/hibernate/validator/cdi/main hibernate-validator-cdi-${project.version}.jar + + org.jsoup + jsoup + ${jsoup.version} + false + ${wildfly-secondary.patched.target-dir}/modules/system/layers/base/org/jsoup/main + jsoup-${jsoup.version}.jar + @@ -284,7 +303,6 @@ generate-patch - ${patch-gen-maven-plugin.argLine.add-opens} ${wildfly-main.original.target-dir} ${module.xml.targetdir}/wildfly-current-patch.xml ${wildfly-main.patched.target-dir} @@ -298,7 +316,6 @@ generate-patch - ${patch-gen-maven-plugin.argLine.add-opens} ${wildfly-secondary.original.target-dir} ${module.xml.targetdir}/wildfly-secondary-patch.xml ${wildfly-secondary.patched.target-dir} @@ -309,16 +326,4 @@ - - - - jdk9 - - [9,) - - - --add-opens=java.base/java.lang=ALL-UNNAMED - - - diff --git a/modules/src/main/modules/wildfly-current-patch.xml b/modules/src/main/modules/wildfly-current-patch.xml index 3f8f6178f2..4de2867be6 100644 --- a/modules/src/main/modules/wildfly-current-patch.xml +++ b/modules/src/main/modules/wildfly-current-patch.xml @@ -16,6 +16,7 @@ + diff --git a/modules/src/main/modules/wildfly-secondary-patch.xml b/modules/src/main/modules/wildfly-secondary-patch.xml index 8692b9b7f8..14c018a22e 100644 --- a/modules/src/main/modules/wildfly-secondary-patch.xml +++ b/modules/src/main/modules/wildfly-secondary-patch.xml @@ -16,6 +16,7 @@ + diff --git a/modules/src/script/setupModules.groovy b/modules/src/script/setupModules.groovy index f4763c3c88..daf0936cd3 100644 --- a/modules/src/script/setupModules.groovy +++ b/modules/src/script/setupModules.groovy @@ -17,6 +17,10 @@ def appendDependency(File file, String dependencyToAppend, boolean optional) { file.write( file.text.replaceAll( /<\/dependencies>/, ' \n ' ) ) } +def removeDependency(File file, String dependencyToRemove) { + file.write( file.text.replaceAll( //, '' ) ) +} + // BV API bvModuleXml = new File( wildflyPatchedTargetDir, 'modules/system/layers/base/javax/validation/api/main/module.xml' ) def bvArtifactName = 'validation-api-' + project.properties['bv.api.version'] + '.jar'; @@ -34,6 +38,11 @@ println "[INFO] Using HV version " + hvArtifactName; processFileInplace( hvModuleXml ) { text -> text.replaceAll( /hibernate-validator.*jar/, hvArtifactName ) } + +removeDependency( hvModuleXml, "org.apache.xerces" ) +appendDependency( hvModuleXml, "javax.xml.stream.api", false ) +appendDependency( hvModuleXml, "javax.api", false ) + appendDependency( hvModuleXml, "javax.money.api", true ) appendDependency( hvModuleXml, "javafx.api", true ) @@ -49,4 +58,14 @@ processFileInplace( hvCdiModuleXml ) { text -> deleteFiles( new FileNameFinder().getFileNames( wildflyPatchedTargetDir + '/modules/system/layers/base/org/hibernate/validator/cdi/main', 'hibernate-validator-cdi-*.jar' ) ) +// JSOUP 1.15.4 +jsoupModuleXml = new File( wildflyPatchedTargetDir, 'modules/system/layers/base/org/jsoup/main/module.xml' ) +def jsoupArtifactName = 'jsoup-' + project.properties['jsoup.version'] + '.jar'; +println "[INFO] Using JSOUP version " + jsoupArtifactName; +processFileInplace( jsoupModuleXml ) { text -> + text.replaceAll( /jsoup.*jar/, jsoupArtifactName ) +} + +deleteFiles( new FileNameFinder().getFileNames( wildflyPatchedTargetDir + '/modules/system/layers/base/org/jsoup/main', 'jsoup-*.jar' ) ) + println "[INFO] ------------------------------------------------------------------------"; diff --git a/osgi/felixtest/pom.xml b/osgi/felixtest/pom.xml index 7ce49d4fb6..b3a13374dd 100644 --- a/osgi/felixtest/pom.xml +++ b/osgi/felixtest/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-osgi - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -68,6 +68,16 @@ arquillian-testng-container test + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-api-maven + test + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-impl-maven + test + org.jboss.arquillian.protocol arquillian-protocol-servlet @@ -75,7 +85,7 @@ fish.payara.arquillian - arquillian-payara-server-4-managed + arquillian-payara-server-managed ${payara-arquillian.version} test @@ -103,6 +113,12 @@ true + + maven-gpg-plugin + + true + + org.apache.maven.plugins maven-failsafe-plugin diff --git a/osgi/felixtest/src/test/java/org/hibernate/validator/osgi/felix/FelixCDIIT.java b/osgi/felixtest/src/test/java/org/hibernate/validator/osgi/felix/FelixCDIIT.java index 1a18c2cb47..f0a41b6331 100644 --- a/osgi/felixtest/src/test/java/org/hibernate/validator/osgi/felix/FelixCDIIT.java +++ b/osgi/felixtest/src/test/java/org/hibernate/validator/osgi/felix/FelixCDIIT.java @@ -14,6 +14,8 @@ import org.jboss.arquillian.testng.Arquillian; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; +import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage; import org.testng.annotations.Test; import com.example.cdi.MyBean; @@ -27,11 +29,13 @@ public class FelixCDIIT extends Arquillian { @Deployment public static Archive deployment() { + PomEquippedResolveStage pom = Maven.resolver().loadPomFromFile( "pom.xml" ); WebArchive war = create( WebArchive.class ) .addClasses( MyBean.class, ValidNumber.class, - ValidNumberValidator.class ); + ValidNumberValidator.class ) + .addAsLibraries( pom.resolve( "org.testng:testng" ).withTransitivity().asFile() ); return war; } diff --git a/osgi/integrationtest/pom.xml b/osgi/integrationtest/pom.xml index 869857cb10..c72f851593 100644 --- a/osgi/integrationtest/pom.xml +++ b/osgi/integrationtest/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-osgi - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -23,6 +23,7 @@ true + true forbidden-allow-junit.txt ../.. @@ -137,8 +138,8 @@ test - org.slf4j - slf4j-log4j12 + org.apache.logging.log4j + log4j-slf4j-impl test @@ -168,6 +169,10 @@ maven-surefire-plugin false + + ${settings.localRepository} + ${mavencentral.repo.url} + diff --git a/osgi/integrationtest/src/test/java/com/example/ExampleConstraintValidatorFactory.java b/osgi/integrationtest/src/test/java/com/example/ExampleConstraintValidatorFactory.java index 3b6c81ee5a..ed160f1eec 100644 --- a/osgi/integrationtest/src/test/java/com/example/ExampleConstraintValidatorFactory.java +++ b/osgi/integrationtest/src/test/java/com/example/ExampleConstraintValidatorFactory.java @@ -6,6 +6,7 @@ */ package com.example; +import java.lang.reflect.InvocationTargetException; import java.util.concurrent.atomic.AtomicInteger; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorFactory; @@ -22,9 +23,10 @@ public class ExampleConstraintValidatorFactory implements ConstraintValidatorFac @Override public > T getInstance(Class key) { try { - return key.newInstance(); + return key.getConstructor().newInstance(); } - catch (InstantiationException | IllegalAccessException e) { + catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException + | SecurityException e) { throw new RuntimeException( e ); } finally { diff --git a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/JavaVersionUtil.java b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/JavaVersionUtil.java new file mode 100644 index 0000000000..a38a53ac71 --- /dev/null +++ b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/JavaVersionUtil.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.osgi.integrationtest; + +import java.util.Locale; + +final class JavaVersionUtil { + + private JavaVersionUtil() { + } + + static int getMajorVersion() { + String javaSpecVersion = System.getProperty( "java.specification.version" ); + try { + if ( javaSpecVersion.contains( "." ) ) { //before jdk 9 + return Integer.parseInt( javaSpecVersion.split( "\\." )[1] ); + } + else { + return Integer.parseInt( javaSpecVersion ); + } + } + catch (NumberFormatException e) { + throw new IllegalArgumentException( String.format( Locale.ROOT, "We are unable to parse Java version '%1$s'.", javaSpecVersion ) ); + } + } +} diff --git a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/KarafFeaturesAreInstallableTest.java b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/KarafFeaturesAreInstallableTest.java index 12b5c2e266..4931ca3fca 100644 --- a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/KarafFeaturesAreInstallableTest.java +++ b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/KarafFeaturesAreInstallableTest.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.osgi.integrationtest; +import static org.hibernate.validator.osgi.integrationtest.PaxExamOptions.JAVA_9; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.ops4j.pax.exam.CoreOptions.maven; @@ -66,8 +67,12 @@ public Option[] config() { .type( "xml" ) .versionAsInProject(); + String mavenLocalRepository = System.getProperty( "maven.settings.localRepository" ); + String mavenCentralRepository = System.getProperty( "maven.mavencentral.repo.url" ); + return options( when( DEBUG ).useOptions( debugConfiguration( "5005", true ) ), + when( JavaVersionUtil.getMajorVersion() >= 9 ).useOptions( JAVA_9.options() ), karafDistributionConfiguration() .frameworkUrl( maven() @@ -90,6 +95,31 @@ public Option[] config() { "featuresBoot", "system" ), + /* + * Use the same local Maven repository as the build job. + * This allows to retrieve the just-installed artifacts in case + * the local repo was overridden from the command line. + * + * See https://ops4j1.jira.com/wiki/spaces/paxurl/pages/3833866/Mvn+Protocol for more information + * on the configuration below. + */ + editConfigurationFilePut( + "etc/org.ops4j.pax.url.mvn.cfg", + "org.ops4j.pax.url.mvn.defaultRepositories", + "file://" + mavenLocalRepository + + "@snapshots" + + "@id=local-repo-from-maven-settings" + ), + editConfigurationFilePut( + "etc/org.ops4j.pax.url.mvn.cfg", + "org.ops4j.pax.url.mvn.localRepository", + mavenLocalRepository + ), + editConfigurationFilePut( // Erase the defaults: Maven Central uses HTTP by default... + "etc/org.ops4j.pax.url.mvn.cfg", + "org.ops4j.pax.url.mvn.repositories", + mavenCentralRepository + "@id=central" + ), systemProperty( "validatorRepositoryUrl" ).value( hibernateValidatorFeature.getURL() ) ); } 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 135074a717..e65b1f3b3d 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 @@ -6,6 +6,7 @@ */ package org.hibernate.validator.osgi.integrationtest; +import static org.hibernate.validator.osgi.integrationtest.PaxExamOptions.JAVA_9; import static org.junit.Assert.assertEquals; import static org.ops4j.pax.exam.CoreOptions.maven; import static org.ops4j.pax.exam.CoreOptions.options; @@ -89,8 +90,12 @@ public Option[] config() { .type( "xml" ) .versionAsInProject(); + String mavenLocalRepository = System.getProperty( "maven.settings.localRepository" ); + String mavenCentralRepository = System.getProperty( "maven.mavencentral.repo.url" ); + return options( when( DEBUG ).useOptions( debugConfiguration( "5005", true ) ), + when( JavaVersionUtil.getMajorVersion() >= 9 ).useOptions( JAVA_9.options() ), karafDistributionConfiguration() .frameworkUrl( maven() @@ -113,6 +118,31 @@ public Option[] config() { "featuresBoot", "system" ), + /* + * Use the same local Maven repository as the build job. + * This allows to retrieve the just-installed artifacts in case + * the local repo was overridden from the command line. + * + * See https://ops4j1.jira.com/wiki/spaces/paxurl/pages/3833866/Mvn+Protocol for more information + * on the configuration below. + */ + editConfigurationFilePut( + "etc/org.ops4j.pax.url.mvn.cfg", + "org.ops4j.pax.url.mvn.defaultRepositories", + "file://" + mavenLocalRepository + + "@snapshots" + + "@id=local-repo-from-maven-settings" + ), + editConfigurationFilePut( + "etc/org.ops4j.pax.url.mvn.cfg", + "org.ops4j.pax.url.mvn.localRepository", + mavenLocalRepository + ), + editConfigurationFilePut( // Erase the defaults: Maven Central uses HTTP by default... + "etc/org.ops4j.pax.url.mvn.cfg", + "org.ops4j.pax.url.mvn.repositories", + mavenCentralRepository + "@id=central" + ), features( hibernateValidatorFeature, "hibernate-validator", "hibernate-validator-jsoup", "hibernate-validator-joda-time", "hibernate-validator-javax-money", "hibernate-validator-groovy" ) ); diff --git a/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/PaxExamOptions.java b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/PaxExamOptions.java new file mode 100644 index 0000000000..cf8166b46d --- /dev/null +++ b/osgi/integrationtest/src/test/java/org/hibernate/validator/osgi/integrationtest/PaxExamOptions.java @@ -0,0 +1,43 @@ +/* + * 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.osgi.integrationtest; + +import org.ops4j.pax.exam.CoreOptions; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.options.DefaultCompositeOption; + +public enum PaxExamOptions { + JAVA_9( + CoreOptions.vmOptions( + "--add-opens", + "java.base/java.security=ALL-UNNAMED", + "--add-opens", + "java.base/java.net=ALL-UNNAMED", + "--add-opens", + "java.base/java.lang=ALL-UNNAMED", + "--add-opens", + "java.base/java.util=ALL-UNNAMED", + "--add-exports=java.base/sun.net.www.protocol.http=ALL-UNNAMED", + "--add-exports=java.base/sun.net.www.protocol.https=ALL-UNNAMED", + "--add-exports=java.xml.bind/com.sun.xml.internal.bind.v2.runtime=ALL-UNNAMED", + "--add-exports=jdk.xml.dom/org.w3c.dom.html=ALL-UNNAMED", + "--add-exports=jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED", + "--add-exports=java.xml.ws/com.sun.xml.internal.messaging.saaj.soap.impl=ALL-UNNAMED", + "--add-modules", + "java.xml.ws.annotation,java.corba,java.transaction,java.xml.bind,java.xml.ws,jdk.xml.bind" ) + ); + + private final Option options; + + PaxExamOptions(Option... options) { + this.options = new DefaultCompositeOption( options ); + } + + public Option options() { + return options; + } +} diff --git a/osgi/karaf-features/pom.xml b/osgi/karaf-features/pom.xml index d7df970c38..6cf4c90ce2 100644 --- a/osgi/karaf-features/pom.xml +++ b/osgi/karaf-features/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-osgi - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml diff --git a/osgi/pom.xml b/osgi/pom.xml index e54c36050d..16f50a2a2a 100644 --- a/osgi/pom.xml +++ b/osgi/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml diff --git a/performance/README.md b/performance/README.md index 27fbf0b9ca..a2f3c300da 100644 --- a/performance/README.md +++ b/performance/README.md @@ -11,9 +11,9 @@ To allow performance testing of different Hibernate Validator versions there are Choosing a profile executes the tests against the specified Hibernate Validator or BVal version, respectively. The defined profiles are: -* hv-current (Hibernate Validator 6.0.0-SNAPSHOT) -* hv-6.0 (Hibernate Validator 6.0.5.Final) -* hv-5.4 (Hibernate Validator 5.4.1.Final) +* hv-current (Hibernate Validator `${project.version}`) +* hv-6.0 (Hibernate Validator 6.0.15.Final) +* hv-5.4 (Hibernate Validator 5.4.3.Final) * hv-5.3 (Hibernate Validator 5.3.4.Final) * hv-5.2 (Hibernate Validator 5.2.4.Final) * hv-5.1 (Hibernate Validator 5.1.3.Final) diff --git a/performance/pom.xml b/performance/pom.xml index 37f1726e70..332836017d 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -98,6 +98,12 @@ true + + maven-gpg-plugin + + true + + org.apache.maven.plugins maven-shade-plugin @@ -203,7 +209,7 @@ Hibernate Validator - 6.0.8.Final + 6.0.15.Final @@ -245,7 +251,7 @@ 1.1.0.Final Hibernate Validator - 5.4.2.Final + 5.4.3.Final diff --git a/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java b/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java index 636a4d4205..fb5175b625 100644 --- a/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java +++ b/performance/src/main/java-bv2/org/hibernate/validator/performance/multilevel/MultiLevelContainerValidation.java @@ -13,6 +13,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Random; @@ -189,7 +190,7 @@ public EmailAddress(String value) { public static EmailAddress generate() { return new EmailAddress( - String.format( "%s@%s.com", RandomDataGenerator.randomString(), RandomDataGenerator.randomString() ) + String.format( Locale.ROOT, "%s@%s.com", RandomDataGenerator.randomString(), RandomDataGenerator.randomString() ) ); } diff --git a/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java b/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java index 98671afb86..740318cdd4 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/simple/SimpleValidation.java @@ -6,10 +6,8 @@ */ package org.hibernate.validator.performance.simple; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Random; import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import javax.validation.ConstraintViolation; @@ -53,14 +51,23 @@ public class SimpleValidation { @State(Scope.Benchmark) public static class ValidationState { public volatile Validator validator; - public volatile Random random; + public volatile ThreadLocalRandom random; + public volatile Driver[] drivers; { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); - random = new Random(); + random = ThreadLocalRandom.current(); + + drivers = new Driver[100]; + for ( int i = 0; i < 100; i++ ) { + drivers[i] = new DriverSetup( random ).getDriver(); + } } + public Driver nextDriver() { + return drivers[random.nextInt( 100 )]; + } } @Benchmark @@ -71,13 +78,13 @@ public static class ValidationState { @Warmup(iterations = 10) @Measurement(iterations = 20) public void testSimpleBeanValidation(ValidationState state, Blackhole bh) { - DriverSetup driverSetup = new DriverSetup( state ); - Set> violations = state.validator.validate( driverSetup.getDriver() ); - assertThat( violations ).hasSize( driverSetup.getExpectedViolationCount() ); + Driver driver = state.nextDriver(); + Set> violations = state.validator.validate( driver ); + assert driver.getExpectedViolationCount() == violations.size(); bh.consume( violations ); } - public class Driver { + public static class Driver { @NotNull private String name; @@ -87,10 +94,17 @@ public class Driver { @AssertTrue private boolean hasDrivingLicense; - public Driver(String name, int age, boolean hasDrivingLicense) { + private int expectedViolationCount; + + public Driver(String name, int age, boolean hasDrivingLicense, int expectedViolationCount) { this.name = name; this.age = age; this.hasDrivingLicense = hasDrivingLicense; + this.expectedViolationCount = expectedViolationCount; + } + + public int getExpectedViolationCount() { + return expectedViolationCount; } @Override @@ -105,34 +119,30 @@ public String toString() { } } - private class DriverSetup { + private static class DriverSetup { private int expectedViolationCount; private Driver driver; - public DriverSetup(ValidationState state) { + public DriverSetup(ThreadLocalRandom random) { expectedViolationCount = 0; - String name = names[state.random.nextInt( 10 )]; + String name = names[random.nextInt( 10 )]; if ( name == null ) { expectedViolationCount++; } - int randomAge = state.random.nextInt( 100 ); + int randomAge = random.nextInt( 100 ); if ( randomAge < 18 ) { expectedViolationCount++; } - int rand = state.random.nextInt( 2 ); + int rand = random.nextInt( 2 ); boolean hasLicense = rand == 1; if ( !hasLicense ) { expectedViolationCount++; } - driver = new Driver( name, randomAge, hasLicense ); - } - - public int getExpectedViolationCount() { - return expectedViolationCount; + driver = new Driver( name, randomAge, hasLicense, expectedViolationCount ); } public Driver getDriver() { diff --git a/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java b/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java index 7d484f5bd8..9bca4a9df5 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java +++ b/performance/src/main/java/org/hibernate/validator/performance/statistical/TestEntity.java @@ -7,9 +7,13 @@ package org.hibernate.validator.performance.statistical; import java.math.BigDecimal; +import java.time.ZoneId; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + import javax.validation.Valid; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.AssertTrue; @@ -28,7 +32,7 @@ */ public class TestEntity { public static final int MAX_DEPTH = 10; - private static final Calendar cal = GregorianCalendar.getInstance(); + private static final Calendar cal = GregorianCalendar.getInstance( TimeZone.getTimeZone( ZoneId.of( "GMT" ) ), Locale.ROOT ); public TestEntity(int depth) { if ( depth <= MAX_DEPTH ) { diff --git a/performance/src/main/java/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java b/performance/src/main/java/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java new file mode 100644 index 0000000000..70f8c4c804 --- /dev/null +++ b/performance/src/main/java/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java @@ -0,0 +1,127 @@ +/* + * 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.performance.unconstrained; + +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/** + * @author Guillaume Smet + */ +public class UnconstrainedBeanValidation { + + private static final String[] names = { + null, + "Jacob", + "Isabella", + "Ethan", + "Sophia", + "Michael", + "Emma", + "Jayden", + "Olivia", + "William" + }; + + @State(Scope.Benchmark) + public static class ValidationState { + public volatile Validator validator; + public volatile ThreadLocalRandom random; + private volatile Driver[] drivers; + + { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + random = ThreadLocalRandom.current(); + + drivers = new Driver[100]; + for ( int i = 0; i < 100; i++ ) { + drivers[i] = new DriverSetup( random ).getDriver(); + } + } + + public Driver nextDriver() { + return drivers[random.nextInt( 100 )]; + } + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @Fork(value = 1) + @Threads(50) + @Warmup(iterations = 10) + @Measurement(iterations = 20) + public void testUnconstrainedBeanValidation(ValidationState state, Blackhole bh) { + Set> violations = state.validator.validate( state.nextDriver() ); + bh.consume( violations ); + } + + public static class Driver { + + private String name; + + private int age; + + private boolean hasDrivingLicense; + + public Driver(String name, int age, boolean hasDrivingLicense) { + this.name = name; + this.age = age; + this.hasDrivingLicense = hasDrivingLicense; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append( "Driver" ); + sb.append( "{name='" ).append( name ).append( '\'' ); + sb.append( ", age=" ).append( age ); + sb.append( ", hasDrivingLicense=" ).append( hasDrivingLicense ); + sb.append( '}' ); + return sb.toString(); + } + } + + private static class DriverSetup { + + private Driver driver; + + public DriverSetup(ThreadLocalRandom random) { + String name = names[random.nextInt( 10 )]; + + int randomAge = random.nextInt( 100 ); + + int rand = random.nextInt( 2 ); + boolean hasLicense = rand == 1; + + driver = new Driver( name, randomAge, hasLicense ); + } + + public Driver getDriver() { + return driver; + } + } +} diff --git a/pom.xml b/pom.xml index bc05e88713..da10ced91d 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT pom Hibernate Validator Aggregator @@ -86,13 +86,9 @@ test-utils build-config engine - cdi - modules tck-runner annotation-processor - integration performance - osgi @@ -113,10 +109,10 @@ true 2.0.1.Final - 2.0.2.Final + 2.0.6 - 6.0.4.Final + 6.0.9.Final 2.8 3.0.1-b09 @@ -124,9 +120,9 @@ 2.1.0.Final - 12.0.0.Final + 17.0.1.Final - 11.0.0.Final + 16.0.0.Final ${wildfly.version} @@ -135,55 +131,58 @@ See http://search.maven.org/#search|gav|1|g%3A"org.wildfly"%20AND%20a%3A"wildfly-parent" --> 1.3.4 - 1.8.3 + 1.15.4 2.9.7 1.7.22 - 1.0.2.Final + 2.2 - 2.0 - 3.0.3.Final - 2.1.0.Final - 1.0.1.Final + 2.0.SP1 + 3.1.1.Final + 2.2.0.Final + 1.0.2.Final 1.0.1 1.1 - + 1.2 - 2.0.1.Alpha5 + 2.0.1.Final 5.0.3 1.2.1.Final 4.0.0.Final + + 11.0.2 + - 1.1.11.Final - 6.8 + 1.6.0.Final + 6.14.3 3.8.0 4.12 3.4 - 4.11.0 - 2.5.2 - 4.1.1 + 4.12.0 + 2.5.4 + 4.2.0 6.0.0 - 5.Beta1 - 1.0.Beta2 + 5.181 + 2.3.1 8.1 2.4.12 - 23.0 - + 27.1-jre + 2.17.1 4.3.10.RELEASE @@ -203,25 +202,46 @@ http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api http://javamoney.github.io/apidocs - 2.20.1 + 3.0.1 + 2.21.0 - + + + -Dmaven.repo.local=${settings.localRepository} + - java.xml.bind - java.xml.bind + Should be set from the command line. + Used by CI jobs that execute tests in multiple environments. + Allows distinguishing between multiple executions of the same test in test reports. + --> + default - + + + ${arquillian.wildfly.jvm.args.add-opens} ${arquillian.wildfly.jvm.args.add-modules} forbidden-junit.txt + 10 + + + https://repo.maven.apache.org/maven2/ + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + ossrh + https://oss.sonatype.org/content/repositories/snapshots . @@ -318,9 +338,15 @@ ${jsoup.version} - log4j - log4j - 1.2.17 + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + test-jar + ${log4j.version} org.slf4j @@ -328,14 +354,14 @@ ${slf4j.version} - org.slf4j - slf4j-log4j12 - ${slf4j.version} + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} - org.hibernate.javax.persistence - hibernate-jpa-2.1-api - ${hibernate-jpa-2.1-api.version} + javax.persistence + javax.persistence-api + ${javax.persistence-api.version} junit @@ -372,7 +398,7 @@ javax.annotation javax.annotation-api - 1.2 + ${javax-annotation-api.version} org.jboss.spec.javax.interceptor @@ -573,8 +599,9 @@ de.thetaphi forbiddenapis - 2.4.1 + 2.5 + ${forbiddenapis.jdk.target} false @@ -606,13 +633,11 @@ verify - + jdk-unsafe jdk-deprecated - jdk-system-out + jdk-non-portable + jdk-internal @@ -624,10 +649,6 @@ verify - jdk-deprecated @@ -682,7 +703,8 @@ **/*Test.java - ${maven-surefire-plugin.argLine} + ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.shrinkwrap} + ${surefire.environment} @@ -706,7 +728,8 @@ maven-failsafe-plugin ${maven-surefire-plugin.version} - ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.argLine.add-modules} ${maven-surefire-plugin.argLine.add-opens} + ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.shrinkwrap} ${maven-surefire-plugin.argLine.add-opens} + ${surefire.environment} @@ -759,11 +782,6 @@ --> 1.5.0 - - org.codehaus.mojo - jaxb2-maven-plugin - 2.2 - org.asciidoctor asciidoctor-maven-plugin @@ -823,6 +841,10 @@ maven-deploy-plugin 2.8.2 + + maven-gpg-plugin + ${version.gpg.plugin} + maven-resources-plugin 3.0.2 @@ -935,7 +957,7 @@ org.netbeans.tools sigtest-maven-plugin - 1.0 + 1.2 @@ -1068,14 +1090,14 @@ - jboss-releases-repository - JBoss Releases Repository - https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ + ${ossrh.releases.repo.id} + OSSRH Releases Repository + ${ossrh.releases.repo.url} - jboss-snapshots-repository - JBoss Snapshots Repository - https://repository.jboss.org/nexus/content/repositories/snapshots/ + ${ossrh.snapshots.repo.id} + OSSRH Snapshots Repository + ${ossrh.snapshots.repo.url} @@ -1087,6 +1109,8 @@ disableDocumentationBuild !true + + (,12) documentation @@ -1099,6 +1123,8 @@ disableDistributionBuild !true + + (,12) distribution @@ -1131,21 +1157,37 @@ + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + ${env.RELEASE_GPG_HOMEDIR} + ${env.RELEASE_GPG_PASSPHRASE} + + + + - jdk9 + jdk9+ [9,) - --add-modules=${maven-surefire-plugin.jigsaw.modules} - + --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED @@ -1160,7 +1202,7 @@ --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.management/javax.management.openmbean=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED - + @@ -1169,16 +1211,13 @@ maven-compiler-plugin true - - -J--add-modules=java.xml.ws.annotation - maven-surefire-plugin ${maven-surefire-plugin.version} - ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.argLine.add-modules} ${maven-surefire-plugin.argLine.add-opens} + ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.shrinkwrap} ${maven-surefire-plugin.argLine.add-opens} @@ -1196,6 +1235,67 @@ + + + jdk10- + + (,11) + + + osgi + + + + + jdk11- + + (,12) + + + + cdi + modules + integration + + + + jdk11+ + + [11,) + + + + --add-modules=java.se + + + + + + + org.wildfly.plugins + wildfly-maven-plugin + + + --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.security=ALL-UNNAMED + --add-opens=java.base/java.io=ALL-UNNAMED + --add-modules=java.se + + + + + org.jboss.as + patch-gen-maven-plugin + + + --add-modules=java.se + + + + + + + jqassistant + + idea.maven.embedder.version + + + + + + + maven-compiler-plugin + ${version.compiler.plugin} + + + -Aorg.jboss.logging.tools.addGeneratedAnnotation=false + + -parameters + + + + + + + diff --git a/relocation/annotation-processor/pom.xml b/relocation/annotation-processor/pom.xml index c78e4a08d7..7ddfc1bdb2 100644 --- a/relocation/annotation-processor/pom.xml +++ b/relocation/annotation-processor/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT org.hibernate diff --git a/relocation/cdi/pom.xml b/relocation/cdi/pom.xml index 5346ffb489..fccc36142c 100644 --- a/relocation/cdi/pom.xml +++ b/relocation/cdi/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT org.hibernate diff --git a/relocation/engine/pom.xml b/relocation/engine/pom.xml index f8b6393a23..651ea404a1 100644 --- a/relocation/engine/pom.xml +++ b/relocation/engine/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT org.hibernate diff --git a/relocation/karaf-features/pom.xml b/relocation/karaf-features/pom.xml index a237283b0a..8489de6c9d 100644 --- a/relocation/karaf-features/pom.xml +++ b/relocation/karaf-features/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-relocation - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT org.hibernate diff --git a/relocation/pom.xml b/relocation/pom.xml index f8b8ce054d..0b1edd703b 100644 --- a/relocation/pom.xml +++ b/relocation/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT hibernate-validator-relocation diff --git a/settings-example.xml b/settings-example.xml deleted file mode 100644 index e96f41de72..0000000000 --- a/settings-example.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - jboss-public-repository - - - - central - Maven Central - http://repo.maven.apache.org/maven2/ - - false - never - - - - jboss-public-repository-group - JBoss Public Maven Repository Group - https://repository.jboss.org/nexus/content/groups/public-jboss/ - default - - true - never - - - true - never - - - - - - - central - Maven Central - http://repo.maven.apache.org/maven2/ - - false - never - - - - jboss-public-repository-group - JBoss Public Maven Repository Group - https://repository.jboss.org/nexus/content/groups/public-jboss/ - default - - true - never - - - true - never - - - - - - - - jboss-public-repository - - - diff --git a/tck-runner/pom.xml b/tck-runner/pom.xml index d637abbde5..f841ca47f6 100644 --- a/tck-runner/pom.xml +++ b/tck-runner/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT ../pom.xml @@ -38,17 +38,13 @@ ${project.groupId} hibernate-validator - - ${project.groupId} - hibernate-validator-cdi - org.glassfish javax.el - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + javax.persistence + javax.persistence-api test @@ -101,10 +97,37 @@ true + + maven-gpg-plugin + + true + + org.apache.maven.plugins maven-dependency-plugin + + copy-tck-bv-api-signature-file + generate-test-sources + + unpack + + + + + org.hibernate.beanvalidation.tck + beanvalidation-tck-tests + ${version.org.hibernate.beanvalidation.tck} + jar + true + + + + **/*.sig + ${project.build.directory}/api-signature + + copy-tck-test-suite-file generate-test-sources @@ -139,8 +162,6 @@ ${validation.provider} - methods - 4 @@ -236,6 +257,13 @@ wildfly-arquillian-container-managed test + + ${project.groupId} + hibernate-validator-modules + ${project.version} + wildfly-${wildfly-tck.version}-patch + zip + @@ -305,6 +333,83 @@ ${wildfly.target-dir} + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + update-standalone-xml + generate-test-resources + + execute + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + once + + incontainer + + + + + + + + sigtest + + + + org.netbeans.tools + sigtest-maven-plugin + + + + check + + + + + strictcheck + javax.validation,javax.validation.bootstrap,javax.validation.constraints, + javax.validation.constraintvalidation,javax.validation.executable,javax.validation.groups, + javax.validation.metadata,javax.validation.spi,javax.validation.valueextraction + + ${project.build.directory}/api-signature/validation-api-java8.sig + + + + + + + jdk9+ + + [9,) + + + --add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED + + + + jdk10- + + (,11) + + + maven-resources-plugin @@ -319,7 +424,7 @@ ${wildfly.target-dir}/modules/system/layers/base/ - src/test/modules + src/test/modules/jdk8 true @@ -327,45 +432,112 @@ - + + + + + jdk11+ + + [11,) + + + + org.openjfx + javafx-base + ${version.org.openjfx} + test + + + + + - org.codehaus.gmavenplus - gmavenplus-plugin + maven-resources-plugin - update-standalone-xml + copy-resources generate-test-resources - execute + copy-resources - - - + ${wildfly.target-dir}/modules/system/layers/base/ + + + src/test/modules/jdk11 + true + + org.apache.maven.plugins - maven-surefire-plugin - - once - - incontainer - - + maven-dependency-plugin + + + copy + generate-test-resources + + copy + + + + + org.openjfx + javafx-base + ${version.org.openjfx} + jar + + + org.openjfx + javafx-base + ${version.org.openjfx} + ${javafx.platform} + jar + + + ${wildfly.target-dir}/modules/system/layers/base/javafx/api/main/ + + + + - jdk9 + linux - [9,) + + linux + - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED + linux + + + + macosx + + + mac os x + + + + mac + + + + windows + + + windows + + + + win diff --git a/tck-runner/src/test/java/org/hibernate/validator/tckrunner/securitymanager/arquillian/LocalSecurityManagerTestingExecutionEvent.java b/tck-runner/src/test/java/org/hibernate/validator/tckrunner/securitymanager/arquillian/LocalSecurityManagerTestingExecutionEvent.java index 85c408df9d..13fb09a2fc 100644 --- a/tck-runner/src/test/java/org/hibernate/validator/tckrunner/securitymanager/arquillian/LocalSecurityManagerTestingExecutionEvent.java +++ b/tck-runner/src/test/java/org/hibernate/validator/tckrunner/securitymanager/arquillian/LocalSecurityManagerTestingExecutionEvent.java @@ -39,6 +39,11 @@ public DelegatingTestMethodExecutor(TestMethodExecutor delegate) { this.delegate = new DelegatingExecutor( new ArquillianExecutor( delegate ) ); } + @Override + public String getMethodName() { + return method.getName(); + } + @Override public Method getMethod() { return method; diff --git a/tck-runner/src/test/modules/jdk11/javafx/api/main/module.xml b/tck-runner/src/test/modules/jdk11/javafx/api/main/module.xml new file mode 100644 index 0000000000..91344118d8 --- /dev/null +++ b/tck-runner/src/test/modules/jdk11/javafx/api/main/module.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/tck-runner/src/test/modules/javafx/api/main/module.xml b/tck-runner/src/test/modules/jdk8/javafx/api/main/module.xml similarity index 100% rename from tck-runner/src/test/modules/javafx/api/main/module.xml rename to tck-runner/src/test/modules/jdk8/javafx/api/main/module.xml diff --git a/tck-runner/src/test/resources/arquillian.xml b/tck-runner/src/test/resources/arquillian.xml index ce14b1b50c..700b29110d 100644 --- a/tck-runner/src/test/resources/arquillian.xml +++ b/tck-runner/src/test/resources/arquillian.xml @@ -26,7 +26,7 @@ ${wildfly.target-dir} - ${arquillian.javaVmArguments.add-opens} + ${arquillian.wildfly.jvm.args} -Xmx1024m -XX:MaxPermSize=512m ${remote.debug} -Dvalidation.provider=${validation.provider} diff --git a/tck-runner/src/test/resources/test.policy b/tck-runner/src/test/resources/test.policy index cf6722c55e..09033d9aa8 100644 --- a/tck-runner/src/test/resources/test.policy +++ b/tck-runner/src/test/resources/test.policy @@ -29,9 +29,8 @@ grant codeBase "file:${localRepository}/org/hibernate/validator/hibernate-valida permission java.lang.RuntimePermission "setContextClassLoader"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; - - // JAXB - permission java.util.PropertyPermission "mapAnyUriToUri", "read"; + permission java.lang.RuntimePermission "getenv.ORG_HIBERNATE_VALIDATOR_EXPRESSION_LANGUAGE_ENABLED"; + permission java.util.PropertyPermission "org.hibernate.validator.expressionLanguageEnabled", "read"; }; // Used during aggregator builds also building "engine", e.g. mvn clean install -pl tck-runner -am @@ -42,9 +41,8 @@ grant codeBase "file:${basedir}/../engine/target/hibernate-validator-${project.v permission java.lang.RuntimePermission "setContextClassLoader"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; - - // JAXB - permission java.util.PropertyPermission "mapAnyUriToUri", "read"; + permission java.lang.RuntimePermission "getenv.ORG_HIBERNATE_VALIDATOR_EXPRESSION_LANGUAGE_ENABLED"; + permission java.util.PropertyPermission "org.hibernate.validator.expressionLanguageEnabled", "read"; }; grant codeBase "file:${localRepository}/com/fasterxml/classmate/-" { @@ -82,11 +80,13 @@ grant codeBase "file:${localRepository}/org/hibernate/beanvalidation/tck/-" { // Ideally, this domain should have no permissions at all; Only specifically enabling some API calls done by the BV TCK // tests (which do not use privileged actions for these) +// and by TestNG (which does not use privileged actions either). grant codeBase "file:${project.build.directory}/classes" { permission java.util.PropertyPermission "validation.provider", "read"; permission java.io.FilePermission "${localRepository}/org/hibernate/beanvalidation/tck/beanvalidation-tck-tests/${tck.version}/beanvalidation-tck-tests-${tck.version}.jar", "read"; permission java.util.PropertyPermission "user.language", "write"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; + permission "java.lang.reflect.ReflectPermission" "suppressAccessChecks"; }; grant codeBase "file:${project.build.directory}/test-classes" { diff --git a/test-utils/pom.xml b/test-utils/pom.xml index c7c34f94ee..6c5e14fe72 100644 --- a/test-utils/pom.xml +++ b/test-utils/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.0.23.SP3-SNAPSHOT hibernate-validator-test-utils @@ -58,8 +58,8 @@ validation-api - log4j - log4j + org.apache.logging.log4j + log4j-core provided diff --git a/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java b/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java index ae8830cd19..1316865371 100644 --- a/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java +++ b/test-utils/src/main/java/org/hibernate/validator/testutil/ConstraintViolationAssert.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; @@ -272,7 +273,8 @@ public void containsOnlyViolations(ViolationExpectation... expectedViolations) { referencePropertiesToTest = expectedViolations[0].propertiesToTest; for ( ViolationExpectation expectedViolation : expectedViolations ) { if ( !referencePropertiesToTest.equals( expectedViolation.propertiesToTest ) ) { - throw new IllegalArgumentException( String.format( "Expected violations passed in parameter must test the exact same properties but do not: %1$s != %2$s", + throw new IllegalArgumentException( String.format( Locale.ROOT, + "Expected violations passed in parameter must test the exact same properties but do not: %1$s != %2$s", expectedViolations[0], expectedViolation ) ); } } @@ -309,7 +311,7 @@ public void containsPath(PathExpectation expectedPath) { actualPaths.add( actual ); } - fail( String.format( "Didn't find path <%s> in actual paths <%s>.", expectedPath, actualPaths ) ); + fail( String.format( Locale.ROOT, "Didn't find path <%s> in actual paths <%s>.", expectedPath, actualPaths ) ); } public void containsPaths(PathExpectation... expectedPaths) { diff --git a/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java b/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java index 873b52c7e5..0fc8c8f1e1 100644 --- a/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java +++ b/test-utils/src/main/java/org/hibernate/validator/testutil/DescriptorAssert.java @@ -8,11 +8,13 @@ import static org.testng.Assert.fail; -import org.assertj.core.api.IterableAssert; - +import java.util.Locale; import java.util.Set; + import javax.validation.metadata.GroupConversionDescriptor; +import org.assertj.core.api.IterableAssert; + /** * Provides assertion methods for testing {@link javax.validation.metadata.ElementDescriptor} * implementations and collections thereof. @@ -54,7 +56,7 @@ public void containsConversion(Class from, Class to) { } if ( !foundMatchingConversion ) { - fail( String.format( "<%s> does not contain a conversion from <%s> to <%s>.", actual, from, to ) ); + fail( String.format( Locale.ROOT, "<%s> does not contain a conversion from <%s> to <%s>.", actual, from, to ) ); } } } diff --git a/test-utils/src/main/java/org/hibernate/validator/testutil/MessageLoggedAssertionLogger.java b/test-utils/src/main/java/org/hibernate/validator/testutil/MessageLoggedAssertionLogger.java deleted file mode 100644 index 4bb9324ba2..0000000000 --- a/test-utils/src/main/java/org/hibernate/validator/testutil/MessageLoggedAssertionLogger.java +++ /dev/null @@ -1,49 +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.testutil; - -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.spi.LoggingEvent; - -import static org.testng.Assert.assertTrue; - -/** - * A log4j logger which can be used to assert that a specified message got logged. - * - * @author Hardy Ferentschik - */ -public class MessageLoggedAssertionLogger extends AppenderSkeleton { - private final String expectedMessageCode; - private boolean messageLogged; - - public MessageLoggedAssertionLogger(String expectedMessageCode) { - this.expectedMessageCode = expectedMessageCode; - } - - @Override - protected void append(LoggingEvent event) { - if ( event.getRenderedMessage().startsWith( expectedMessageCode ) ) { - messageLogged = true; - } - } - - @Override - public void close() { - } - - @Override - public boolean requiresLayout() { - return false; - } - - public void assertMessageLogged() { - assertTrue( messageLogged, "Message " + expectedMessageCode + " got never logged" ); - } -} - -