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..8b9993ca3c --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,576 @@ +/* + * 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 +import org.hibernate.jenkins.pipeline.helpers.version.Version + +/* + * 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 includes two deployment modes: + * + * - A deployment of snapshot artifacts for every non-PR build on "primary" branches (master and maintenance branches). + * - A full release when starting the job with specific parameters. + * + * In the first case, the name of a Maven settings file must be provided in the job configuration file + * (see below). + * + * #### Gitter (optional) + * + * You need to enable the Jenkins integration in your Gitter room first: + * see https://gitlab.com/gitlab-org/gitter/webapp/blob/master/docs/integrations.md + * + * Then you will also need to configure *global* secret text credentials containing the Gitter webhook URL, + * and list the ID of these credentials in the job configuration file + * (see https://github.com/hibernate/hibernate-jenkins-pipeline-helpers#job-configuration-file). + * + * ### 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 servers with ID + * # 'jboss-releases-repository' and 'jboss-snapshots-repository'. + * settingsId: ... + */ + +@Field final String MAVEN_TOOL = 'Apache Maven 3.6' + +// Default node pattern, to be used for resource-intensive stages. +// Should not include the master node. +@Field final String NODE_PATTERN_BASE = 'Slave' +// Quick-use node pattern, to be used for very light, quick, and environment-independent stages, +// such as sending a notification. May include the master node in particular. +@Field final String QUICK_USE_NODE_PATTERN = 'Master||Slave' + +@Field AlternativeMultiMap environments +@Field JobHelper helper + +@Field boolean enableDefaultBuild = false +@Field boolean enableDefaultBuildIT = false +@Field boolean performRelease = false +@Field boolean deploySnapshot = false + +@Field Version releaseVersion +@Field Version afterReleaseDevelopmentVersion + +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: '14', buildJdkTool: 'OpenJDK 14 Latest', + condition: TestCondition.AFTER_MERGE), + // Disabled because of https://bugs.openjdk.java.net/browse/JDK-8253566 + new JdkBuildEnvironment(version: '15', buildJdkTool: 'OpenJDK 15 Latest', + condition: TestCondition.ON_DEMAND), + new JdkBuildEnvironment(version: '16', buildJdkTool: 'OpenJDK 16 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: '90') + ), + 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 and whether a release is being performed. +'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'. +""" + ), + string( + name: 'RELEASE_VERSION', + defaultValue: '', + description: 'The version to be released, e.g. 5.10.0.Final. Setting this triggers a release.', + trim: true + ), + string( + name: 'RELEASE_DEVELOPMENT_VERSION', + defaultValue: '', + description: 'The next version to be used after the release, e.g. 5.10.0-SNAPSHOT.', + trim: true + ), + booleanParam( + name: 'RELEASE_DRY_RUN', + defaultValue: false, + description: 'If true, just simulate the release, without pushing any commits or tags, and without uploading any artifacts or documentation.' + ) + ]) + ]) + + performRelease = (params.RELEASE_VERSION ? true : false) + + if (!performRelease && 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} + performRelease=$performRelease + deploySnapshot=$deploySnapshot +""" + + if (performRelease) { + releaseVersion = Version.parseReleaseVersion(params.RELEASE_VERSION) + echo "Inferred version family for the release to '$releaseVersion.family'" + + // Check that all the necessary parameters are set + if (!params.RELEASE_DEVELOPMENT_VERSION) { + throw new IllegalArgumentException( + "Missing value for parameter RELEASE_DEVELOPMENT_VERSION." + + " This parameter must be set when RELEASE_VERSION is set." + ) + } + if (!params.RELEASE_DRY_RUN && !helper.configuration.file?.deployment?.maven?.settingsId) { + throw new IllegalArgumentException( + "Missing deployment configuration in job configuration file." + + " Cannot deploy artifacts during the release." + ) + } + } + + if (params.RELEASE_DEVELOPMENT_VERSION) { + afterReleaseDevelopmentVersion = Version.parseDevelopmentVersion(params.RELEASE_DEVELOPMENT_VERSION) + } +} + +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) + } +} + +stage('Deploy') { + if (deploySnapshot) { + // TODO delay the release to this stage? This would require to use staging repositories for snapshots, not sure it's possible. + echo "Already deployed snapshot as part of the 'Default build' stage." + } + else if (performRelease) { + echo "Performing full release for version ${releaseVersion.toString()}" + runBuildOnNode { + helper.withMavenWorkspace(mavenSettingsConfig: params.RELEASE_DRY_RUN ? null : helper.configuration.file.deployment.maven.settingsId) { + sh "git clone https://github.com/hibernate/hibernate-noorm-release-scripts.git" + sh "bash -xe hibernate-noorm-release-scripts/prepare-release.sh validator ${releaseVersion.toString()}" + + String deployCommand = "bash -xe hibernate-noorm-release-scripts/deploy.sh validator" + if (!params.RELEASE_DRY_RUN) { + sh deployCommand + } else { + echo "WARNING: Not deploying. Would have executed:" + echo deployCommand + } + + String uploadDistributionCommand = "bash -xe hibernate-noorm-release-scripts/upload-distribution.sh validator ${releaseVersion.toString()}" + String uploadDocumentationCommand = "bash -xe hibernate-noorm-release-scripts/upload-documentation.sh validator ${releaseVersion.toString()} ${releaseVersion.family}" + if (!params.RELEASE_DRY_RUN) { + sh uploadDistributionCommand + sh uploadDocumentationCommand + } + else { + echo "WARNING: Not uploading anything. Would have executed:" + echo uploadDistributionCommand + echo uploadDocumentationCommand + } + + sh "bash -xe hibernate-noorm-release-scripts/update-version.sh validator ${afterReleaseDevelopmentVersion.toString()}" + sh "bash -xe hibernate-noorm-release-scripts/push-upstream.sh validator ${releaseVersion.toString()} ${helper.scmSource.branch.name} ${!params.RELEASE_DRY_RUN}" + } + } + } + else { + echo "Skipping deployment" + helper.markStageSkipped() + return + } +} + +} // End of helper.runWithNotification + +// Job-specific helpers + +enum TestCondition { + // For environments that are expected to work correctly + // before merging into master or maintenance branches. + // Tested on master 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 master 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 (params.RELEASE_VERSION) { + echo "Releasing version '$params.RELEASE_VERSION'." + } else 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..51cc66a76f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Hibernate Validator -*Version: 6.0.9.Final - 27-03-2018* +*Version: 6.2.0.Final - 23-12-2020* ## What is it? -This is the reference implementation of [JSR-380 - Bean Validation 2.0](http://beanvalidation.org/). -Bean Validation defines a metadata model and API for JavaBean as well as method validation. +This is the reference implementation of [Jakarta Bean Validation 2.0](http://beanvalidation.org/). +Jakarta Bean Validation defines a metadata model and API for JavaBean as well as method validation. The default metadata source are annotations, with the ability to override and extend -the meta-data through the use of XML validation descriptors. +the metadata through the use of XML validation descriptors. ## Documentation @@ -20,7 +20,7 @@ The full list of changes for this release can be found in changelog.txt. ## System Requirements -JDK 1.8 or above. +JDK 8 or above. ## Using Hibernate Validator @@ -30,31 +30,31 @@ the JBoss Logging API, an abstraction layer which supports several logging solut provided by the JDK) as implementation. Just add a supported logging library to the classpath (e.g. _log4j-<version>.jar_) and JBoss Logging will delegate any log requests to that provider. -* Add the following to your Maven or Ivy dependency list +* Add the following artifact to your Maven/Ivy/Gradle dependency list: org.hibernate.validator hibernate-validator - 6.0.9.Final + 6.2.0.Final You also need an API and implementation of the Unified Expression Language. These dependencies must be explicitly added in an SE environment. - In an EE environment they are often already provided. + In a Jakarta EE environment, they are often already provided. org.glassfish - javax.el - 3.0.1-b09 + jakarta.el + 3.0.3 -* Bean Validation defines integration points with [CDI](http://jcp.org/en/jsr/detail?id=346). If your application runs +* Jakarta Bean Validation defines integration points with [CDI](http://jcp.org/en/jsr/detail?id=346). If your application runs in an environment which does not provide this integration out of the box, you may use the Hibernate Validator CDI portable extension by adding the following dependency: org.hibernate.validator hibernate-validator-cdi - 6.0.9.Final + 6.2.0.Final * _hibernate-validator-annotation-processor-<version>.jar_ is an optional jar which can be integrated with your build @@ -63,34 +63,18 @@ documentation](https://docs.jboss.org/hibernate/stable/validator/reference/en-US ## Licensing -Hibernate Validator itself as well as the Bean Validation API and TCK are all provided and distributed under +Hibernate Validator itself as well as the Jakarta Bean Validation API and TCK are all provided and distributed under the Apache Software License 2.0. Refer to license.txt for more information. ## Build from Source -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: +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/). -### Build on JDK 9 - -To build Hibernate Validator with JDK 9, export the following environment variable: - - export MAVEN_OPTS="--add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-modules=java.xml.bind" - -Then the build can be started like this: - - mvn -s settings-example.xml clean install - -Here are the reasons why we added the various build options: - - * add-opens java.security: required by wildfly-maven-plugin:execute-commands (for the WildFly integration tests and the TCK runner running in container mode) - * add-opens java.lang: required by JRuby for Asciidoc processing - * add-modules java.xml.bind: required by the forbiddenapis Maven plugin - ## Continuous Integration The official Continuous Integration service for the project is hosted on [ci.hibernate.org](http://ci.hibernate.org/view/Validator/). @@ -100,7 +84,7 @@ We provide a `.travis.yml` file so that you can enable CI for your GitHub fork b ## Hibernate Validator URLs * [Home Page](http://hibernate.org/validator/) -* [Bean Validation Home](http://beanvalidation.org/) +* [Jakarta Bean Validation Home](http://beanvalidation.org/) * [Downloads](http://hibernate.org/validator/downloads/) * [Mailing Lists](http://hibernate.org/community/) * [Issue Tracking](https://hibernate.atlassian.net/browse/HV) diff --git a/annotation-processor/pom.xml b/annotation-processor/pom.xml index 3687fd8c44..decee91936 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.2.0.Final ../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..bfd0b0170f 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) { @@ -182,11 +182,11 @@ public Boolean visitExecutableAsConstructor(ExecutableElement constructorElement * * @param typeMirror The {@code TypeMirror} instance. * - * @return The generic type or {@code null} if the given type doesn't implement the {@link org.hibernate.validator.group.DefaultGroupSequenceProvider} interface. + * @return The generic type or {@code null} if the given type doesn't implement the {@link org.hibernate.validator.spi.group.DefaultGroupSequenceProvider} interface. */ 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/CollectionHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/CollectionHelper.java index f9396fb377..b6e4c52e89 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/CollectionHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/CollectionHelper.java @@ -40,13 +40,13 @@ public static ArrayList newArrayList() { return new ArrayList(); } + @SafeVarargs public static Set asSet(T... ts) { - return new HashSet( Arrays.asList( ts ) ); } + @SafeVarargs public static Set asTreeSet(T... ts) { - return new TreeSet( Arrays.asList( ts ) ); } 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..52861f67a7 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,8 @@ */ package org.hibernate.validator.ap.internal.util; -import java.text.MessageFormat; import java.util.Map; + import javax.annotation.processing.Messager; import javax.tools.Diagnostic.Kind; @@ -97,11 +97,11 @@ 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, StringHelper.format( + "The given value %1$s is no valid diagnostic kind. %2$s will be used.", + diagnosticKindFromOptions, + DEFAULT_DIAGNOSTIC_KIND + ) ); } } @@ -118,10 +118,10 @@ 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, StringHelper.format( + "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..e0c6548fa9 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; @@ -254,6 +254,7 @@ public ConstraintHelper(Types typeUtils, AnnotationApiHelper annotationApiHelper registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DECIMAL_MIN, Number.class, String.class ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DECIMAL_MIN, JavaMoneyTypes.MONETARY_AMOUNT ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DIGITS, Number.class, String.class ); + registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.DIGITS, JavaMoneyTypes.MONETARY_AMOUNT ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.EMAIL, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.FUTURE, Calendar.class, Date.class ); registerAllowedTypesForBuiltInConstraint( BeanValidationTypes.FUTURE, JodaTypes.READABLE_PARTIAL, JodaTypes.READABLE_INSTANT ); @@ -301,9 +302,10 @@ public ConstraintHelper(Types typeUtils, AnnotationApiHelper annotationApiHelper registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.REGON_CHECK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NIP_CHECK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.PESEL_CHECK, CharSequence.class ); + registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.INN_CHECK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NOT_BLANK, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NOT_EMPTY, TYPES_SUPPORTED_BY_SIZE_AND_NOT_EMPTY_ANNOTATIONS ); - registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.SAFE_HTML, CharSequence.class ); + registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NORMALIZED, CharSequence.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.SCRIPT_ASSERT, Object.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.UNIQUE_ELEMENTS, Collection.class ); registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.URL, CharSequence.class ); @@ -394,7 +396,7 @@ public List getPartsOfMultiValuedConstraint( .getAnnotationArrayValue( annotationMirror, "value" ) ) { oneValuePart.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Void visitAnnotation(AnnotationMirror a, Void p) { @@ -454,7 +456,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 +560,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 +649,7 @@ private boolean isMultiValuedConstraint(AnnotationMirror annotationMirror) { for ( AnnotationValue oneAnnotationValue : annotationArrayValue ) { Boolean isConstraintAnnotation = oneAnnotationValue.accept( - new SimpleAnnotationValueVisitor6() { + new SimpleAnnotationValueVisitor8() { @Override public Boolean visitAnnotation( @@ -797,7 +799,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 +820,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 +872,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 +891,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 +900,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 +927,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 +946,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 +1011,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/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java index ea30a1486b..eb95e5c10d 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/StringHelper.java @@ -102,4 +102,7 @@ private static boolean startsWithSeveralUpperCaseLetters(String string) { Character.isUpperCase( string.charAt( 1 ) ); } + public static String format(String format, Object... args) { + return String.format( Locale.ROOT, format, args ); + } } diff --git a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java index b2a1ef1c9c..94fc1a4cb4 100644 --- a/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java +++ b/annotation-processor/src/main/java/org/hibernate/validator/ap/internal/util/TypeNames.java @@ -78,9 +78,10 @@ public static class HibernateValidatorTypes { public static final String REGON_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.REGON"; public static final String NIP_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.NIP"; public static final String PESEL_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.PESEL"; + public static final String INN_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".ru.INN"; + public static final String NORMALIZED = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".Normalized"; public static final String NOT_BLANK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".NotBlank"; public static final String NOT_EMPTY = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".NotEmpty"; - public static final String SAFE_HTML = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".SafeHtml"; public static final String SCRIPT_ASSERT = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".ScriptAssert"; public static final String UNIQUE_ELEMENTS = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".UniqueElements"; public static final String URL = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".URL"; diff --git a/annotation-processor/src/main/resources/META-INF/services/gradle/incremental.annotation.processors b/annotation-processor/src/main/resources/META-INF/services/gradle/incremental.annotation.processors new file mode 100644 index 0000000000..7148eed480 --- /dev/null +++ b/annotation-processor/src/main/resources/META-INF/services/gradle/incremental.annotation.processors @@ -0,0 +1 @@ +org.hibernate.validator.ap.ConstraintValidationProcessor,isolating diff --git a/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties b/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties index 9988c9eab4..cec98220cd 100644 --- a/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties +++ b/annotation-processor/src/main/resources/org/hibernate/validator/ap/ValidationProcessorMessages.properties @@ -48,6 +48,6 @@ INVALID_GROUP_SEQUENCE_VALUE_NOT_INTERFACES=Invalid @GroupSequence configuration INVALID_GROUP_SEQUENCE_VALUE_CYCLIC_DEFINITION=Invalid @GroupSequence configuration. The defined group sequence should be expandable (no cyclic definition). INVALID_GROUP_SEQUENCE_VALUE_MISSING_HOSTING_BEAN_DECLARATION=Invalid default group sequence redefinition. The value should contain the hosting bean class. INVALID_GROUP_SEQUENCE_VALUE_MULTIPLE_DECLARATIONS_OF_THE_SAME_INTERFACE=Invalid @GroupSequence configuration. {0} was already declared in this group sequence. -INVALID_GROUP_SEQUENCE_EXTEND_INTERFACES=Having group sequences extending other interfaces is discouraged by the Bean Validation specification. +INVALID_GROUP_SEQUENCE_EXTEND_INTERFACES=Having group sequences extending other interfaces is discouraged by the Jakarta Bean Validation specification. MIXED_LIST_AND_DIRECT_ANNOTATION_DECLARATION=Constraint @{0} is declared both directly and as a list. Which is not allowed. INVALID_PAYLOAD_UNWRAPPING_VALUE_ANNOTATION_PARAMETERS=Having both Unwrapping.Unwrap and Unwrapping.Skip in the payload is not allowed. diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java index 289aef25c7..8fdad4b55f 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/ConstraintValidationProcessorTest.java @@ -26,6 +26,7 @@ import org.hibernate.validator.ap.testmodel.ModelWithJava8DateTime; import org.hibernate.validator.ap.testmodel.ModelWithJavaMoneyTypes; import org.hibernate.validator.ap.testmodel.ModelWithJodaTypes; +import org.hibernate.validator.ap.testmodel.ModelWithNormalizedConstraints; import org.hibernate.validator.ap.testmodel.ModelWithUniqueElementsConstraints; import org.hibernate.validator.ap.testmodel.ModelWithoutConstraints; import org.hibernate.validator.ap.testmodel.MultipleConstraintsOfSameType; @@ -116,19 +117,19 @@ public void overridingMethodParameterConstraintsTest() { assertFalse( compilationResult ); assertThatDiagnosticsMatch( diagnostics, - new DiagnosticExpectation( Kind.ERROR, 39 ), - new DiagnosticExpectation( Kind.ERROR, 56 ), - new DiagnosticExpectation( Kind.ERROR, 71 ), - new DiagnosticExpectation( Kind.ERROR, 90 ), - new DiagnosticExpectation( Kind.ERROR, 144 ), - new DiagnosticExpectation( Kind.ERROR, 152 ), - new DiagnosticExpectation( Kind.ERROR, 169 ), - new DiagnosticExpectation( Kind.ERROR, 191 ), - new DiagnosticExpectation( Kind.ERROR, 219 ), - new DiagnosticExpectation( Kind.ERROR, 373 ), - new DiagnosticExpectation( Kind.ERROR, 387 ), - new DiagnosticExpectation( Kind.ERROR, 409 ), - new DiagnosticExpectation( Kind.ERROR, 434 ) + new DiagnosticExpectation( Kind.ERROR, 38 ), + new DiagnosticExpectation( Kind.ERROR, 55 ), + new DiagnosticExpectation( Kind.ERROR, 70 ), + new DiagnosticExpectation( Kind.ERROR, 89 ), + new DiagnosticExpectation( Kind.ERROR, 143 ), + new DiagnosticExpectation( Kind.ERROR, 151 ), + new DiagnosticExpectation( Kind.ERROR, 168 ), + new DiagnosticExpectation( Kind.ERROR, 190 ), + new DiagnosticExpectation( Kind.ERROR, 218 ), + new DiagnosticExpectation( Kind.ERROR, 372 ), + new DiagnosticExpectation( Kind.ERROR, 386 ), + new DiagnosticExpectation( Kind.ERROR, 408 ), + new DiagnosticExpectation( Kind.ERROR, 433 ) ); assertEquals( diagnostics.getDiagnostics().get( 0 ).getMessage( Locale.getDefault() ), @@ -154,6 +155,8 @@ public void hibernateValidatorProvidedCustomConstraints() { assertFalse( compilationResult ); assertThatDiagnosticsMatch( diagnostics, + new DiagnosticExpectation( Kind.ERROR, 66 ), + new DiagnosticExpectation( Kind.ERROR, 67 ), new DiagnosticExpectation( Kind.ERROR, 68 ), new DiagnosticExpectation( Kind.ERROR, 69 ), new DiagnosticExpectation( Kind.ERROR, 70 ), @@ -170,10 +173,7 @@ public void hibernateValidatorProvidedCustomConstraints() { new DiagnosticExpectation( Kind.ERROR, 81 ), new DiagnosticExpectation( Kind.ERROR, 82 ), new DiagnosticExpectation( Kind.ERROR, 83 ), - new DiagnosticExpectation( Kind.ERROR, 84 ), - new DiagnosticExpectation( Kind.ERROR, 85 ), - new DiagnosticExpectation( Kind.ERROR, 86 ), - new DiagnosticExpectation( Kind.ERROR, 87 ) + new DiagnosticExpectation( Kind.ERROR, 84 ) ); } @@ -724,6 +724,25 @@ public void codePointLengthConstraints() { ); } + @Test + @TestForIssue(jiraKey = "HV-1780") + public void normalizedConstraints() { + File[] sourceFiles = new File[] { + compilerHelper.getSourceFile( ModelWithNormalizedConstraints.class ) + }; + + boolean compilationResult = + compilerHelper.compile( new ConstraintValidationProcessor(), diagnostics, false, true, sourceFiles ); + + assertFalse( compilationResult ); + assertThatDiagnosticsMatch( + diagnostics, + new DiagnosticExpectation( Kind.ERROR, 17 ), + new DiagnosticExpectation( Kind.ERROR, 20 ), + new DiagnosticExpectation( Kind.ERROR, 23 ) + ); + } + @Test public void isbnConstraints() { File[] sourceFiles = new File[] { diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java index 67268ae7f8..2dd5279f51 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithJavaMoneyTypes.java @@ -9,6 +9,7 @@ import javax.money.MonetaryAmount; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Digits; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.Negative; @@ -29,6 +30,7 @@ public class ModelWithJavaMoneyTypes { @DecimalMin("0.00") @Positive @PositiveOrZero + @Digits(integer = 6, fraction = 2) public MonetaryAmount monetaryAmount; @Max(1000L) diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithNormalizedConstraints.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithNormalizedConstraints.java new file mode 100644 index 0000000000..2551e5189d --- /dev/null +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/ModelWithNormalizedConstraints.java @@ -0,0 +1,28 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.ap.testmodel; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.hibernate.validator.constraints.Normalized; + +public class ModelWithNormalizedConstraints { + + @Normalized + public Collection collection; + + @Normalized + public List list; + + @Normalized + public Set set; + + @Normalized + public String string; +} diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java index 587fecec97..75d3706624 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/customconstraints/HibernateValidatorProvidedCustomConstraints.java @@ -19,7 +19,6 @@ import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotEmpty; 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.br.CNPJ; @@ -48,7 +47,6 @@ public class HibernateValidatorProvidedCustomConstraints { @NotBlank @NotEmpty @Range - @SafeHtml @URL @CNPJ @CPF @@ -75,7 +73,6 @@ public class HibernateValidatorProvidedCustomConstraints { @NotBlank @NotEmpty @Range - @SafeHtml @URL @CNPJ @CPF diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java index f1df05454d..5d5a0149aa 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/overriding/MethodOverridingTests.java @@ -17,11 +17,10 @@ import javax.validation.constraints.Max; import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import org.hibernate.validator.constraints.NotBlank; - public class MethodOverridingTests { /** diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java index c123f0080f..36e785deb8 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testutil/CompilerTestHelper.java @@ -6,6 +6,9 @@ */ package org.hibernate.validator.ap.testutil; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -28,11 +31,9 @@ import org.hibernate.validator.ap.internal.util.CollectionHelper; import org.hibernate.validator.ap.internal.util.Configuration; +import org.hibernate.validator.ap.internal.util.StringHelper; import org.hibernate.validator.ap.util.DiagnosticExpectation; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; - /** * Infrastructure for unit tests based on the Java Compiler API. * @@ -49,7 +50,7 @@ public enum Library { HIBERNATE_VALIDATOR( "hibernate-validator.jar" ), - VALIDATION_API( "validation-api.jar" ), + VALIDATION_API( "jakarta.validation-api.jar" ), JODA_TIME( "joda-time.jar" ), @@ -180,16 +181,16 @@ public boolean compile(Processor annotationProcessor, List options = new ArrayList(); if ( diagnosticKind != null ) { - options.add( String.format( "-A%s=%s", Configuration.DIAGNOSTIC_KIND_PROCESSOR_OPTION, diagnosticKind ) ); + options.add( StringHelper.format( "-A%s=%s", Configuration.DIAGNOSTIC_KIND_PROCESSOR_OPTION, diagnosticKind ) ); } if ( verbose != null ) { - options.add( String.format( "-A%s=%b", Configuration.VERBOSE_PROCESSOR_OPTION, verbose ) ); + options.add( StringHelper.format( "-A%s=%b", Configuration.VERBOSE_PROCESSOR_OPTION, verbose ) ); } if ( allowMethodConstraints != null ) { options.add( - String.format( + StringHelper.format( "-A%s=%b", Configuration.METHOD_CONSTRAINTS_SUPPORTED_PROCESSOR_OPTION, allowMethodConstraints diff --git a/build-config/pom.xml b/build-config/pom.xml index 6e3d9a3cff..38d4b9037a 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.2.0.Final ../pom.xml diff --git a/build-config/src/main/resources/checkstyle.xml b/build-config/src/main/resources/checkstyle.xml index dbe0bfd190..df0f8a3a0f 100644 --- a/build-config/src/main/resources/checkstyle.xml +++ b/build-config/src/main/resources/checkstyle.xml @@ -10,8 +10,9 @@ + + - 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..d87c29bfa5 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.2.0.Final ../pom.xml @@ -33,18 +33,18 @@ hibernate-validator - javax.annotation - javax.annotation-api + jakarta.annotation + jakarta.annotation-api provided - org.jboss.spec.javax.interceptor - jboss-interceptors-api_1.2_spec + jakarta.interceptor + jakarta.interceptor-api provided - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api provided @@ -63,12 +63,12 @@ org.glassfish - javax.el + jakarta.el test - log4j - log4j + org.apache.logging.log4j + log4j-core test @@ -90,6 +90,28 @@ org.jboss.weld weld-core-impl test + + + javax.enterprise + cdi-api + + + org.jboss.spec.javax.el + jboss-el-api_3.0_spec + + + org.jboss.spec.javax.interceptor + + jboss-interceptors-api_1.2_spec + + + + org.jboss.spec.javax.annotation + + jboss-annotations-api_1.3_spec + + + org.jboss.shrinkwrap.resolver @@ -152,7 +174,7 @@ ${project.build.outputDirectory}/META-INF/MANIFEST.MF - Bean Validation + Jakarta Bean Validation 2.0 ${hibernate-validator-cdi.module-name} @@ -166,8 +188,8 @@ maven-bundle-plugin - ${hibernate-validator-cdi.bundle-name} - ${hibernate-validator.bundle-name} + ${hibernate-validator-cdi.module-name} + ${hibernate-validator.module-name} javax.validation.*;version="[2.0,3.0)", javax.annotation.*;version="[1.2,2.0)", @@ -214,7 +236,7 @@ - --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED diff --git a/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java b/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java index 45c5ef63f6..4be3dab84e 100644 --- a/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java +++ b/cdi/src/main/java/org/hibernate/validator/cdi/ValidationExtension.java @@ -17,6 +17,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; import javax.enterprise.event.Observes; @@ -52,9 +53,9 @@ import org.hibernate.validator.cdi.internal.ValidatorFactoryBean; import org.hibernate.validator.cdi.internal.interceptor.ValidationEnabledAnnotatedType; import org.hibernate.validator.cdi.internal.interceptor.ValidationInterceptor; +import org.hibernate.validator.cdi.internal.util.GetterPropertySelectionStrategyHelper; import org.hibernate.validator.internal.util.Contracts; 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.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; @@ -99,6 +100,7 @@ public class ValidationExtension implements Extension { */ private final Validator validator; private final ValidatorFactory validatorFactory; + private final GetterPropertySelectionStrategyHelper getterPropertySelectionStrategyHelper; private final Set globalExecutableTypes; private final boolean isExecutableValidationEnabled; @@ -119,6 +121,7 @@ public ValidationExtension() { isExecutableValidationEnabled = bootstrap.isExecutableValidationEnabled(); validatorFactory = config.buildValidatorFactory(); validator = validatorFactory.getValidator(); + getterPropertySelectionStrategyHelper = GetterPropertySelectionStrategyHelper.forValidationFactory( validatorFactory ); executableHelper = new ExecutableHelper( new TypeResolutionHelper() ); } @@ -259,15 +262,16 @@ private void determineConstrainedMethods(AnnotatedType type, BeanDescript for ( AnnotatedMethod annotatedMethod : type.getMethods() ) { Method method = annotatedMethod.getJavaMember(); - boolean isGetter = ReflectionHelper.isGetterMethod( method ); + Optional correspondingProperty = getterPropertySelectionStrategyHelper.getProperty( method ); // obtain @ValidateOnExecution from the top-most method in the hierarchy Method methodForExecutableTypeRetrieval = replaceWithOverriddenOrInterfaceMethod( method, overriddenAndImplementedMethods ); EnumSet classLevelExecutableTypes = executableTypesDefinedOnType( methodForExecutableTypeRetrieval.getDeclaringClass() ); - EnumSet memberLevelExecutableType = executableTypesDefinedOnMethod( methodForExecutableTypeRetrieval, isGetter ); + EnumSet memberLevelExecutableType = executableTypesDefinedOnMethod( methodForExecutableTypeRetrieval, + correspondingProperty.isPresent() ); - ExecutableType currentExecutableType = isGetter ? ExecutableType.GETTER_METHODS : ExecutableType.NON_GETTER_METHODS; + ExecutableType currentExecutableType = correspondingProperty.isPresent() ? ExecutableType.GETTER_METHODS : ExecutableType.NON_GETTER_METHODS; // validation is enabled per default, so explicit configuration can just veto whether // validation occurs @@ -276,11 +280,11 @@ private void determineConstrainedMethods(AnnotatedType type, BeanDescript } boolean needsValidation; - if ( isGetter ) { - needsValidation = isGetterConstrained( method, beanDescriptor ); + if ( correspondingProperty.isPresent() ) { + needsValidation = isGetterConstrained( beanDescriptor, method, correspondingProperty.get() ); } else { - needsValidation = isNonGetterConstrained( method, beanDescriptor ); + needsValidation = isNonGetterConstrained( beanDescriptor, method ); } if ( needsValidation ) { @@ -307,13 +311,12 @@ private void determineConstrainedConstructors(AnnotatedType type, BeanDes } } - private boolean isNonGetterConstrained(Method method, BeanDescriptor beanDescriptor) { + private boolean isNonGetterConstrained(BeanDescriptor beanDescriptor, Method method) { return beanDescriptor.getConstraintsForMethod( method.getName(), method.getParameterTypes() ) != null; } - private boolean isGetterConstrained(Method method, BeanDescriptor beanDescriptor) { - String propertyName = ReflectionHelper.getPropertyName( method ); - PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( propertyName ); + private boolean isGetterConstrained(BeanDescriptor beanDescriptor, Method method, String property) { + PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( property ); return propertyDescriptor != null && propertyDescriptor.findConstraints() .declaredOn( ElementType.METHOD ) .hasConstraints(); 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/internal/util/GetterPropertySelectionStrategyHelper.java b/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertySelectionStrategyHelper.java new file mode 100644 index 0000000000..609e53860e --- /dev/null +++ b/cdi/src/main/java/org/hibernate/validator/cdi/internal/util/GetterPropertySelectionStrategyHelper.java @@ -0,0 +1,70 @@ +/* + * 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.internal.util; + +import java.lang.reflect.Method; +import java.util.Optional; + +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.HibernateValidatorFactory; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; + +/** + * A wrapper around {@link GetterPropertySelectionStrategy}. + * + * @author Marko Bekhta + */ +public class GetterPropertySelectionStrategyHelper { + + private final GetterPropertySelectionStrategy getterPropertySelectionStrategy; + + private GetterPropertySelectionStrategyHelper(GetterPropertySelectionStrategy getterPropertySelectionStrategy) { + this.getterPropertySelectionStrategy = getterPropertySelectionStrategy; + } + + public Optional getProperty(Method method) { + return getterPropertySelectionStrategy.getProperty( new ConstrainableMethod( method ) ); + } + + public static GetterPropertySelectionStrategyHelper forValidationFactory(ValidatorFactory factory) { + GetterPropertySelectionStrategy getterPropertySelectionStrategy; + if ( factory instanceof HibernateValidatorFactory ) { + getterPropertySelectionStrategy = factory.unwrap( HibernateValidatorFactory.class ).getGetterPropertySelectionStrategy(); + } + else { + getterPropertySelectionStrategy = new DefaultGetterPropertySelectionStrategy(); + } + return new GetterPropertySelectionStrategyHelper( getterPropertySelectionStrategy ); + } + + private static class ConstrainableMethod implements ConstrainableExecutable { + + private final Method method; + + private ConstrainableMethod(Method method) { + this.method = method; + } + + @Override + public Class getReturnType() { + return method.getReturnType(); + } + + @Override + public String getName() { + return method.getName(); + } + + @Override + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + } +} 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/ValidationExtensionTest.java b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/ValidationExtensionTest.java index 3c6720495d..e861a915ff 100644 --- a/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/ValidationExtensionTest.java +++ b/cdi/src/test/java/org/hibernate/validator/test/cdi/internal/ValidationExtensionTest.java @@ -138,6 +138,7 @@ public void testRegisterBeanWithCustomQualifier() { @Override public Object answer() throws Throwable { + @SuppressWarnings("unchecked") ProcessBean event = getProcessBeanEvent( (Bean) EasyMock.getCurrentArguments()[0] ); extension.processBean( event ); return null; @@ -151,6 +152,7 @@ public Object answer() throws Throwable { @Override public Object answer() throws Throwable { + @SuppressWarnings("unchecked") ProcessBean event = getProcessBeanEvent( (Bean) EasyMock.getCurrentArguments()[0] ); extension.processBean( event ); return null; 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..7d24c8fdd3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,354 @@ Hibernate Validator Changelog ============================= +6.2.0.Final (23-12-2020) +------------------------- + +** Bug + * HV-1821 - engine - HV-1755 introduces NPE in org.hibernate.validator.internal.engine.ValidatorFactoryImpl constructor + +** New Feature + * HV-1822 - validators - Add Russian specific validator for russian taxpayer identification number + +6.2.0.CR1 (07-12-2020) +------------------------- + +** Improvement + * HV-1812 - engine - Avoid reflection-based metadata extraction for built-in value extractors + +** New Feature + * HV-1816 - engine - Disable Expression Language by default for custom constraint violations + +** Remove Feature + * HV-1790 - engine, validators - Remove the SafeHtml constraint + +** Task + * HV-1820 - engine - Upgrade JBoss Logging to 3.4.1.Final + * HV-1819 - engine - Upgrade Classmate to 1.5.1 + * HV-1817 - build - Upgrade to checkstyle 8.38 + +6.1.6.Final (30-09-2020) +------------------------- + +** Bug + * HV-1804 - translations - Fix Dutch translation for @Size constraint + * HV-1797 - validators - Validation on classes with a bidirectional relationship cause stack overflow on 6.1.x + * HV-1761 - engine - Interpolation of primitive arrays causes a ClassCastException + +** Improvement + * HV-1782 - translations - Remove trailing dot from @Max constraint German translation + +** New Feature + * HV-1780 - validators - Add @Normalized contraint that validates if text is normalized in a given form + +** Task + * HV-1803 - tests - Move the tests to log4j2 + * HV-1802 - tests - Update ByteBuddy test dependency to 1.10.16 + * HV-1795 - build - Remove link to JavaMoney javadoc + +6.1.5.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 + * HV-1771 - translations - Fix DecimalMin message German translation + +** Improvement + * HV-1773 - documentation - Be more explicit about issues with EL injection and how to avoid them + +6.1.4.Final (17-04-2020) +------------------------- + +** Bug + * HV-1760 - validators - @Negative*/@Positive* do not support CharSequence as documented + +** Improvement + * HV-1770 - engine - Relax constraint consistency checking for built-in constraints + * HV-1769 - engine - Only create the default TraversableResolver if none has been specified + * HV-1767 - engine - Reduce the overhead of ConstraintHelper initialization in the predefined scope case + +6.1.3.Final (10-04-2020) +------------------------- + +** Bug + * HV-1758 - translations - Extra dollar sign in validation messages for ModCheck + +** Improvement + * HV-1763 - engine - Improve performances of ExecutableHelper#getSignature + +** New Feature + * HV-1755 - engine - Introduce the notion of BeanMetaDataClassNormalizer in the standard ValidatorFactory + +** Task + * HV-1765 - integration - Upgrade WildFly versions to 18.0.1.Final and 19.0.0.Final + * HV-1764 - tests - Upgrade Jackson test dependencies to 2.10.3 + +6.1.2.Final (31-01-2020) +------------------------- + +** Bug + * HV-1756 - translations - Incorrect variables in the newly added translations + +** Task + * HV-1753 - tests - Force Pax-Exam and Karaf to use Maven Central repository with SSL enabled + +6.1.1.Final (15-01-2020) +------------------------- + +** Improvement + * HV-1750 - engine - Add debug log when expression factory has failed to load + * HV-1747 - engine - Allow overriding the message interpolator in PredefinedScopeValidatorFactory context + * HV-1744 - documentation - Use auto-expanding entries in the documentation TOC + +** New Feature + * HV-1749 - engine - Introduce a LocaleResolver SPI + * HV-1748 - engine - Improve localization support via Locale.LanguageRange + +** Task + * HV-1745 - tests - Bump Jackson test dependency to 2.10.1 + +6.1.0.Final (25-10-2019) +------------------------- + +** Bug + * HV-1730 - engine - JavaBeanExecutable fails to initialize for enum type + * 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-1743 - build - Upgrade maven-compiler-plugin to 3.8.1 + * HV-1742 - build - Upgrade to WildFly 18.0.0.Final + * HV-1741 - build - Upgrade ByteBuddy test dependency to 1.10.2 + * HV-1740 - engine - Deprecate @SafeHtml + * HV-1739 - engine - CVE-2019-10219 Security issue with @SafeHtml + * HV-1738 - build - Update Jackson test dependency to 2.9.10 + * HV-1733 - tests - Fix locale settings of PredefinedScopeValidatorFactoryTest + * HV-1732 - build - Change tarLongFileMode to posix for assembly building + * HV-1731 - tck-runner - Move TCK signature check to tck-runner module + * HV-1728 - build - Upgrade to WildFly 17.0.1.Final + * HV-1727 - build - Update Jackson Databind test dependency to 2.9.9.2 + * HV-1725 - build - Switch to using Jakarta EE artifacts + * HV-1724 - build - Update to OpenJFX 11.0.2 + * HV-1680 - engine - Avoid reflection by using instrumentation - build the enhancer + +6.1.0.Alpha6 (19-07-2019) +------------------------- + +** Bug + * HV-1722 - engine - Remove settings-example.xml reference from .travis.yml + * HV-1721 - engine - Take into account Hibernate Validator-specific configuration with PredefinedScopeValidatorFactoryImpl + * HV-1720 - engine - Support bounded wildcard types in container value unwrapping + +** New Feature + * HV-1723 - validators - Provide a DigitsValidatorForMonetaryAmount to support @Digits on MonetaryAmounts + +** Task + * HV-1726 - engine - Make PredefinedScopeHibernateValidatorFactory extend HibernateValidatorFactory + +6.1.0.Alpha5 (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 + +** Improvement + * HV-1719 - engine - Accept setting per-validator TraversableResolver with PredefinedScopeValidatorFactoryImpl + * HV-1708 - translations - Add Danish translations of validation messages + * HV-1707 - validators - Add ISBN.Type.ANY + +** New Feature + * HV-823 - engine - Provide contract for customization of property names in constraint violation + +** Task + * HV-1718 - engine - Predefined scope ValidatorFactory: uninitialized bean class shouldn't throw an exception + * HV-1717 - build - Update test dependencies + * HV-1716 - build - Update WildFly secondary version to 17.0.0.Beta1 + * HV-1712 - engine - Add org.hibernate.validator.metadata to OSGi manifest + * HV-1711 - build - Fix aggregated javadoc build with recent JDK 11 + * HV-1710 - build - Remove settings-example.xml + +6.1.0.Alpha4 (22-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-1701 - - Add Max/MinValidatorForInteger + * HV-1700 - tests - Remove reference of absent valueextractor from test resources + * HV-1697 - engine - Reduce memory allocation of ValidationContexts + * 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-1702 - build - Upgrade to checkstyle 8.18 and maven-checkstyle-plugin 3.0.0 + * HV-1693 - build - Test compatibility with JDK 12 + +6.1.0.Alpha3 (18-02-2019) +------------------------- + +** Bug + * HV-1692 - engine - Custom group sequence might cause StackOverflowError on objects with cycles + * HV-1684 - validators - StackOverflowError with Hibernate Validator 6.0.13.Final + +** Improvement + * HV-1691 - engine - IndexOutOfBoundsException in PathImpl + * HV-1689 - engine - In GetInstancesFromServiceLoader, do not hide ServiceConfigurationError + * HV-1687 - engine - Reduce bootstrap log verbosity + * HV-1686 - translations - Fix a few typos in the Dutch translation + * HV-1683 - build - Upgrade javadoc plugin + +** New Feature + * HV-1657 - engine - Make the “propertyPath” available via the “HibernateMessageInterpolatorContext” + +** Task + * HV-1685 - integration - Upgrade the WildFly versions we generate patches for to 14.0.1.Final and 15.0.0.Final + +6.1.0.Alpha2 (19-12-2018) +------------------------- + +** Bug + * HV-1681 - engine - PredefinedScopeValidatorFactory and @Valid on unregistered bean throws a NPE + +** Improvement + * HV-1651 - translations - Contribute additional language translations from OpenLiberty + +** New Feature + * HV-1682 - engine - Provide a way to normalize the class before getting the bean metadata + +6.1.0.Alpha1 (11-12-2018) +------------------------- + +** Bug + * HV-1650 - validators - French translations are badly encoded + * 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 + * HV-1637 - translations - PropertyNotFoundException for @DecimalMax when using the German translation + * HV-1634 - engine - Deal with synthetic and implicit parameters properly when getting the generic type of a parameter + * HV-1510 - validators - @NotNull doesn't work in 50% when annotated method is extended from multiple classes + * HV-1450 - engine - BeanMetaDataImpl.BeanMetaDataBuilder#build() can choose ConstraintMetaData w/o constraints + +** Improvement + * HV-1662 - engine - Extract BeanMetaDataBuilder to its own class file + * HV-1661 - engine - Remove impossible case from ValidatorImpl#buildNewLocalExecutionContext() + * HV-1656 - translations - Add translation of Validation Messages for Japanese language + * HV-1653 - engine - Improve the javadoc of ParameterScriptAssert + * HV-1643 - translations - Fix Russian translation for @Null constraint + * HV-1636 - engine - Avoid instantiating unnecessary objects during constraint metadata creation + * HV-1631 - engine - Avoid doing two lookups in the read methods of AnnotationProcessingOptionsImpl + * HV-1630 - engine - Introduce StringHelper.format() + * HV-1629 - engine - Fix compiler warnings + * HV-1628 - annotation-processor, engine, tests - Configure a stricter forbidden-apis policy and remove calls deprecated in Java 10 + * HV-1626 - build - Remove useless Maven plugins and extensions + * HV-1623 - engine - Build an abstraction over reflection in engine + * HV-1622 - integration - Update the WildFly integration tests to use WildFly 13 + * HV-1617 - build - Make our pom files more consistent with WildFly and JBoss parent pom files + * HV-1599 - engine - Avoid creating later discarded violations when reportAsSingleViolation is true + * HV-1526 - engine - Create separate validation context for different validation kinds + * HV-1484 - build - Unify JPMS module names and OSGi bundle names + * HV-667 - engine - Consider to create descriptor model lazily + +** New Feature + * HV-1671 - engine - Allow to retrieve all the built-in constraint annotations from ConstraintHelper + * HV-1670 - engine - Add the ability to preload a set of locales + * HV-1667 - engine - Create a predefined scope ValidatorFactory which initializes things eagerly + * HV-1363 - engine - Support for non-standard Java beans + +** Remove Feature + * HV-1624 - engine - Remove the StaticFieldELResolver + +** Task + * HV-1677 - engine - Allow for an explicit environment variable to disable the JavaFX extensions + * HV-1674 - engine - Make the name of the default bundle public in AbstractMessageInterpolator + * HV-1673 - engine - Audit new privileged calls in the reflection abstraction + * HV-1669 - engine - Only enable the SafeHtml constraint if jsoup in in the classpath + * HV-1668 - engine - Properly register all built-in constraints + * HV-1659 - integration - Upgrade WildFly to 14.0.1 + * HV-1658 - engine - Remove a couple of stream usages in ConstraintHelper that cause a problem for another project + * 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-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-1635 - documentation - Remove specific instructions for building with JDK 9 from the README + * HV-1627 - build - Upgrade our JPA test dependency to 2.2 + * 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.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..9016a56980 100644 --- a/copyright.txt +++ b/copyright.txt @@ -3,6 +3,7 @@ Ahmed Al Hafoudh Alaa Nassef Andrey Derevyanko Andrey Rodionov +Asutosh Pandya Benson Margulies Brent Douglas Carlos Vara @@ -10,6 +11,8 @@ Carlo de Wolf Chris Beckey Christian Ivan Dag Hovland +Damir Alibegovic +Dario Seidl Davide D'Alto Davide Marchignoli Denis Tiago @@ -29,6 +32,7 @@ Henno Vermeulen Hillmer Chona Jan-Willem Willebrands Jason T. Greene +Jesper Preuss Jiri Bilek Julien Furgerot Julien May @@ -51,14 +55,17 @@ Nicola Ferraro Nicolas François Paolo Perrotta Pete Muir +Rob Dickinson Sanne Grinovero Sebastian Bayerl Shahram Goodarzi Shane Bryzak Shelly McGowan +Sjaak Derksen Steve Ebersole Strong Liu Tadhg Pearson +Takashi Aoe Tomaz Cerar Tommy Johansen Victor Rezende dos Santos diff --git a/distribution/pom.xml b/distribution/pom.xml index a8d1f5afdd..9f31a4eb58 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.2.0.Final ../pom.xml @@ -45,17 +45,17 @@ org.glassfish - javax.el + jakarta.el - log4j - log4j + org.apache.logging.log4j + log4j-core - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + jakarta.persistence + jakarta.persistence-api joda-time @@ -65,25 +65,21 @@ javax.money money-api - - org.jsoup - jsoup - com.thoughtworks.paranamer paranamer - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api - org.jboss.spec.javax.interceptor - jboss-interceptors-api_1.2_spec + jakarta.interceptor + jakarta.interceptor-api - javax.annotation - javax.annotation-api + jakarta.annotation + jakarta.annotation-api org.jboss.logging @@ -93,13 +89,13 @@ ${project.groupId} hibernate-validator-modules - wildfly-${wildfly.version}-patch + wildfly-${version.wildfly}-patch zip ${project.groupId} hibernate-validator-modules - wildfly-${wildfly-secondary.version}-patch + wildfly-${version.wildfly.secondary}-patch zip @@ -119,7 +115,7 @@ ${java.api-docs.base-url} ${javaee.api-docs.base-url} ${bv.api-docs.base-url} - ${javamoney.api-docs.base-url} + @@ -166,7 +162,7 @@ ${basedir}/src/main/assembly/dist.xml hibernate-validator-${project.version} - gnu + posix ${project.build.directory}/dist/ @@ -207,8 +203,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..0f1fb92a9e 100644 --- a/distribution/src/main/assembly/dist.xml +++ b/distribution/src/main/assembly/dist.xml @@ -33,19 +33,18 @@ dist/lib/required - javax.validation:validation-api + jakarta.validation:jakarta.validation-api org.jboss.logging:jboss-logging com.fasterxml:classmate - org.glassfish:javax.el + org.glassfish:jakarta.el dist/lib/optional - log4j:log4j + org.apache.logging.log4j:log4j-core joda-time:joda-time - org.hibernate.javax.persistence:hibernate-jpa-2.1-api - org.jsoup:jsoup + jakarta.persistence:jakarta.persistence-api com.thoughtworks.paranamer:paranamer diff --git a/documentation/pom.xml b/documentation/pom.xml index 4cfd462026..9fd8576bc2 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.2.0.Final ../pom.xml @@ -31,7 +31,7 @@ ${basedir}/../osgi/integrationtest/src/test/java Hibernate Validator, Annotation based constraints for your domain model - Reference Documentation - hibernate, validator, hibernate validator, validation, bean validation + hibernate, validator, hibernate validator, validation, jakarta bean validation, bean validation validator UA-45270411-3 GTM-NJWS5L @@ -40,7 +40,7 @@ true - -Duser.language=en + -Duser.language=en forbidden-allow-junit.txt .. @@ -54,7 +54,7 @@ org.glassfish - javax.el + jakarta.el test @@ -63,8 +63,8 @@ test - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api test @@ -98,6 +98,21 @@ assertj-core test + + jakarta.annotation + jakarta.annotation-api + test + + + com.fasterxml.jackson.core + jackson-databind + test + + + com.fasterxml.jackson.core + jackson-annotations + test + @@ -126,7 +141,7 @@ org.hibernate.infra hibernate-asciidoctor-theme - ${hibernate-asciidoctor-theme.version} + ${version.org.hibernate.infra.hibernate-asciidoctor-theme} zip true ${project.build.directory}/ @@ -188,6 +203,21 @@ ${asciidoctor.base-output-dir}/html_single/css/ + + copy-script-to-html-output + generate-resources + + copy-resources + + + + + ${asciidoctor.aggregated-resources-dir}/script/ + + + ${asciidoctor.base-output-dir}/html_single/script/ + + copy-images-to-html-output generate-resources @@ -250,13 +280,13 @@ ${asciidoctor.osgi-integrationtest-source-dir} ${project.version} - ${bv.api.version} - ${jboss.logging.version} - ${classmate.version} - ${javax.el.version} + ${version.jakarta.validation-api} + ${version.org.jboss.logging.jboss-logging} + ${version.com.fasterxml.classmate} + ${version.org.glassfish.jakarta.el} - ${wildfly.version} - ${wildfly-secondary.version} + ${version.wildfly} + ${version.wildfly.secondary} ${java.api-docs.base-url}/ @@ -334,5 +364,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..8c761fe37f 100644 --- a/documentation/src/main/asciidoc/ch01.asciidoc +++ b/documentation/src/main/asciidoc/ch01.asciidoc @@ -1,7 +1,7 @@ [[validator-gettingstarted]] == Getting started -This chapter will show you how to get started with Hibernate Validator, the reference implementation (RI) of Bean Validation. For the following quick-start you need: +This chapter will show you how to get started with Hibernate Validator, the reference implementation (RI) of Jakarta Bean Validation. For the following quick-start you need: * A JDK 8 * http://maven.apache.org/[Apache Maven] @@ -26,18 +26,18 @@ your __pom.xml__: ---- ==== -This transitively pulls in the dependency to the Bean Validation API -(`javax.validation:validation-api:{bvVersion}`). +This transitively pulls in the dependency to the Jakarta Bean Validation API +(`jakarta.validation:jakarta.validation-api:{bvVersion}`). [[validator-gettingstarted-uel]] ==== Unified EL -Hibernate Validator requires an implementation of the Unified Expression Language -(http://jcp.org/en/jsr/detail?id=341[JSR 341]) for evaluating dynamic expressions in constraint +Hibernate Validator requires an implementation of https://projects.eclipse.org/projects/ee4j.el[Jakarta Expression Language] +for evaluating dynamic expressions in constraint violation messages (see <>). When your application runs in a Java EE container such as JBoss AS, an EL implementation is already provided by the container. In a Java SE environment, however, you have to add an implementation as dependency to your POM file. For instance -you can add the following dependency to use the JSR 341 https://javaee.github.io/uel-ri/[reference +you can add the following dependency to use the Jakarta EL https://github.com/eclipse-ee4j/el-ri[reference implementation]: .Maven dependencies for Unified EL reference implementation @@ -47,8 +47,8 @@ implementation]: ---- org.glassfish - javax.el - {javaxElVersion} + jakarta.el + {jakartaElVersion} ---- ==== @@ -57,14 +57,15 @@ implementation]: ==== For environments where one cannot provide a EL implementation Hibernate Validator is offering a <>. However, the use of this interpolator -is not Bean Validation specification compliant. +is not Jakarta Bean Validation specification compliant. ==== [[section-getting-started-cdi]] ==== CDI -Bean Validation defines integration points with CDI (Contexts and Dependency Injection for Java ^TM^ -EE, http://jcp.org/en/jsr/detail?id=346[JSR 346]). If your application runs in an +Jakarta Bean Validation defines integration points with CDI +(https://projects.eclipse.org/projects/ee4j.cdi[Contexts and Dependency Injection for Jakarta EE]). +If your application runs in an environment which does not provide this integration out of the box, you may use the Hibernate Validator CDI portable extension by adding the following Maven dependency to your POM: @@ -82,14 +83,14 @@ Validator CDI portable extension by adding the following Maven dependency to you ==== Note that adding this dependency is usually not required for applications running on a Java EE -application server. You can learn more about the integration of Bean Validation and CDI in +application server. You can learn more about the integration of Jakarta Bean Validation and CDI in <>. [[section-getting-started-security-manager]] ==== Running with a security manager Hibernate Validator supports running with a {javaTechnotesBaseUrl}/guides/security/index.html[security manager] being enabled. -To do so, you must assign several permissions to the code bases of Hibernate Validator, the Bean Validation API, Classmate and JBoss Logging and also to the code base calling Bean Validation. +To do so, you must assign several permissions to the code bases of Hibernate Validator, the Jakarta Bean Validation API, Classmate and JBoss Logging and also to the code base calling Jakarta Bean Validation. The following shows how to do this via a {javaTechnotesBaseUrl}/guides/security/PolicyFiles.html[policy file] as processed by the Java default policy implementation: .Policy file for using Hibernate Validator with a security manager @@ -108,7 +109,7 @@ grant codeBase "file:path/to/hibernate-validator-{hvVersion}.jar" { permission java.util.PropertyPermission "mapAnyUriToUri", "read"; }; -grant codeBase "file:path/to/validation-api-{bvVersion}.jar" { +grant codeBase "file:path/to/jakarta.validation-api-{bvVersion}.jar" { permission java.io.FilePermission "path/to/hibernate-validator-{hvVersion}.jar", "read"; }; @@ -130,7 +131,7 @@ grant codeBase "file:path/to/validation-caller-x.y.z.jar" { ==== Updating Hibernate Validator in WildFly The http://wildfly.org/[WildFly application server] contains Hibernate Validator out of the box. -In order to update the server modules for Bean Validation API and Hibernate Validator to the latest and greatest, the patch mechanism of WildFly can be used. +In order to update the server modules for Jakarta Bean Validation API and Hibernate Validator to the latest and greatest, the patch mechanism of WildFly can be used. You can download the patch file from http://sourceforge.net/projects/hibernate/files/hibernate-validator[SourceForge] or from Maven Central using the following dependency: @@ -198,7 +199,7 @@ There are no JPMS module descriptors provided yet, but Hibernate Validator is us These are the module names as declared using the `Automatic-Module-Name` header: -* Bean Validation API: `java.validation` +* Jakarta Bean Validation API: `java.validation` * Hibernate Validator core: `org.hibernate.validator` * Hibernate Validator CDI extension: `org.hibernate.validator.cdi` * Hibernate Validator test utilities: `org.hibernate.validator.testutils` @@ -206,20 +207,17 @@ 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. -This module contains a subset of the JSR 250 API ("Commons Annotations"), but some annotations such as `javax.annotation.Priority` are missing. +This module contains a subset of Jakarta Annotations, but some annotations such as `javax.annotation.Priority` are missing. This causes the method validation interceptor of Hibernate Validator to not be registered, i.e. method validation won't work. -Instead, add the full JSR 250 API to the unnamed module (i.e. the classpath), e.g. by pulling in the _javax.annotation:javax.annotation-api_ dependency -(there already is a transitive dependency to the JSR 250 API when depending on _org.hibernate.validator:hibernate-validator-cdi_). +Instead, add the full Jakarta Annotations API to the unnamed module (i.e. the classpath), e.g. by pulling in the _jakarta.annotation:jakarta.annotation-api_ dependency +(there already is a transitive dependency to the Jakarta Annotations API when depending on _org.hibernate.validator:hibernate-validator-cdi_). If you need to enable the `java.xml.ws.annotation` module for some reason, you should patch it with the contents of the full API -by appending `--patch-module java.xml.ws.annotation=/path/to/complete-jsr250-api.jar` to your _java_ invocation. +by appending `--patch-module java.xml.ws.annotation=/path/to/complete-jakarta.annotation-api.jar` to your _java_ invocation. ==== [[validator-gettingstarted-createmodel]] @@ -287,11 +285,11 @@ code. [[validator-gettingstarted-whatsnext]] === Where to go next? -That concludes the 5 minutes tour through the world of Hibernate Validator and Bean Validation. +That concludes the 5 minutes tour through the world of Hibernate Validator and Jakarta Bean Validation. Continue exploring the code examples or look at further examples referenced in <>. To learn more about the validation of beans and properties, just continue reading -<>. If you are interested in using Bean Validation for the validation of +<>. If you are interested in using Jakarta Bean Validation for the validation of method pre- and postcondition refer to <>. In case your application has specific validation requirements have a look at <>. diff --git a/documentation/src/main/asciidoc/ch02.asciidoc b/documentation/src/main/asciidoc/ch02.asciidoc index 8f62d8c3c2..69d386bdf8 100644 --- a/documentation/src/main/asciidoc/ch02.asciidoc +++ b/documentation/src/main/asciidoc/ch02.asciidoc @@ -12,7 +12,7 @@ If you are interested in applying constraints to method parameters and return va [[section-declaring-bean-constraints]] === Declaring bean constraints -Constraints in Bean Validation are expressed via Java annotations. In this section you will learn +Constraints in Jakarta Bean Validation are expressed via Java annotations. In this section you will learn how to enhance an object model with these annotations. There are four types of bean constraints: * field constraints @@ -23,7 +23,7 @@ how to enhance an object model with these annotations. There are four types of b [NOTE] ==== Not all constraints can be placed on all of these levels. In fact, none of the default constraints -defined by Bean Validation can be placed at class level. The `java.lang.annotation.Target` annotation +defined by Jakarta Bean Validation can be placed at class level. The `java.lang.annotation.Target` annotation in the constraint annotation itself determines on which elements a constraint can be placed. See <> for more information. ==== @@ -95,7 +95,7 @@ It is possible to specify constraints directly on the type argument of a parameterized type: these constraints are called container element constraints. This requires that `ElementType.TYPE_USE` is specified via `@Target` -in the constraint definition. As of Bean Validation 2.0, built-in Bean Validation as well as +in the constraint definition. As of Jakarta Bean Validation 2.0, built-in Jakarta Bean Validation as well as Hibernate Validator specific constraints specify `ElementType.TYPE_USE` and can be used directly in this context. @@ -332,7 +332,7 @@ evaluated in addition to the `@NotNull` constraint from the superclass. [[section-object-graph-validation]] ==== Object graphs -The Bean Validation API does not only allow to validate single class instances but also complete +The Jakarta Bean Validation API does not only allow to validate single class instances but also complete object graphs (cascaded validation). To do so, just annotate a field or property representing a reference to another object with `@Valid` as demonstrated in <>. @@ -410,7 +410,7 @@ as it is more expressive. [[section-validating-bean-constraints]] === Validating bean constraints -The `Validator` interface is the most important object in Bean Validation. The next section shows how +The `Validator` interface is the most important object in Jakarta Bean Validation. The next section shows how to obtain a `Validator` instance. Afterwards you'll learn how to use the different methods of the `Validator` interface. @@ -496,7 +496,7 @@ include::{sourcedir}/org/hibernate/validator/referenceguide/chapter02/validation ==== -`Validator#validateProperty()` is for example used in the integration of Bean Validation into JSF 2 +`Validator#validateProperty()` is for example used in the integration of Jakarta Bean Validation into JSF 2 (see <>) to perform a validation of the values entered into a form before they are propagated to the model. @@ -539,28 +539,28 @@ The returned `Path` is composed of ``Node``s describing the path to the element. More information about the structure of the `Path` and the various types of ``Node``s can be found in {bvSpecUrl}#validationapi-constraintviolation[the `ConstraintViolation` section] of the -Bean Validation specification. +Jakarta Bean Validation specification. [[section-builtin-constraints]] === Built-in constraints Hibernate Validator comprises a basic set of commonly used constraints. These are foremost the -constraints defined by the Bean Validation specification (see <>). +constraints defined by the Jakarta Bean Validation specification (see <>). Additionally, Hibernate Validator provides useful custom constraints (see <>). [[validator-defineconstraints-spec]] -==== Bean Validation constraints +==== Jakarta Bean Validation constraints -Below you can find a list of all constraints specified in the Bean Validation API. -All these constraints apply to the field/property level, there are no class-level constraints defined in the Bean Validation specification. +Below you can find a list of all constraints specified in the Jakarta Bean Validation API. +All these constraints apply to the field/property level, there are no class-level constraints defined in the Jakarta Bean Validation specification. If you are using the Hibernate object-relational mapper, some of the constraints are taken into account when creating the DDL for your model (see "Hibernate metadata impact"). [NOTE] ==== Hibernate Validator allows some constraints to be applied to more data types than required by the -Bean Validation specification (e.g. `@Max` can be applied to strings). Relying on this feature can -impact portability of your application between Bean Validation providers. +Jakarta Bean Validation specification (e.g. `@Max` can be applied to strings). Relying on this feature can +impact portability of your application between Jakarta Bean Validation providers. ==== `@AssertFalse`:: Checks that the annotated element is false @@ -580,7 +580,7 @@ impact portability of your application between Bean Validation providers. Hibernate metadata impact::: None `@Digits(integer=, fraction=)`:: Checks whether the annotated value is a number having up to `integer` digits and `fraction` fractional digits - Supported data types::: BigDecimal, `BigInteger`, `CharSequence`, `byte`, `short`, `int`, `long` and the respective wrappers of the primitive types; additionally supported by HV: any sub-type of `Number` + Supported data types::: BigDecimal, `BigInteger`, `CharSequence`, `byte`, `short`, `int`, `long` and the respective wrappers of the primitive types; additionally supported by HV: any sub-type of `Number` and `javax.money.MonetaryAmount` Hibernate metadata impact::: Defines column precision and scale `@Email`:: Checks whether the specified character sequence is a valid email address. The optional parameters `regexp` and `flags` allow to specify an additional regular expression (including regular expression flags) which the email must match. @@ -654,13 +654,13 @@ impact portability of your application between Bean Validation providers. [NOTE] ==== On top of the parameters listed above each constraint has the parameters -message, groups and payload. This is a requirement of the Bean Validation specification. +message, groups and payload. This is a requirement of the Jakarta Bean Validation specification. ==== [[validator-defineconstraints-hv-constraints]] ==== Additional constraints -In addition to the constraints defined by the Bean Validation API, Hibernate Validator provides several useful custom constraints which are listed below. +In addition to the constraints defined by the Jakarta Bean Validation API, Hibernate Validator provides several useful custom constraints which are listed below. With one exception also these constraints apply to the field/property level, only `@ScriptAssert` is a class-level constraint. `@CreditCardNumber(ignoreNonDigitCharacters=)`:: Checks that the annotated character sequence passes the Luhn checksum test. Note, this validation aims to check for user mistakes, not credit card validity! See also http://www.dirigodev.com/blog/ecommerce/anatomy-of-a-credit-card-number/[Anatomy of a credit card number]. `ignoreNonDigitCharacters` allows to ignore non digit characters. The default is `false`. @@ -707,15 +707,14 @@ With one exception also these constraints apply to the field/property level, onl Supported data types::: `CharSequence` Hibernate metadata impact::: None +`@Normalized(form=)`:: Validates that the annotated character sequence is normalized according to the given `form`. + Supported data types::: `CharSequence` + Hibernate metadata impact::: None + `@Range(min=, max=)`:: Checks whether the annotated value lies between (inclusive) the specified minimum and maximum Supported data types::: `BigDecimal`, `BigInteger`, `CharSequence`, `byte`, `short`, `int`, `long` and the respective wrappers of the primitive types Hibernate metadata impact::: None -`@SafeHtml(whitelistType= , additionalTags=, additionalTagsWithAttributes=, baseURI=)`:: Checks whether the annotated value contains potentially malicious fragments such as `" ); - Set> violations = validator.validate( foo ); - assertThat( violations ).containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - } - - private static class Foo { - - @SafeHtml - private final String html; - - public Foo(String html) { - this.html = html; - } - } -} 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/constraints/annotations/hv/ru/INNValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/ru/INNValidatorTest.java new file mode 100644 index 0000000000..4d51429b3a --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/ru/INNValidatorTest.java @@ -0,0 +1,53 @@ +/* + * 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.constraints.annotations.hv.ru; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.Set; + +import org.hibernate.validator.constraints.ru.INN; +import org.hibernate.validator.test.constraints.annotations.AbstractConstrainedTest; + +import javax.validation.ConstraintViolation; +import org.testng.annotations.Test; + +/** + * Test to make sure that elements annotated with {@link INN} are validated. + * + * @author Artem Boiarshinov + */ +public class INNValidatorTest extends AbstractConstrainedTest { + + @Test + public void testINN() { + final Person person = new Person( "245885856020" ); + final Set> violations = validator.validate( person ); + assertNoViolations( violations ); + } + + @Test + public void testINNInvalid() { + final Person person = new Person( "0123456789" ); + final Set> violations = validator.validate( person ); + assertThat( violations ).containsOnlyViolations( + violationOf( INN.class ).withMessage( "invalid Russian taxpayer identification number (INN)" ) + ); + } + + private static class Person { + + @INN + private final String inn; + + public Person(String inn) { + this.inn = inn; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java b/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java index f1b8781d5a..790a396182 100644 --- a/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/boolcomposition/IsBlank.java @@ -11,9 +11,9 @@ import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; +import javax.validation.constraints.NotBlank; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.FIELD; 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/el/ConstraintExpressionLanguageFeatureLevelTest.java b/engine/src/test/java/org/hibernate/validator/test/el/ConstraintExpressionLanguageFeatureLevelTest.java new file mode 100644 index 0000000000..6370b842d8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/el/ConstraintExpressionLanguageFeatureLevelTest.java @@ -0,0 +1,257 @@ +/* + * 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.el; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidationXmlTestHelper; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1816") +public class ConstraintExpressionLanguageFeatureLevelTest { + + private static ValidationXmlTestHelper validationXmlTestHelper; + + @BeforeClass + public static void setupValidationXmlTestHelper() { + validationXmlTestHelper = new ValidationXmlTestHelper( ConstraintExpressionLanguageFeatureLevelTest.class ); + } + + @Test + public void default_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void none_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.NONE ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: ${validatedValue}" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void variables_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.VARIABLES ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void bean_properties_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void bean_methods_expression_language_feature_level() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .constraintExpressionLanguageFeatureLevel( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + + @Test + public void property_default_value() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-constraints-default.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + } ); + } + + @Test + public void property_bean_methods() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-constraints-bean-methods.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new VariablesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( VariablesConstraint.class ).withMessage( "Variable: value" ) ); + assertThat( validator.validate( new BeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( BeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ) ); + assertThat( validator.validate( new BeanMethodsBean() ) ) + .containsOnlyViolations( violationOf( BeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + } ); + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { VariablesStringValidator.class }) + private @interface VariablesConstraint { + String message() default "Variable: ${validatedValue}"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } + + public static class VariablesStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return false; + } + } + + public static class VariablesBean { + + public VariablesBean(String value) { + this.value = value; + } + + @VariablesConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { BeanPropertiesConstraintStringValidator.class }) + private @interface BeanPropertiesConstraint { + String message() default "Bean property: ${validatedValue.bytes[0]}"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } + + public static class BeanPropertiesConstraintStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return false; + } + } + + public static class BeanPropertiesBean { + + public BeanPropertiesBean(String value) { + this.value = value; + } + + @BeanPropertiesConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { BeanMethodsConstraintStringValidator.class }) + private @interface BeanMethodsConstraint { + String message() default "Method execution: ${'aaaa'.substring(0, 1)}"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } + + public static class BeanMethodsConstraintStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + return false; + } + } + + public static class BeanMethodsBean { + + @BeanMethodsConstraint + public String value; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/el/CustomViolationExpressionLanguageFeatureLevelTest.java b/engine/src/test/java/org/hibernate/validator/test/el/CustomViolationExpressionLanguageFeatureLevelTest.java new file mode 100644 index 0000000000..01a49f89ac --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/el/CustomViolationExpressionLanguageFeatureLevelTest.java @@ -0,0 +1,388 @@ +/* + * 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.el; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.testng.Assert.assertTrue; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +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.HibernateValidator; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidationXmlTestHelper; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1816") +public class CustomViolationExpressionLanguageFeatureLevelTest { + + private static ValidationXmlTestHelper validationXmlTestHelper; + + private ListAppender constraintValidatorContextImplLoglistAppender; + + + @BeforeClass + public static void setupValidationXmlTestHelper() { + validationXmlTestHelper = new ValidationXmlTestHelper( ConstraintExpressionLanguageFeatureLevelTest.class ); + } + + @BeforeTest + public void setUp() { + LoggerContext context = LoggerContext.getContext( false ); + Logger logger = context.getLogger( ConstraintValidatorContextImpl.class.getName() ); + constraintValidatorContextImplLoglistAppender = (ListAppender) logger.getAppenders().get( "List" ); + constraintValidatorContextImplLoglistAppender.clear(); + } + + @AfterTest + public void tearDown() { + constraintValidatorContextImplLoglistAppender.clear(); + } + + @Test + public void default_behavior() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new DefaultLevelBean() ) ) + .containsOnlyViolations( violationOf( DefaultLevelConstraint.class ).withMessage( "Variable: ${validatedValue}" ), + violationOf( DefaultLevelConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ), + violationOf( DefaultLevelConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void enable_el() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new EnableELBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ), + violationOf( EnableELConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void enable_el_bean_properties() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new EnableELBeanPropertiesBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELBeanPropertiesConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELBeanPropertiesConstraint.class ).withMessage( "Bean property: 118" ), + violationOf( EnableELBeanPropertiesConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + + @Test + public void enable_el_bean_methods() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new EnableELBeanMethodsBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Bean property: 118" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + + @Test + public void warn_when_default_behavior_and_expression_variables() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + assertThat( validator.validate( new DefaultLevelWithExpressionVariablesBean() ) ) + .containsOnlyViolations( violationOf( DefaultLevelWithExpressionVariablesConstraint.class ).withMessage( "Variable: ${myVariable}" ) ); + + assertTrue( constraintValidatorContextImplLoglistAppender.getEvents().stream() + .filter( event -> event.getLevel().equals( Level.WARN ) ) + .map( event -> event.getMessage().getFormattedMessage() ) + .anyMatch( m -> m.startsWith( "HV000257" ) ) ); + } + + @Test + public void property_default_value() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-custom-violations-default.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new EnableELBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELConstraint.class ).withMessage( "Bean property: ${validatedValue.bytes[0]}" ), + violationOf( EnableELConstraint.class ).withMessage( "Method execution: ${'aaaa'.substring(0, 1)}" ) ); + } + } ); + } + + @Test + public void property_bean_methods() { + validationXmlTestHelper.runWithCustomValidationXml( + "validation-custom-violations-bean-methods.xml", new Runnable() { + + @Override + public void run() { + Validator validator = ValidatorUtil.getValidator(); + + assertThat( validator.validate( new EnableELBeanMethodsBean( "value" ) ) ) + .containsOnlyViolations( violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Variable: value" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Bean property: 118" ), + violationOf( EnableELBeanMethodsConstraint.class ).withMessage( "Method execution: a" ) ); + } + } ); + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { DefaultLevelStringValidator.class }) + private @interface DefaultLevelConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class DefaultLevelStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ).addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ).addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ).addConstraintViolation(); + + return false; + } + } + + public static class DefaultLevelBean { + + @DefaultLevelConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { EnableELStringValidator.class }) + private @interface EnableELConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class EnableELStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + + return false; + } + } + + public static class EnableELBean { + + public EnableELBean(String value) { + this.value = value; + } + + @EnableELConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { EnableELBeanPropertiesStringValidator.class }) + private @interface EnableELBeanPropertiesConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class EnableELBeanPropertiesStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ) + .addConstraintViolation(); + + return false; + } + } + + public static class EnableELBeanPropertiesBean { + + public EnableELBeanPropertiesBean(String value) { + this.value = value; + } + + @EnableELBeanPropertiesConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { EnableELBeanMethodsStringValidator.class }) + private @interface EnableELBeanMethodsConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class EnableELBeanMethodsStringValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.buildConstraintViolationWithTemplate( "Variable: ${validatedValue}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Bean property: ${validatedValue.bytes[0]}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .addConstraintViolation(); + hibernateContext.buildConstraintViolationWithTemplate( "Method execution: ${'aaaa'.substring(0, 1)}" ) + .enableExpressionLanguage( ExpressionLanguageFeatureLevel.BEAN_METHODS ) + .addConstraintViolation(); + + return false; + } + } + + public static class EnableELBeanMethodsBean { + + public EnableELBeanMethodsBean(String value) { + this.value = value; + } + + @EnableELBeanMethodsConstraint + public String value; + } + + @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { DefaultLevelWithExpressionVariablesStringValidator.class }) + private @interface DefaultLevelWithExpressionVariablesConstraint { + + String message() default "-"; + + Class[] groups() default {}; + + Class[] payload() default {}; + } + + public static class DefaultLevelWithExpressionVariablesStringValidator + implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + HibernateConstraintValidatorContext hibernateContext = (HibernateConstraintValidatorContext) context; + + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext + .addExpressionVariable( "myVariable", "value" ) + .buildConstraintViolationWithTemplate( "Variable: ${myVariable}" ) + .addConstraintViolation(); + + return false; + } + } + + public static class DefaultLevelWithExpressionVariablesBean { + + @DefaultLevelWithExpressionVariablesConstraint + public String value; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java index 58f3d21b3b..fea137653c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/BootstrappingTest.java @@ -24,13 +24,13 @@ import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.executable.ExecutableValidator; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.constraints.NotEmpty; -import org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator; +import org.hibernate.validator.internal.constraintvalidators.bv.notempty.NotEmptyValidatorForCharSequence; import org.hibernate.validator.internal.engine.ConfigurationImpl; import org.hibernate.validator.internal.engine.ValidatorFactoryImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; @@ -81,8 +81,8 @@ public void testCustomConstraintValidatorFactory() { new ConstraintValidatorFactory() { @Override public > T getInstance(Class key) { - if ( key == NotNullValidator.class ) { - return (T) new BadlyBehavedNotNullConstraintValidator(); + if ( key == NotEmptyValidatorForCharSequence.class ) { + return key.cast( new BadlyBehavedNotEmptyValidatorForCharSequence() ); } return new ConstraintValidatorFactoryImpl().getInstance( key ); } @@ -142,9 +142,9 @@ private void assertDefaultFactory(ValidatorFactory factory) { assertTrue( factory instanceof ValidatorFactoryImpl ); } - class BadlyBehavedNotNullConstraintValidator extends NotNullValidator { + class BadlyBehavedNotEmptyValidatorForCharSequence extends NotEmptyValidatorForCharSequence { @Override - public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) { + public boolean isValid(CharSequence charSequence, ConstraintValidatorContext constraintValidatorContext) { return true; } } 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/bootstrap/Customer.java b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/Customer.java index dedffe300c..4005a9c247 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/Customer.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/bootstrap/Customer.java @@ -8,10 +8,11 @@ import java.util.HashSet; import java.util.Set; + import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.NotEmpty; /** * @author Hardy Ferentschik 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..dc28661d56 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/MessagePropertiesTest.java @@ -0,0 +1,369 @@ +/* + * 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.Normalized; +import org.hibernate.validator.constraints.ParameterScriptAssert; +import org.hibernate.validator.constraints.Range; +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.ru.INN; +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<>(); + + for ( Locale locale : ALL_SUPPORTED_LOCALES ) { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .defaultLocale( locale ) + .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( Normalized.class ), + violationOf( org.hibernate.validator.constraints.NotBlank.class ), + violationOf( org.hibernate.validator.constraints.NotEmpty.class ), + violationOf( Range.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( INN.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" ); + } + } + + 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"; + + @Normalized(form = java.text.Normalizer.Form.NFKC) + private String normalized = "\uFE64script\uFE65"; + + @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; + + @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"; + + @INN + private String inn = "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/PredefinedScopeAllConstraintsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/PredefinedScopeAllConstraintsTest.java new file mode 100644 index 0000000000..8826217afc --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/PredefinedScopeAllConstraintsTest.java @@ -0,0 +1,466 @@ +/* + * 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.lang.annotation.Annotation; +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +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.PredefinedScopeHibernateValidator; +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.Normalized; +import org.hibernate.validator.constraints.ParameterScriptAssert; +import org.hibernate.validator.constraints.Range; +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.ru.INN; +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 PredefinedScopeAllConstraintsTest { + + @Test + public void testConstraints() throws NoSuchMethodException, SecurityException { + testConstraint( AssertFalse.class, new AssertFalseBean() ); + testConstraint( AssertTrue.class, new AssertTrueBean() ); + testConstraint( DecimalMax.class, new DecimalMaxBean() ); + testConstraint( DecimalMin.class, new DecimalMinBean() ); + testConstraint( Digits.class, new DigitsBean() ); + testConstraint( Email.class, new EmailBean() ); + testConstraint( Future.class, new FutureBean() ); + testConstraint( FutureOrPresent.class, new FutureOrPresentBean() ); + testConstraint( Max.class, new MaxBean() ); + testConstraint( Min.class, new MinBean() ); + testConstraint( Negative.class, new NegativeBean() ); + testConstraint( NegativeOrZero.class, new NegativeOrZeroBean() ); + testConstraint( NotBlank.class, new NotBlankBean() ); + testConstraint( NotEmpty.class, new NotEmptyBean() ); + testConstraint( NotNull.class, new NotNullBean() ); + testConstraint( Null.class, new NullBean() ); + testConstraint( Past.class, new PastBean() ); + testConstraint( PastOrPresent.class, new PastOrPresentBean() ); + testConstraint( Pattern.class, new PatternBean() ); + testConstraint( Positive.class, new PositiveBean() ); + testConstraint( PositiveOrZero.class, new PositiveOrZeroBean() ); + testConstraint( Size.class, new SizeBean() ); + testConstraint( CreditCardNumber.class, new CreditCardNumberBean() ); + testConstraint( Currency.class, new CurrencyBean() ); + testConstraint( EAN.class, new EANBean() ); + testConstraint( org.hibernate.validator.constraints.Email.class, new HvEmailBean() ); + testConstraint( ISBN.class, new ISBNBean() ); + testConstraint( Length.class, new LengthBean() ); + testConstraint( CodePointLength.class, new CodePointLengthBean() ); + testConstraint( LuhnCheck.class, new LuhnCheckBean() ); + testConstraint( Mod10Check.class, new Mod10CheckBean() ); + testConstraint( Mod11Check.class, new Mod11CheckBean() ); + testConstraint( ModCheck.class, new ModCheckBean() ); + testConstraint( Normalized.class, new NormalizedBean() ); + testConstraint( org.hibernate.validator.constraints.NotBlank.class, new HvNotBlankBean() ); + testConstraint( org.hibernate.validator.constraints.NotEmpty.class, new HvNotEmptyBean() ); + testConstraint( Range.class, new RangeBean() ); + testConstraint( UniqueElements.class, new UniqueElementsBean() ); + testConstraint( URL.class, new URLBean() ); + testConstraint( CNPJ.class, new CNPJBean() ); + testConstraint( CPF.class, new CPFBean() ); + testConstraint( TituloEleitoral.class, new TituloEleitoralBean() ); + testConstraint( REGON.class, new REGONBean() ); + testConstraint( NIP.class, new NIPBean() ); + testConstraint( PESEL.class, new PESELBean() ); + testConstraint( INN.class, new INNBean() ); + testConstraint( DurationMax.class, new DurationMaxBean() ); + testConstraint( DurationMin.class, new DurationMinBean() ); + testConstraint( ScriptAssert.class, new ScriptAssertBean() ); + + Set> parameterScriptAssertBeanViolations = getValidator( ParameterScriptAssert.class, + ParameterScriptAssertBean.class ).forExecutables().validateParameters( + new ParameterScriptAssertBean(), ParameterScriptAssertBean.class.getDeclaredMethod( "doTest", boolean.class ), new Object[]{ false } ); + + ConstraintViolationAssert.assertThat( parameterScriptAssertBeanViolations ) + .containsOnlyViolations( + violationOf( ParameterScriptAssert.class ) ); + } + + private void testConstraint(Class constraint, T bean) { + Set> violations = getValidator( constraint, bean.getClass() ) + .validate( bean ); + ConstraintViolationAssert.assertThat( violations ) + .containsOnlyViolations( + violationOf( constraint ) ); + } + + private static Validator getValidator(Class constraint, Class beanClass) { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( Collections.singleton( constraint.getName() ) ) + .initializeBeanMetaData( Collections.singleton( beanClass ) ) + .buildValidatorFactory() + .getValidator(); + } + + private static class AssertFalseBean { + + @AssertFalse + private boolean assertFalse = true; + } + + private static class AssertTrueBean { + + @AssertTrue + private boolean assertTrue = false; + } + + private static class DecimalMaxBean { + + @DecimalMax("3") + private double decimalMax = 4; + } + + private static class DecimalMinBean { + + @DecimalMin("3") + private double decimalMin = 2; + } + + private static class DigitsBean { + + @Digits(integer = 1, fraction = 3) + private BigDecimal digits = BigDecimal.valueOf( 13333.3333f ); + } + + private static class EmailBean { + + @Email + private String email = "invalid"; + } + + private static class FutureBean { + + @Future + private LocalDate future = LocalDate.of( 2010, 10, 4 ); + } + + private static class FutureOrPresentBean { + + @FutureOrPresent + private LocalDate futureOrPresent = LocalDate.of( 2010, 10, 4 ); + } + + private static class MaxBean { + + @Max(4) + private int max = 6; + } + + private static class MinBean { + + @Min(4) + private int min = 2; + } + + private static class NegativeBean { + + @Negative + private int negative = 4; + } + + private static class NegativeOrZeroBean { + + @NegativeOrZero + private int negativeOrZero = 4; + } + + private static class NotBlankBean { + + @NotBlank + private String notBlank = ""; + } + + private static class NotEmptyBean { + + @NotEmpty + private List notEmpty = Collections.emptyList(); + } + + private static class NotNullBean { + + @NotNull + private String notNull = null; + } + + private static class NullBean { + + @Null + private String nullConstraint = "not null"; + } + + private static class PastBean { + + @Past + private LocalDate past = LocalDate.of( 2890, 10, 4 ); + } + + private static class PastOrPresentBean { + + @PastOrPresent + private LocalDate pastOrPresent = LocalDate.of( 2890, 10, 4 ); + } + + private static class PatternBean { + + @Pattern(regexp = "[0-9]+") + private String pattern = "invalid"; + } + + private static class PositiveBean { + + @Positive + private int positive = -4; + } + + private static class PositiveOrZeroBean { + + @PositiveOrZero + private int positiveOrZero = -4; + } + + private static class SizeBean { + + @Size(min = 2, max = 4) + private String size = "666666"; + } + + private static class CreditCardNumberBean { + + @CreditCardNumber + private String creditCardNumber = "invalid"; + } + + private static class CurrencyBean { + + @Currency("EUR") + private MonetaryAmount currency = Money.of( 1000f, "USD" ); + } + + private static class EANBean { + + @EAN + private String ean = "invalid"; + } + + private static class HvEmailBean { + + @org.hibernate.validator.constraints.Email + private String hvEmail = "invalid"; + } + + private static class ISBNBean { + + @ISBN + private String isbn = "invalid"; + } + + private static class LengthBean { + + @Length(min = 2, max = 4) + private String length = "666666"; + } + + private static class CodePointLengthBean { + + @CodePointLength(min = 2, max = 4) + private String codePointLength = "666666"; + } + + private static class LuhnCheckBean { + + @LuhnCheck + private String luhnCheck = "4"; + } + + private static class Mod10CheckBean { + + @Mod10Check + private String mod10Check = "4"; + } + + private static class Mod11CheckBean { + + @Mod11Check + private String mod11Check = "4"; + } + + private static class ModCheckBean { + + @ModCheck(multiplier = 2, modType = ModCheck.ModType.MOD10) + private String modCheck = "4"; + } + + private static class NormalizedBean { + + @Normalized(form = java.text.Normalizer.Form.NFKC) + private String normalized = "\uFE64script\uFE65"; + + } + + private static class HvNotBlankBean { + + @org.hibernate.validator.constraints.NotBlank + private String hvNotBlank = ""; + } + + private static class HvNotEmptyBean { + + @org.hibernate.validator.constraints.NotEmpty + private List hvNotEmpty = Collections.emptyList(); + } + + private static class RangeBean { + + @Range(min = 2, max = 4) + private int range = 6; + } + + private static class UniqueElementsBean { + + @UniqueElements + private List uniqueElements = Arrays.asList( "a", "a" ); + } + + private static class URLBean { + + @URL + private String url = "invalid"; + } + + private static class CNPJBean { + + @CNPJ + private String cnpj = "invalid"; + } + + private static class CPFBean { + + @CPF + private String cpf = "invalid"; + } + + private static class TituloEleitoralBean { + + @TituloEleitoral + private String tituloEleitoral = "invalid"; + } + + private static class REGONBean { + + @REGON + private String regon = "invalid"; + } + + private static class NIPBean { + + @NIP + private String nip = "invalid"; + } + + private static class PESELBean { + + @PESEL + private String pesel = "invalid"; + } + + private static class INNBean { + + @INN + private String inn = "invalid"; + } + + private static class DurationMaxBean { + + @DurationMax(days = 4, hours = 4, minutes = 4, millis = 4, nanos = 4) + private Duration durationMax = Duration.ofDays( 8 ); + } + + private static class DurationMinBean { + + @DurationMin(days = 4, hours = 4, minutes = 4, millis = 4, nanos = 4) + private Duration durationMin = Duration.ofDays( 2 ); + } + + @ScriptAssert(lang = "groovy", script = "_this.scriptAssert") + private static class ScriptAssertBean { + + @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..db61ffb393 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 @@ -10,8 +10,10 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import java.lang.annotation.Annotation; import java.math.BigDecimal; import java.math.BigInteger; + import javax.validation.ConstraintValidator; /** @@ -19,7 +21,7 @@ */ public class BaseMinMaxValidatorForNumberTest { - protected void testNumberValidator(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + protected void testNumberValidator(ConstraintValidator constraint, boolean inclusive, boolean isMax) { byte b = 1; Byte bWrapper = 127; if ( inclusive ) { @@ -38,6 +40,18 @@ protected void testNumberValidator(ConstraintValidator constraint, bo assertEquals( constraint.isValid( b, null ), isMax ); assertEquals( constraint.isValid( 14.99, null ), isMax ); assertEquals( constraint.isValid( -14.99, null ), isMax ); + assertEquals( constraint.isValid( 14.99F, null ), isMax ); + assertEquals( constraint.isValid( -14.99F, null ), isMax ); + assertEquals( constraint.isValid( 14, null ), isMax ); + assertEquals( constraint.isValid( 16, null ), !isMax ); + assertEquals( constraint.isValid( (short) 14, null ), isMax ); + assertEquals( constraint.isValid( (short) 16, null ), !isMax ); + assertEquals( constraint.isValid( BigInteger.valueOf( 14L ), null ), isMax ); + assertEquals( constraint.isValid( BigInteger.valueOf( 16L ), null ), !isMax ); + assertEquals( constraint.isValid( BigDecimal.valueOf( 14L ), null ), isMax ); + assertEquals( constraint.isValid( BigDecimal.valueOf( 16L ), null ), !isMax ); + assertEquals( constraint.isValid( new BigDecimal( "14.99" ), null ), isMax ); + assertEquals( constraint.isValid( new BigDecimal( "15.001" ), null ), !isMax ); assertEquals( constraint.isValid( bWrapper, null ), !isMax ); assertEquals( constraint.isValid( 20, null ), !isMax ); } @@ -74,6 +88,51 @@ protected void testValidatorBigInteger(ConstraintValidator constr assertEquals( constraint.isValid( BigInteger.valueOf( 1560000000 ), null ), !isMax ); } + protected void testValidatorByte(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + if ( inclusive ) { + assertTrue( constraint.isValid( (byte) 15, null ) ); + } + else { + assertFalse( constraint.isValid( (byte) 15, null ) ); + } + + assertTrue( constraint.isValid( null, null ) ); + assertEquals( constraint.isValid( (byte) 14, null ), isMax ); + assertEquals( constraint.isValid( (byte) 16, null ), !isMax ); + assertEquals( constraint.isValid( Byte.MIN_VALUE, null ), isMax ); + assertEquals( constraint.isValid( Byte.MAX_VALUE, null ), !isMax ); + } + + protected void testValidatorShort(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + if ( inclusive ) { + assertTrue( constraint.isValid( (short) 15, null ) ); + } + else { + assertFalse( constraint.isValid( (short) 15, null ) ); + } + + assertTrue( constraint.isValid( null, null ) ); + assertEquals( constraint.isValid( (short) 14, null ), isMax ); + assertEquals( constraint.isValid( (short) 16, null ), !isMax ); + assertEquals( constraint.isValid( Short.MIN_VALUE, null ), isMax ); + assertEquals( constraint.isValid( Short.MAX_VALUE, null ), !isMax ); + } + + protected void testValidatorInteger(ConstraintValidator constraint, boolean inclusive, boolean isMax) { + if ( inclusive ) { + assertTrue( constraint.isValid( 15, null ) ); + } + else { + assertFalse( constraint.isValid( 15, null ) ); + } + + assertTrue( constraint.isValid( null, null ) ); + assertEquals( constraint.isValid( 14, null ), isMax ); + assertEquals( constraint.isValid( 16, null ), !isMax ); + assertEquals( constraint.isValid( Integer.MIN_VALUE, null ), isMax ); + assertEquals( constraint.isValid( Integer.MAX_VALUE, null ), !isMax ); + } + protected void testValidatorLong(ConstraintValidator constraint, boolean inclusive, boolean isMax) { if ( inclusive ) { assertTrue( constraint.isValid( 15L, null ) ); @@ -97,6 +156,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/DecimalMinMaxValidatorBoundaryTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/DecimalMinMaxValidatorBoundaryTest.java index 3ae2c4ace2..58b9841f74 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/DecimalMinMaxValidatorBoundaryTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/DecimalMinMaxValidatorBoundaryTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.bv; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -45,7 +44,7 @@ public void setUp() { public void testDecimalMinValue() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( DecimalMinMaxValidatorBoundaryTest.class ) - .property( "d", FIELD ) + .field( "d" ) .constraint( new DecimalMinDef().value( "0.100000000000000005" ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -62,7 +61,7 @@ public void testDecimalMinValue() { public void testDecimalMaxValue() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( DecimalMinMaxValidatorBoundaryTest.class ) - .property( "d", FIELD ) + .field( "d" ) .constraint( new DecimalMaxDef().value( "0.1" ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -79,7 +78,7 @@ public void testDecimalMaxValue() { public void testDoubleTrouble() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( DecimalMinMaxValidatorBoundaryTest.class ) - .property( "d", FIELD ) + .field( "d" ) .constraint( new DecimalMaxDef().value( "1.2" ) ); config.addMapping( mapping ); 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..880eb6771d 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,25 +6,42 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.bv; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +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 static org.testng.Assert.assertFalse; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import javax.validation.Validator; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.Max; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.AbstractMaxValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.AbstractDecimalMaxValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForShort; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.Test; /** @@ -55,6 +72,40 @@ public void testIsValidDecimalMax() { testDecimalMax( m, true ); } + @Test + public void testIsValidDecimalMaxWithDecimalFractionInConstraint() { + class Foo { + @DecimalMax("15.0001") + private final Number num; + + Foo(final Number num) { + this.num = num; + } + } + + Validator validator = getValidator(); + + assertThat( validator.validate( new Foo( 16 ) ) ).containsOnlyViolations( violationOf( DecimalMax.class ) ); + assertNoViolations( validator.validate( new Foo( 15 ) ) ); + + assertThat( validator.validate( new Foo( 15.01 ) ) ).containsOnlyViolations( violationOf( DecimalMax.class ) ); + assertNoViolations( validator.validate( new Foo( 15.00001 ) ) ); + + assertThat( validator.validate( new Foo( BigDecimal.valueOf( 15.01 ) ) ) ).containsOnlyViolations( violationOf( DecimalMax.class ) ); + assertNoViolations( validator.validate( new Foo( BigDecimal.valueOf( 15.00001 ) ) ) ); + } + + @Test + public void testIsValidDecimalMax1() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( DecimalMax.class ); + descriptorBuilder.setAttribute( "value", Integer.toString( Integer.MAX_VALUE - 1 ) ); + DecimalMax m = descriptorBuilder.build().getAnnotation(); + + DecimalMaxValidatorForNumber constraint = new DecimalMaxValidatorForNumber(); + constraint.initialize( m ); + assertFalse( constraint.isValid( Double.POSITIVE_INFINITY, null ) ); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testInitializeDecimalMaxWithInvalidValue() { ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( DecimalMax.class ); @@ -79,6 +130,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 ); @@ -92,6 +160,18 @@ private void testDecimalMax(DecimalMax m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, true ); + constraint = new DecimalMaxValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, true ); + + constraint = new DecimalMaxValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, true ); + + constraint = new DecimalMaxValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, true ); + constraint = new DecimalMaxValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, true ); @@ -118,6 +198,18 @@ private void testMax(Max m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, true ); + constraint = new MaxValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, true ); + + constraint = new MaxValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, true ); + + constraint = new MaxValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, true ); + constraint = new MaxValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, true ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java index f81b2edb50..6a24171fae 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForStringTest.java @@ -14,8 +14,8 @@ import javax.validation.constraints.DecimalMax; import javax.validation.constraints.Max; -import org.hibernate.validator.internal.constraintvalidators.bv.DecimalMaxValidatorForCharSequence; -import org.hibernate.validator.internal.constraintvalidators.bv.MaxValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MaxValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForCharSequence; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMaxValidatorForNumber; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.MyCustomStringImpl; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java index 5482548957..2b1e742649 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForNumberTest.java @@ -6,25 +6,40 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.bv; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +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 javax.validation.Validator; import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Min; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.AbstractMinValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForShort; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.AbstractDecimalMinValidator; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForBigDecimal; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForBigInteger; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForByte; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForDouble; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForFloat; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForLong; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForShort; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.Test; /** @@ -55,6 +70,29 @@ public void testIsValidDecimalMinValidator() { testDecimalMin( m, true ); } + @Test + public void testIsValidDecimalMinWithDecimalFractionInConstraint() { + class Foo { + @DecimalMin("15.0001") + private final Number num; + + Foo(final Number num) { + this.num = num; + } + } + + Validator validator = getValidator(); + + assertThat( validator.validate( new Foo( 15 ) ) ).containsOnlyViolations( violationOf( DecimalMin.class ) ); + assertNoViolations( validator.validate( new Foo( 16 ) ) ); + + assertThat( validator.validate( new Foo( 15.00001 ) ) ).containsOnlyViolations( violationOf( DecimalMin.class ) ); + assertNoViolations( validator.validate( new Foo( 15.01 ) ) ); + + assertThat( validator.validate( new Foo( BigDecimal.valueOf( 15.00001 ) ) ) ).containsOnlyViolations( violationOf( DecimalMin.class ) ); + assertNoViolations( validator.validate( new Foo( BigDecimal.valueOf( 15.01 ) ) ) ); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testInitializeDecimalMinWithInvalidValue() { ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( DecimalMin.class ); @@ -92,6 +130,18 @@ private void testDecimalMin(DecimalMin m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, false ); + constraint = new DecimalMinValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, false ); + + constraint = new DecimalMinValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, false ); + + constraint = new DecimalMinValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, false ); + constraint = new DecimalMinValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, false ); @@ -118,6 +168,18 @@ private void testMin(Min m, boolean inclusive) { constraint.initialize( m ); testValidatorBigInteger( constraint, inclusive, false ); + constraint = new MinValidatorForByte(); + constraint.initialize( m ); + testValidatorByte( constraint, inclusive, false ); + + constraint = new MinValidatorForShort(); + constraint.initialize( m ); + testValidatorShort( constraint, inclusive, false ); + + constraint = new MinValidatorForInteger(); + constraint.initialize( m ); + testValidatorInteger( constraint, inclusive, false ); + constraint = new MinValidatorForLong(); constraint.initialize( m ); testValidatorLong( constraint, inclusive, false ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java index 4701cd58e1..5a06df9e86 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MinValidatorForStringTest.java @@ -14,8 +14,8 @@ import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Min; -import org.hibernate.validator.internal.constraintvalidators.bv.DecimalMinValidatorForCharSequence; -import org.hibernate.validator.internal.constraintvalidators.bv.MinValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForCharSequence; import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForNumber; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.MyCustomStringImpl; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/NegativePositiveValidatorForStringTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/NegativePositiveValidatorForStringTest.java new file mode 100644 index 0000000000..f702729915 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/NegativePositiveValidatorForStringTest.java @@ -0,0 +1,108 @@ +/* + * 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; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import javax.validation.constraints.Negative; +import javax.validation.constraints.NegativeOrZero; +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; + +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.NegativeOrZeroValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.NegativeValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.PositiveOrZeroValidatorForCharSequence; +import org.hibernate.validator.internal.constraintvalidators.bv.number.sign.PositiveValidatorForCharSequence; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + */ +public class NegativePositiveValidatorForStringTest { + + @Test + public void testIsValidPositiveValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Positive.class ); + descriptorBuilder.setMessage( "{validator.positive}" ); + Positive m = descriptorBuilder.build().getAnnotation(); + + PositiveValidatorForCharSequence constraint = new PositiveValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( "15", null ) ); + assertTrue( constraint.isValid( "15.0", null ) ); + assertFalse( constraint.isValid( "0", null ) ); + assertFalse( constraint.isValid( "-10", null ) ); + assertFalse( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } + + @Test + public void testIsValidPositiveOrZeroValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( PositiveOrZero.class ); + descriptorBuilder.setMessage( "{validator.positiveOrZero}" ); + PositiveOrZero m = descriptorBuilder.build().getAnnotation(); + + PositiveOrZeroValidatorForCharSequence constraint = new PositiveOrZeroValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( "15", null ) ); + assertTrue( constraint.isValid( "15.0", null ) ); + assertTrue( constraint.isValid( "0", null ) ); + assertFalse( constraint.isValid( "-10", null ) ); + assertFalse( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } + + @Test + public void testIsValidNegativeValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Negative.class ); + descriptorBuilder.setMessage( "{validator.negative}" ); + Negative m = descriptorBuilder.build().getAnnotation(); + + NegativeValidatorForCharSequence constraint = new NegativeValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertFalse( constraint.isValid( "15", null ) ); + assertFalse( constraint.isValid( "15.0", null ) ); + assertFalse( constraint.isValid( "0", null ) ); + assertTrue( constraint.isValid( "-10", null ) ); + assertTrue( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } + + @Test + public void testIsValidNegativeOrZeroValidator() { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( NegativeOrZero.class ); + descriptorBuilder.setMessage( "{validator.negativeOrZero}" ); + NegativeOrZero m = descriptorBuilder.build().getAnnotation(); + + NegativeOrZeroValidatorForCharSequence constraint = new NegativeOrZeroValidatorForCharSequence(); + constraint.initialize( m ); + + assertTrue( constraint.isValid( null, null ) ); + assertFalse( constraint.isValid( "15", null ) ); + assertFalse( constraint.isValid( "15.0", null ) ); + assertTrue( constraint.isValid( "0", null ) ); + assertTrue( constraint.isValid( "-10", null ) ); + assertTrue( constraint.isValid( "-14.99", null ) ); + + // number format exception + assertFalse( constraint.isValid( "15l", null ) ); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java index 9768d6daa3..540832606e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/CurrencyValidatorForMonetaryAmountTest.java @@ -11,7 +11,6 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; -import java.lang.annotation.ElementType; import java.util.Set; import javax.money.MonetaryAmount; @@ -75,7 +74,7 @@ public void programmaticDefinition() { ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Order.class ) .ignoreAllAnnotations() - .property( "amount", ElementType.FIELD ) + .field( "amount" ) .constraint( new CurrencyDef().value( "EUR", "USD" ) ); Validator validator = config.addMapping( mapping ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmountTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmountTest.java new file mode 100644 index 0000000000..2fc8739eb1 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/money/DigitsValidatorForMonetaryAmountTest.java @@ -0,0 +1,117 @@ +/* + * 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.money; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.math.BigDecimal; +import javax.money.CurrencyUnit; +import javax.money.Monetary; +import javax.validation.constraints.Digits; + +import org.hibernate.validator.internal.constraintvalidators.bv.money.DigitsValidatorForMonetaryAmount; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.testutil.TestForIssue; + +import org.javamoney.moneta.Money; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Dario Seidl + */ +@TestForIssue(jiraKey = "HV-1723") +public class DigitsValidatorForMonetaryAmountTest { + + private ConstraintAnnotationDescriptor.Builder descriptorBuilder; + + @BeforeMethod + public void setUp() { + descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Digits.class ); + descriptorBuilder.setMessage( "{validator.digits}" ); + } + + @Test + public void testIsValid() { + descriptorBuilder.setAttribute( "integer", 5 ); + descriptorBuilder.setAttribute( "fraction", 2 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + + CurrencyUnit currency = Monetary.getCurrency( "EUR" ); + + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( Money.of( Byte.valueOf( "0" ), currency ), null ) ); + assertTrue( constraint.isValid( Money.of( Double.valueOf( "500.2" ), currency ), null ) ); + + assertTrue( constraint.isValid( Money.of( new BigDecimal( "-12345.12" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "-123456.12" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "-123456.123" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "-12345.123" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( new BigDecimal( "12345.123" ), currency ), null ) ); + + assertTrue( constraint.isValid( Money.of( Float.valueOf( "-000000000.22" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( Integer.valueOf( "256874" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( Double.valueOf( "12.0001" ), currency ), null ) ); + } + + @Test + public void testIsValidZeroLength() { + descriptorBuilder.setAttribute( "integer", 0 ); + descriptorBuilder.setAttribute( "fraction", 0 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + + CurrencyUnit currency = Monetary.getCurrency( "EUR" ); + + assertTrue( constraint.isValid( null, null ) ); + assertFalse( constraint.isValid( Money.of( Byte.valueOf( "0" ), currency ), null ) ); + assertFalse( constraint.isValid( Money.of( Double.valueOf( "500.2" ), currency ), null ) ); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNegativeIntegerLength() { + descriptorBuilder.setAttribute( "integer", -1 ); + descriptorBuilder.setAttribute( "fraction", 1 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNegativeFractionLength() { + descriptorBuilder.setAttribute( "integer", 1 ); + descriptorBuilder.setAttribute( "fraction", -1 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + } + + @Test + public void testTrailingZerosAreTrimmed() { + descriptorBuilder.setAttribute( "integer", 12 ); + descriptorBuilder.setAttribute( "fraction", 3 ); + Digits p = descriptorBuilder.build().getAnnotation(); + + DigitsValidatorForMonetaryAmount constraint = new DigitsValidatorForMonetaryAmount(); + constraint.initialize( p ); + + CurrencyUnit currency = Monetary.getCurrency( "EUR" ); + + assertTrue( constraint.isValid( Money.of( 0.001d, currency ), null ) ); + assertTrue( constraint.isValid( Money.of( 0.00100d, currency ), null ) ); + assertFalse( constraint.isValid( Money.of( 0.0001d, currency ), null ) ); + } + +} 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/hv/BlankValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/BlankValidatorTest.java index 6d28835bdd..9da8f9d987 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/BlankValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/BlankValidatorTest.java @@ -26,6 +26,7 @@ /** * @author Hardy Ferentschik */ +@SuppressWarnings("deprecation") public class BlankValidatorTest { @Test public void testConstraintValidator() { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java index 0054b58be9..5cedfb9893 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN13Test.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -80,7 +79,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Product.class ) - .property( "ean", FIELD ) + .field( "ean" ) .constraint( new EANDef().type( EAN.Type.EAN13 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java index 9b7e23be31..ba38c8e7b8 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/EAN8Test.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -80,7 +79,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( Product.class ) - .property( "ean", FIELD ) + .field( "ean" ) .constraint( new EANDef().type( EAN.Type.EAN8 ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); 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..88d0f43ffc 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 @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; @@ -25,6 +24,7 @@ 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.StringHelper; import org.hibernate.validator.testutil.MyCustomStringImpl; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ValidatorUtil; @@ -157,7 +157,7 @@ public void testEmailRegExp() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( EmailContainer.class ) - .property( "email", METHOD ) + .getter( "email" ) .constraint( new EmailDef().regexp( noOrgEmailAddressRegexp ) .message( "ORG addresses are not valid" ) @@ -255,7 +255,7 @@ private void assertOrgAddressesAreNotValid(Set. + */ +package org.hibernate.validator.test.internal.constraintvalidators.hv; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import org.hibernate.validator.constraints.Normalized; +import org.hibernate.validator.internal.constraintvalidators.hv.NormalizedValidator; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.testutil.MyCustomStringImpl; + +import java.text.Normalizer; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Tests the {@link Normalized} constraint. + * + * @author Kazuki Shimizu + */ +public class NormalizedValidatorTest { + + private ConstraintAnnotationDescriptor.Builder descriptorBuilder; + + @BeforeMethod + public void setUp() throws Exception { + descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Normalized.class ); + } + + @Test + public void testIsValid() { + descriptorBuilder.setMessage( "{validator.Normalized}" ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( null, null ) ); + assertTrue( constraint.isValid( "", null ) ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertTrue( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidCharSequence() { + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( new MyCustomStringImpl( "foobar" ), null ) ); + assertTrue( constraint.isValid( new MyCustomStringImpl( "\uFE64script\uFE65" ), null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfc() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFC ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertTrue( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfkc() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFKC ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertFalse( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfd() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFD ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertTrue( constraint.isValid( "\uFE64script\uFE65", null ) ); + } + + @Test + public void testIsValidNormalizationStrategyIsNfkd() { + descriptorBuilder.setAttribute( "form", Normalizer.Form.NFKD ); + Normalized l = descriptorBuilder.build().getAnnotation(); + NormalizedValidator constraint = new NormalizedValidator(); + constraint.initialize( l ); + assertTrue( constraint.isValid( "foobar", null ) ); + assertFalse( constraint.isValid( "\uFE64script\uFE65", null ) ); + } +} 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 deleted file mode 100644 index 7eb9e31737..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/SafeHtmlValidatorTest.java +++ /dev/null @@ -1,254 +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.constraintvalidators.hv; - -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; -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 static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.util.Set; - -import javax.validation.ConstraintViolation; -import javax.validation.Validator; - -import org.hibernate.validator.constraints.SafeHtml; -import org.hibernate.validator.constraints.SafeHtml.WhiteListType; -import org.hibernate.validator.internal.constraintvalidators.hv.SafeHtmlValidator; -import org.hibernate.validator.internal.util.annotation.AnnotationDescriptor; -import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; -import org.hibernate.validator.testutil.TestForIssue; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -/** - * Unit test for {@link SafeHtmlValidator}. - * - * @author George Gastaldi - * @author Hardy Ferentschik - * @author Marko Bekhta - */ -public class SafeHtmlValidatorTest { - - private ConstraintAnnotationDescriptor.Builder descriptorBuilder; - - @BeforeMethod - public void setUp() { - descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( SafeHtml.class ); - } - - @Test - public void testNullValue() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertTrue( getSafeHtmlValidator().isValid( null, null ) ); - } - - @Test - public void testInvalidScriptTagIncluded() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "HelloWorld !", null ) ); - } - - @Test - public void testInvalidIncompleteImgTagWithScriptIncluded() { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - assertFalse( getSafeHtmlValidator().isValid( "Link

", null ) ); - } - - @Test - public void testAdditionalTags() throws Exception { - descriptorBuilder.setAttribute( "additionalTags", new String[] { "script" } ); - - assertTrue( getSafeHtmlValidator().isValid( "HelloWorld !", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivNotAllowedInBasicWhiteList() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.BASIC ); - - SafeHtmlValidator validator = getSafeHtmlValidator(); - assertFalse( validator.isValid( "
test
", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivAllowedInRelaxedWhiteList() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - - assertTrue( getSafeHtmlValidator().isValid( "
test
", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivWithWhiteListedClassAttribute() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - - AnnotationDescriptor.Builder tagDescriptorBuilder = new AnnotationDescriptor.Builder<>( SafeHtml.Tag.class ); - tagDescriptorBuilder.setAttribute( "name", "div" ); - tagDescriptorBuilder.setAttribute( "attributes", new String[] { "class" } ); - SafeHtml.Tag tag = tagDescriptorBuilder.build().getAnnotation(); - descriptorBuilder.setAttribute( "additionalTagsWithAttributes", new SafeHtml.Tag[] { tag } ); - - assertTrue( - getSafeHtmlValidator().isValid( "
test
", null ), - "class attribute should be white listed" - ); - assertFalse( - getSafeHtmlValidator().isValid( "
test
", null ), - "style attribute is not white listed" - ); - } - - @Test - @TestForIssue(jiraKey = "HV-817") - public void testDivWithWhiteListedStyleAttribute() throws Exception { - Validator validator = getValidator(); - Set> constraintViolations = validator.validate( new Foo( "
test
" ) ); - assertNoViolations( constraintViolations ); - - // the attributes are optional - allowing
also allows just
- constraintViolations = validator.validate( new Foo( "
test
" ) ); - assertNoViolations( constraintViolations ); - - constraintViolations = validator.validate( new Foo( "
test
" ) ); - assertThat( constraintViolations ).containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - } - - @Test - @TestForIssue(jiraKey = "HV-873") - public void testValidationOfInvalidFragment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.NONE ); - - assertFalse( getSafeHtmlValidator().isValid( "1234qwer", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-873") - public void testValidationOfValidFragment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - - assertTrue( getSafeHtmlValidator().isValid( "1234qwer", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-873") - public void testValidationOfTextFragment() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.NONE ); - - assertTrue( getSafeHtmlValidator().isValid( "Foobar", null ) ); - } - - @Test - @TestForIssue(jiraKey = "HV-1302") - public void testAdditionalProtocols() { - Validator validator = getValidator(); - - assertNoViolations( validator.validate( new Bar( "" ) ) ); - assertNoViolations( validator.validate( new Bar( "" ) ) ); - assertThat( validator.validate( new Bar( "" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertThat( validator.validate( new Bar( "" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertThat( validator.validate( new Bar( "
" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertThat( validator.validate( new Bar( "
" ) ) ) - .containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - assertNoViolations( validator.validate( new Bar( - "" + - " " + - " " + - " " + - " " + - "
" + - " " + - "" ) ) ); - assertThat( validator.validate( new Bar( - "
" + - "" + - "" + - "/
" - ) ) ).containsOnlyViolations( - violationOf( SafeHtml.class ) - ); - } - - @Test - @TestForIssue(jiraKey = "HV-1303") - public void testPreserveRelativeLinks() throws Exception { - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - descriptorBuilder.setAttribute( "baseURI", "http://127.0.0.1" ); - - assertTrue( getSafeHtmlValidator().isValid( "", null ) ); - - descriptorBuilder.setAttribute( "whitelistType", WhiteListType.RELAXED ); - descriptorBuilder.setAttribute( "baseURI", "" ); - - assertFalse( getSafeHtmlValidator().isValid( "", null ) ); - } - - private SafeHtmlValidator getSafeHtmlValidator() { - SafeHtml p = descriptorBuilder.build().getAnnotation(); - SafeHtmlValidator validator = new SafeHtmlValidator(); - validator.initialize( p ); - return validator; - } - - public static class Foo { - @SafeHtml( - whitelistType = WhiteListType.BASIC, - additionalTagsWithAttributes = @SafeHtml.Tag(name = "div", attributes = { "style" }) - ) - String source; - - public Foo(String source) { - this.source = source; - } - } - - public static class Bar { - @SafeHtml( - whitelistType = WhiteListType.BASIC, - additionalTagsWithAttributes = { - @SafeHtml.Tag(name = "img", attributesWithProtocols = @SafeHtml.Attribute(name = "src", protocols = { "data" })), - @SafeHtml.Tag(name = "custom", attributesWithProtocols = { - @SafeHtml.Attribute(name = "attr1", protocols = { "dataprotocol", "strange_protocol" }), - @SafeHtml.Attribute(name = "attr2", protocols = { "dataprotocol", "strange_protocol" }), - @SafeHtml.Attribute(name = "attr3", protocols = "some_protocol") - }), - @SafeHtml.Tag(name = "section", attributes = { "attr", "id" }) - } - ) - String source; - - public Bar(String source) { - this.source = source; - } - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java index 20d698abc8..95efc32d4c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ScriptAssertValidatorTest.java @@ -43,14 +43,16 @@ public class ScriptAssertValidatorTest extends AbstractConstrainedTest { @Test public void scriptEvaluatesToTrue() throws Exception { - @ScriptAssert(lang = "groovy", script = "true") class TmpType { } + @ScriptAssert(lang = "groovy", script = "true") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test public void scriptEvaluatesToFalse() throws Exception { - @ScriptAssert(lang = "groovy", script = "false") class TmpType { } + @ScriptAssert(lang = "groovy", script = "false") class TmpType { + } assertThat( validator.validate( new TmpType() ) ).containsOnlyViolations( violationOf( ScriptAssert.class ) ); @@ -107,28 +109,32 @@ public void emptyAliasRaisesException() throws Exception { @Test(expectedExceptions = ConstraintDeclarationException.class) public void unknownLanguageNameRaisesException() throws Exception { - @ScriptAssert(lang = "foo", script = "script") class TmpType { } + @ScriptAssert(lang = "foo", script = "script") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test(expectedExceptions = ConstraintDeclarationException.class) public void illegalScriptExpressionRaisesException() throws Exception { - @ScriptAssert(lang = "groovy", script = "foo") class TmpType { } + @ScriptAssert(lang = "groovy", script = "foo") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test(expectedExceptions = ConstraintDeclarationException.class) public void scriptExpressionReturningNullRaisesException() throws Exception { - @ScriptAssert(lang = "groovy", script = "null") class TmpType { } + @ScriptAssert(lang = "groovy", script = "null") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } @Test(expectedExceptions = ConstraintDeclarationException.class) public void scriptExpressionReturningNoBooleanRaisesException() throws Exception { - @ScriptAssert(lang = "groovy", script = "new java.util.Date()") class TmpType { } + @ScriptAssert(lang = "groovy", script = "new java.util.Date()") class TmpType { + } assertNoViolations( validator.validate( new TmpType() ) ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java index 844a48a313..c9b001c65f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/URLValidatorTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import static java.lang.annotation.ElementType.METHOD; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; @@ -172,7 +171,7 @@ public void explicit_regular_expression_can_be_specified_via_programmatic_config HibernateValidatorConfiguration config = ValidatorUtil.getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( URLContainer.class ) - .property( "url", METHOD ) + .getter( "url" ) .constraint( new URLDef().regexp( "^http://\\S+[\\.htm|\\.html]{1}$" ) ); config.addMapping( mapping ); Validator validator = config.buildValidatorFactory().getValidator(); @@ -195,7 +194,7 @@ public void optional_regular_expression_can_be_refined_with_flags_using_programm HibernateValidatorConfiguration config = ValidatorUtil.getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( URLContainer.class ) - .property( "url", METHOD ) + .getter( "url" ) .constraint( new URLDef().regexp( "^http://\\S+[\\.htm|\\.html]{1}$" ).flags( Flag.CASE_INSENSITIVE ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ru/INNValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ru/INNValidatorTest.java new file mode 100644 index 0000000000..24ff8691ed --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/ru/INNValidatorTest.java @@ -0,0 +1,132 @@ +/* + * 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.hv.ru; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import org.hibernate.validator.constraints.ru.INN; +import org.hibernate.validator.internal.constraintvalidators.hv.ru.INNValidator; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * A set of tests for {@link INN} constraint validator ({@link INNValidator}), + * which make sure that validation is performed correctly. + * + * @author Artem Boiarshinov + * @see fake INN generator + */ +public class INNValidatorTest { + + private INNValidator validator; + + @BeforeMethod + public void setUp() { + validator = new INNValidator(); + } + + @Test + public void validIndividualTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.INDIVIDUAL ) ); + + assertValidINN( null ); + assertValidINN( "246964567008" ); + assertValidINN( "356393289962" ); + assertValidINN( "279837166431" ); + assertValidINN( "827175083460" ); + assertValidINN( "789429596404" ); + assertValidINN( "929603416330" ); + assertValidINN( "086229647992" ); + } + + @Test + public void invalidIndividualTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.INDIVIDUAL ) ); + + //invalid checksum + assertInvalidINN( "012345678912" ); + assertInvalidINN( "246964567009" ); + + //invalid symbols + assertInvalidINN( "a46964567008" ); + + //invalid length + assertInvalidINN( "" ); + assertInvalidINN( "90660563173" ); + assertInvalidINN( "9066056317378" ); + + //invalid type + assertInvalidINN( "4546366155" ); + } + + @Test + public void validJuridicalTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.JURIDICAL ) ); + + assertValidINN( null ); + assertValidINN( "0305773929" ); + assertValidINN( "5496344268" ); + assertValidINN( "0314580754" ); + assertValidINN( "8652697156" ); + assertValidINN( "3527694367" ); + assertValidINN( "8771236130" ); + assertValidINN( "9254906927" ); + } + + @Test + public void invalidJuridicalTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.JURIDICAL ) ); + + //invalid checksum + assertInvalidINN( "0123456789" ); + assertInvalidINN( "0305773928" ); + + //invalid symbols + assertInvalidINN( "a305773929" ); + + //invalid length + assertInvalidINN( "" ); + assertInvalidINN( "906605631" ); + assertInvalidINN( "90660563173" ); + + //invalid type + assertInvalidINN( "246964567008" ); + } + + @Test + public void testAnyTypeINN() { + validator.initialize( initializeAnnotation( INN.Type.ANY ) ); + + final String personalValidINN = "246964567008"; + final String juridicalValidINN = "5496344268"; + final String personalInvalidINN = "246964567009"; + final String juridicalInvalidINN = "0305773928"; + + assertValidINN( null ); + assertValidINN( personalValidINN ); + assertValidINN( juridicalValidINN ); + assertInvalidINN( personalInvalidINN ); + assertInvalidINN( juridicalInvalidINN ); + } + + private void assertValidINN(String inn) { + assertTrue( validator.isValid( inn, null ), inn + " should be a valid INN" ); + } + + private void assertInvalidINN(String inn) { + assertFalse( validator.isValid( inn, null ), inn + " should be a invalid INN" ); + } + + private INN initializeAnnotation(INN.Type type) { + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( INN.class ); + descriptorBuilder.setAttribute( "type", type ); + return descriptorBuilder.build().getAnnotation(); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java index 6d1065746d..cd1f35d516 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMaxValidatorTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv.time; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -76,7 +75,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMaxDef() .days( 1 ).hours( 1 ) .minutes( 1 ).seconds( 1 ) @@ -102,7 +101,7 @@ public void testMessage() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class, Locale.ENGLISH ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMaxDef() .days( 1 ) .nanos( 100 ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java index 3adee0f35c..4ca62f9953 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/time/DurationMinValidatorTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv.time; -import static java.lang.annotation.ElementType.FIELD; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; @@ -76,7 +75,7 @@ public void testProgrammaticConstraint() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMinDef() .days( 1 ).hours( 1 ) .minutes( 1 ).seconds( 1 ) @@ -102,7 +101,7 @@ public void testMessage() { final HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class, Locale.ENGLISH ); ConstraintMapping mapping = config.createConstraintMapping(); mapping.type( AnotherTask.class ) - .property( "timeToComplete", FIELD ) + .field( "timeToComplete" ) .constraint( new DurationMinDef() .days( 30 ) .hours( 12 ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java index f9cbdbb3b3..a983610039 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/ConstraintValidatorCachingTest.java @@ -41,7 +41,7 @@ import javax.validation.constraints.Size; import org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator; -import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForNumber; +import org.hibernate.validator.internal.constraintvalidators.bv.number.bound.MinValidatorForInteger; import org.hibernate.validator.internal.constraintvalidators.bv.size.SizeValidatorForCollection; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; import org.hibernate.validator.testutil.TestForIssue; @@ -78,7 +78,7 @@ public void testConstraintValidatorInstancesAreCached() { constraintValidatorFactory.assertSize( 3 ); constraintValidatorFactory.assertKeyExists( SizeValidatorForCollection.class ); - constraintValidatorFactory.assertKeyExists( MinValidatorForNumber.class ); + constraintValidatorFactory.assertKeyExists( MinValidatorForInteger.class ); constraintValidatorFactory.assertKeyExists( NotNullValidator.class ); // getting a new validator from the same factory should have the same instances cached diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/EnhancedBeanAccessorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/EnhancedBeanAccessorTest.java new file mode 100644 index 0000000000..0671f6feed --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/EnhancedBeanAccessorTest.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.test.internal.engine; + +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.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Positive; + +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +@TestForIssue(jiraKey = "HV-1680") +public class EnhancedBeanAccessorTest { + @Test + public void testValidatePropertyWithRedefinedDefaultGroupOnMainEntity() { + Validator validator = getValidator(); + Foo foo = new Bar(); + + Set> constraintViolations = validator.validate( foo ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( Pattern.class ).withProperty( "string" ), + violationOf( Positive.class ).withProperty( "num" ), + violationOf( Min.class ).withProperty( "looooong" ), + violationOf( Length.class ).withProperty( "message" ), + violationOf( AssertTrue.class ).withProperty( "key" ), + violationOf( NotEmpty.class ).withProperty( "strings" ) + ); + } + + private static class Bar extends Foo implements HibernateValidatorEnhancedBean { + @NotEmpty + private final List strings; + + private Bar() { + this.strings = Collections.emptyList(); + } + + @Override + public Object $$_hibernateValidator_getFieldValue(String name) { + if ( "strings".equals( name ) ) { + return strings; + } + return super.$$_hibernateValidator_getFieldValue( name ); + } + } + + private static class Foo implements HibernateValidatorEnhancedBean { + @Pattern(regexp = "[A-Z]") + private String string; + @Positive + private Integer num; + @Min(100_000L) + private long looooong; + + public Foo() { + this( "test", -1, 100L ); + } + + public Foo(final String string, final Integer num, final long looooong) { + this.string = string; + this.num = num; + this.looooong = looooong; + } + + @Length(min = 100) + public String getMessage() { + return "messssssage"; + } + + @AssertTrue + public boolean getKey() { + return false; + } + + @Override + public Object $$_hibernateValidator_getFieldValue(String name) { + if ( "string".equals( name ) ) { + return string; + } + if ( "num".equals( name ) ) { + return num; + } + if ( "looooong".equals( name ) ) { + return looooong; + } + throw new IllegalArgumentException( "No such property as '" + name + "'" ); + } + + @Override + public Object $$_hibernateValidator_getGetterValue(String name) { + if ( "getKey".equals( name ) ) { + return getKey(); + } + if ( "getMessage".equals( name ) ) { + return getMessage(); + } + throw new IllegalArgumentException( "No such property as '" + name + "'" ); + } + } +} 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/ConstraintValidatorManagerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/ConstraintValidatorManagerTest.java index 26f5df550f..42a9a4badf 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/ConstraintValidatorManagerTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/constraintvalidation/ConstraintValidatorManagerTest.java @@ -36,7 +36,7 @@ import org.hibernate.validator.internal.constraintvalidators.bv.NotNullValidator; import org.hibernate.validator.internal.engine.DefaultClockProvider; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; -import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -49,14 +49,14 @@ * @author Gunnar Morling */ public class ConstraintValidatorManagerTest { - private ConstraintValidatorManager constraintValidatorManager; + private ConstraintValidatorManagerImpl constraintValidatorManager; private ConstraintValidatorFactory constraintValidatorFactory; private Validator validator; @BeforeMethod public void setUp() { constraintValidatorFactory = new ConstraintValidatorFactoryImpl(); - constraintValidatorManager = new ConstraintValidatorManager( constraintValidatorFactory, getDummyConstraintValidatorInitializationContext() ); + constraintValidatorManager = new ConstraintValidatorManagerImpl( constraintValidatorFactory, getDummyConstraintValidatorInitializationContext() ); validator = getValidator(); } 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..3501d164c6 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 @@ -45,6 +45,7 @@ public class HibernateConstraintValidatorContextTest { private static final String QUESTION_2 = "What is 1+1 and what is the answer to life?"; private static final String QUESTION_3 = "This is a trick question"; private static final String QUESTION_4 = "What keywords are not allowed?"; + private static final String QUESTION_5 = "What is 1+1 and what is the answer to life? But I won't get the right answer as Expression Language is disabled"; private static final List INVALID_KEYWORDS = Lists.newArrayList( "foo", "bar", "baz" ); @@ -130,7 +131,7 @@ public void testSettingInvalidCustomExpressionVariable() { @Test @TestForIssue(jiraKey = "HV-701") - public void testCreatingMultipleConstraintViolationWithExpressionVariables() { + public void testCreatingMultipleConstraintViolationWithExpressionVariablesWithExpressionLanguageEnabled() { Validator validator = getValidator(); Set> constraintViolations = validator.validate( new ExpressionVariableFoo( QUESTION_2 ) ); @@ -223,6 +224,18 @@ public void testNullIsReturnedIfPayloadIsNull() { Assert.assertNull( hibernateConstraintViolation.getDynamicPayload( Object.class ) ); } + @Test + @TestForIssue(jiraKey = "HV-1816") + public void testCreatingMultipleConstraintViolationWithExpressionVariables() { + Validator validator = getValidator(); + Set> constraintViolations = validator.validate( new ExpressionVariableFoo( QUESTION_5 ) ); + + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( ExpressionVariableOracleConstraint.class ).withMessage( "answer 1: ${answer}" ), + violationOf( ExpressionVariableOracleConstraint.class ).withMessage( "answer 2: ${answer}" ) + ); + } + public class MessageParameterFoo { @MessageParameterOracleConstraint private final String question; @@ -323,7 +336,7 @@ public boolean isValid(String question, ConstraintValidatorContext context) { createSingleConstraintViolation( hibernateContext ); } else if ( question.equals( QUESTION_2 ) ) { - createMultipleConstraintViolationsUpdatingExpressionVariableValues( hibernateContext ); + createMultipleConstraintViolationsUpdatingExpressionVariableValuesWithExpressionLanguageEnabled( hibernateContext ); } else if ( question.equals( QUESTION_3 ) ) { hibernateContext.addExpressionVariable( "answer", "${foo}" ); @@ -331,6 +344,9 @@ else if ( question.equals( QUESTION_3 ) ) { else if ( question.equals( QUESTION_4 ) ) { hibernateContext.withDynamicPayload( INVALID_KEYWORDS ); } + else if ( question.equals( QUESTION_5 ) ) { + createMultipleConstraintViolationsUpdatingExpressionVariableValues( hibernateContext ); + } else { tryingToIllegallyUseNullExpressionVariableName( hibernateContext ); } @@ -343,6 +359,22 @@ private void tryingToIllegallyUseNullExpressionVariableName(HibernateConstraintV hibernateContext.addMessageParameter( null, "foo" ); } + private void createMultipleConstraintViolationsUpdatingExpressionVariableValuesWithExpressionLanguageEnabled( + HibernateConstraintValidatorContext hibernateContext) { + hibernateContext.disableDefaultConstraintViolation(); + + hibernateContext.addExpressionVariable( "answer", 2 ); + hibernateContext.buildConstraintViolationWithTemplate( "answer 1: ${answer}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + + // resetting the expression variables + hibernateContext.addExpressionVariable( "answer", 42 ); + hibernateContext.buildConstraintViolationWithTemplate( "answer 2: ${answer}" ) + .enableExpressionLanguage() + .addConstraintViolation(); + } + private void createMultipleConstraintViolationsUpdatingExpressionVariableValues(HibernateConstraintValidatorContext hibernateContext) { hibernateContext.disableDefaultConstraintViolation(); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java index 1098e29f1f..a75b3c71df 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/failfast/FailFastTest.java @@ -30,16 +30,16 @@ import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.ValidatorFactory; +import javax.validation.constraints.Email; import javax.validation.constraints.Max; import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.HibernateValidatorFactory; -import org.hibernate.validator.constraints.Email; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.testutil.TestForIssue; @@ -162,8 +162,9 @@ public void testFailFastMethodValidationSetOnValidatorFactory() { fail(); } catch (ConstraintViolationException e) { - assertThat( e.getConstraintViolations() ).containsOnlyViolations( - violationOf( NotBlank.class ) + assertThat( e.getConstraintViolations() ).containsOneOfViolations( + violationOf( NotBlank.class ), + violationOf( Min.class ) ); } } @@ -212,8 +213,9 @@ public void testFailFastMethodValidationSetWithProperty() { fail(); } catch (ConstraintViolationException e) { - assertThat( e.getConstraintViolations() ).containsOnlyViolations( - violationOf( NotBlank.class ) + assertThat( e.getConstraintViolations() ).containsOneOfViolations( + violationOf( NotBlank.class ), + violationOf( Min.class ) ); } } 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/DefaultLocaleMessageInterpolationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/DefaultLocaleMessageInterpolationTest.java new file mode 100644 index 0000000000..b72dc9236d --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/DefaultLocaleMessageInterpolationTest.java @@ -0,0 +1,76 @@ +/* + * 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.assertj.core.api.Assertions.assertThat; + +import java.util.Locale; + +import javax.validation.MessageInterpolator; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.HibernateValidator; +import org.testng.annotations.Test; + +public class DefaultLocaleMessageInterpolationTest { + + @Test + public void testNoDefaultLocaleDefinedStillWorking() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + + MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); + + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "must be false" ); + } + + @Test + public void testDefaultLocaleHonored() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .defaultLocale( Locale.FRANCE ) + .buildValidatorFactory(); + + MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); + + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "doit avoir la valeur faux" ); + + validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .defaultLocale( Locale.ITALY ) + .buildValidatorFactory(); + + messageInterpolator = validatorFactory.getMessageInterpolator(); + + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "deve essere false" ); + } + + private static class TestContext implements MessageInterpolator.Context { + + @Override + public ConstraintDescriptor getConstraintDescriptor() { + return null; + } + + @Override + public Object getValidatedValue() { + return null; + } + + @SuppressWarnings("unchecked") + @Override + public T unwrap(Class type) { + return (T) this; + } + } +} 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..6f75a91b19 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 @@ -18,10 +18,11 @@ import org.hibernate.validator.internal.engine.MessageInterpolatorContext; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.testutil.TestForIssue; - import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -41,18 +42,18 @@ public void setUp() { // Create some annotations for testing using AnnotationProxies ConstraintAnnotationDescriptor.Builder notNullAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( NotNull.class ); notNullDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, notNullAnnotationDescriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); ConstraintAnnotationDescriptor.Builder sizeAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Size.class ); sizeDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, sizeAnnotationDescriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); interpolatorUnderTest = new ResourceBundleMessageInterpolator(); @@ -66,24 +67,66 @@ public void testExpressionLanguageGraphNavigation() { notNullDescriptor, user, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); String expected = "18"; String actual = interpolatorUnderTest.interpolate( "${validatedValue.age}", context ); assertEquals( actual, expected, "Wrong substitution" ); } + @Test + public void testExpressionLanguageGraphNavigationBeanProperties() { + User user = new User(); + user.setAge( 18 ); + MessageInterpolator.Context context = new MessageInterpolatorContext( + notNullDescriptor, + user, + null, + null, + Collections.emptyMap(), + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_PROPERTIES, + false ); + + String expected = "18"; + String actual = interpolatorUnderTest.interpolate( "${validatedValue.age}", context ); + assertEquals( actual, expected, "Wrong substitution" ); + } + + @Test + public void testExpressionLanguageGraphNavigationVariables() { + User user = new User(); + user.setAge( 18 ); + MessageInterpolator.Context context = new MessageInterpolatorContext( + notNullDescriptor, + user, + null, + null, + Collections.emptyMap(), + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.VARIABLES, + false ); + + String expected = "${validatedValue.age}"; + String actual = interpolatorUnderTest.interpolate( "${validatedValue.age}", context ); + assertEquals( actual, expected, "Wrong substitution" ); + } + @Test public void testUnknownPropertyInExpressionLanguageGraphNavigation() { MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, new User(), null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); String expected = "${validatedValue.foo}"; String actual = interpolatorUnderTest.interpolate( "${validatedValue.foo}", context ); @@ -92,7 +135,7 @@ public void testUnknownPropertyInExpressionLanguageGraphNavigation() { @Test public void testNullValidatedValue() { - MessageInterpolator.Context context = createMessageInterpolatorContext( notNullDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( notNullDescriptor ); String expected = "Validated value was null"; String actual = interpolatorUnderTest.interpolate( @@ -104,7 +147,7 @@ public void testNullValidatedValue() { @Test public void testExpressionAndParameterInterpolationInSameMessageDescriptor() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "2 0 2147483647"; String actual = interpolatorUnderTest.interpolate( "${1+1} {min} {max}", context ); @@ -113,7 +156,7 @@ public void testExpressionAndParameterInterpolationInSameMessageDescriptor() { @Test public void testEscapedExpressionLanguage() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "${1+1}"; String actual = interpolatorUnderTest.interpolate( "\\${1+1}", context ); @@ -122,7 +165,7 @@ public void testEscapedExpressionLanguage() { @Test public void testTernaryExpressionLanguageOperator() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "foo"; String actual = interpolatorUnderTest.interpolate( "${min == 0 ? 'foo' : 'bar'}", context ); @@ -131,7 +174,7 @@ public void testTernaryExpressionLanguageOperator() { @Test public void testParameterFormatting() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "Max 2147483647, min 0"; String actual = interpolatorUnderTest.interpolate( "${formatter.format('Max %s, min %s', max, min)}", context ); @@ -140,7 +183,7 @@ public void testParameterFormatting() { @Test public void testLiteralStaysUnchanged() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "foo"; String actual = interpolatorUnderTest.interpolate( "foo", context ); @@ -149,7 +192,7 @@ public void testLiteralStaysUnchanged() { @Test public void testLiteralBackslash() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "\\foo"; String actual = interpolatorUnderTest.interpolate( "\\foo", context ); @@ -158,7 +201,7 @@ public void testLiteralBackslash() { @Test public void testPrecedenceOfParameterInterpolation() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "$0"; String actual = interpolatorUnderTest.interpolate( "${min}", context ); @@ -171,9 +214,11 @@ public void testLocaleBasedFormatting() { notNullDescriptor, 42.00000d, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.VARIABLES, + false ); // german locale String expected = "42,00"; @@ -196,7 +241,7 @@ public void testLocaleBasedFormatting() { @Test public void testMissingFormatArgument() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "${formatter.format('%1$s')}"; String actual = interpolatorUnderTest.interpolate( "${formatter.format('%1$s')}", context ); @@ -209,7 +254,7 @@ public void testMissingFormatArgument() { @Test public void testNoParametersToFormatter() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); String expected = "${formatter.format()}"; String actual = interpolatorUnderTest.interpolate( "${formatter.format()}", context ); @@ -218,22 +263,42 @@ public void testNoParametersToFormatter() { @Test public void testNonFormatterFunction() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "foo"; String actual = interpolatorUnderTest.interpolate( "${'foobar'.substring(0,3)}", context ); assertEquals( actual, expected, "Calling of String#substring should work" ); } + @Test + public void testNonFormatterFunctionVariables() { + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.VARIABLES ); + + String expected = "${'foobar'.substring(0,3)}"; + String actual = interpolatorUnderTest.interpolate( "${'foobar'.substring(0,3)}", context ); + assertEquals( actual, expected, "Calling of String#substring should work" ); + } + + @Test + public void testNonFormatterFunctionBeanProperties() { + MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor, ExpressionLanguageFeatureLevel.BEAN_PROPERTIES ); + + String expected = "${'foobar'.substring(0,3)}"; + String actual = interpolatorUnderTest.interpolate( "${'foobar'.substring(0,3)}", context ); + assertEquals( actual, expected, "Calling of String#substring should work" ); + } + @Test public void testCallingWrongFormatterMethod() { MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, 42.00000d, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); String expected = "${formatter.foo('%1$.2f', validatedValue)}"; String actual = interpolatorUnderTest.interpolate( @@ -251,7 +316,7 @@ public void testCallingWrongFormatterMethod() { @Test @TestForIssue(jiraKey = "HV-834") public void testOpeningCurlyBraceInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "{"; String actual = interpolatorUnderTest.interpolate( "${1 > 0 ? '\\{' : '\\}'}", context ); @@ -261,7 +326,7 @@ public void testOpeningCurlyBraceInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testClosingCurlyBraceInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "}"; String actual = interpolatorUnderTest.interpolate( "${1 < 0 ? '\\{' : '\\}'}", context ); @@ -271,7 +336,7 @@ public void testClosingCurlyBraceInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testCurlyBracesInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "a{b}d"; String actual = interpolatorUnderTest.interpolate( "${1 < 0 ? 'foo' : 'a\\{b\\}d'}", context ); @@ -281,7 +346,7 @@ public void testCurlyBracesInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testEscapedQuoteInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "\""; String actual = interpolatorUnderTest.interpolate( "${ true ? \"\\\"\" : \"foo\"}", context ); @@ -291,7 +356,7 @@ public void testEscapedQuoteInELExpression() { @Test @TestForIssue(jiraKey = "HV-834") public void testSingleEscapedQuoteInELExpression() { - MessageInterpolator.Context context = createMessageInterpolatorContext( sizeDescriptor ); + MessageInterpolator.Context context = createMessageInterpolatorContextELBeanMethods( sizeDescriptor ); String expected = "'"; String actual = interpolatorUnderTest.interpolate( "${ false ? 'foo' : '\\''}", context ); @@ -302,13 +367,20 @@ public void testSingleEscapedQuoteInELExpression() { ); } - private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDescriptorImpl descriptor) { + private MessageInterpolatorContext createMessageInterpolatorContextELBeanMethods(ConstraintDescriptorImpl descriptor) { + return createMessageInterpolatorContext( descriptor, ExpressionLanguageFeatureLevel.BEAN_METHODS ); + } + + private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDescriptorImpl descriptor, + ExpressionLanguageFeatureLevel expressionLanguageFeatureLevel) { return new MessageInterpolatorContext( descriptor, null, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + expressionLanguageFeatureLevel, + false ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/LocaleResolverTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/LocaleResolverTest.java new file mode 100644 index 0000000000..2083e3f9b1 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/LocaleResolverTest.java @@ -0,0 +1,100 @@ +/* + * 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.assertj.core.api.Assertions.assertThat; + +import java.util.Locale; + +import javax.validation.MessageInterpolator; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.spi.messageinterpolation.LocaleResolver; +import org.hibernate.validator.spi.messageinterpolation.LocaleResolverContext; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutil.ValidationXmlTestHelper; +import org.testng.annotations.Test; + +@TestForIssue(jiraKey = "HV-1749") +public class LocaleResolverTest { + + @Test + public void testLocaleResolver() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .localeResolver( new StaticFieldLocaleResolver() ) + .buildValidatorFactory(); + MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); + + StaticFieldLocaleResolver.resolvedLocale = Locale.FRANCE; + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "doit avoir la valeur faux" ); + + StaticFieldLocaleResolver.resolvedLocale = Locale.ITALY; + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "deve essere false" ); + } + + @Test + public void shouldApplyLocaleResolverConfiguredInValidationXml() { + runWithCustomValidationXml( "locale-resolver-validation.xml", new Runnable() { + + @Override + public void run() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); + + StaticFieldLocaleResolver.resolvedLocale = Locale.FRANCE; + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "doit avoir la valeur faux" ); + + StaticFieldLocaleResolver.resolvedLocale = Locale.ITALY; + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "deve essere false" ); + } + } ); + } + + private void runWithCustomValidationXml(String validationXmlName, Runnable runnable) { + new ValidationXmlTestHelper( LocaleResolverTest.class ). + runWithCustomValidationXml( validationXmlName, runnable ); + } + + public static class StaticFieldLocaleResolver implements LocaleResolver { + + private static Locale resolvedLocale = Locale.FRANCE; + + @Override + public Locale resolve(LocaleResolverContext context) { + return resolvedLocale; + } + } + + private static class TestContext implements MessageInterpolator.Context { + + @Override + public ConstraintDescriptor getConstraintDescriptor() { + return null; + } + + @Override + public Object getValidatedValue() { + return null; + } + + @SuppressWarnings("unchecked") + @Override + public T unwrap(Class type) { + return (T) this; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java index 9e6f39fd75..cb8922b041 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/MessageInterpolationWithDefaultBundleTest.java @@ -17,8 +17,8 @@ import javax.validation.Validator; import javax.validation.constraints.DecimalMax; import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Email; -import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Range; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.testutil.TestForIssue; @@ -58,7 +58,7 @@ public void testEmailAndRangeMessageEnglishLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "not a well-formed email address" ), + violationOf( Email.class ).withMessage( "must be a well-formed email address" ), violationOf( Range.class ).withMessage( "must be between 18 and 21" ) ); } @@ -74,8 +74,8 @@ public void testEmailAndRangeMessageGermanLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "keine g\u00FCltige E-Mail-Adresse" ), - violationOf( Range.class ).withMessage( "muss zwischen 18 und 21 liegen" ) + violationOf( Email.class ).withMessage( "muss eine korrekt formatierte E-Mail-Adresse sein" ), + violationOf( Range.class ).withMessage( "muss zwischen 18 und 21 sein" ) ); } @@ -90,8 +90,8 @@ public void testEmailAndRangeMessageFrenchLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "adresse email mal form\u00E9e" ), - violationOf( Range.class ).withMessage( "doit \u00EAtre entre 18 et 21" ) + violationOf( Email.class ).withMessage( "doit être une adresse électronique syntaxiquement correcte" ), + violationOf( Range.class ).withMessage( "doit être compris entre 18 et 21" ) ); } @@ -110,7 +110,7 @@ public void testThatExplicitlySetEnglishLocaleHasPrecedenceOverDefaultLocale() { user.setAge( 16 ); Set> constraintViolations = validator.validate( user ); assertThat( constraintViolations ).containsOnlyViolations( - violationOf( Email.class ).withMessage( "not a well-formed email address" ), + violationOf( Email.class ).withMessage( "must be a well-formed email address" ), violationOf( Range.class ).withMessage( "must be between 18 and 21" ) ); } 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..ab24a4675d 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,47 @@ 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.ExpressionLanguageFeatureLevel; +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 +56,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,9 +91,11 @@ public void testContextWithRightDescriptorAndValueAndRootBeanClassIsPassedToMess constraintDescriptors.iterator().next(), validatedValue, TestBean.class, + null, + Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyMap() - ) + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ) ) ) .andReturn( "invalid" ); @@ -88,13 +112,15 @@ 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(), ExpressionLanguageFeatureLevel.BEAN_METHODS, 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(), ExpressionLanguageFeatureLevel.BEAN_METHODS, false ); MessageInterpolator.Context asMessageInterpolatorContext = context.unwrap( MessageInterpolator.Context.class ); assertSame( asMessageInterpolatorContext, context ); @@ -115,13 +141,50 @@ public void testGetRootBeanType() { null, null, rootBeanType, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + 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(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + 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 +193,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..68678d1ba1 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 @@ -27,7 +27,9 @@ import org.hibernate.validator.internal.engine.MessageInterpolatorContext; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.testutil.TestForIssue; @@ -51,18 +53,18 @@ public void setUp() { // Create some annotations for testing using AnnotationProxies ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( NotNull.class ); notNullDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, descriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); ConstraintAnnotationDescriptor.Builder sizeAnnotationDescriptorBuilder = new ConstraintAnnotationDescriptor.Builder( Size.class ); sizeDescriptor = new ConstraintDescriptorImpl<>( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, sizeAnnotationDescriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); } @@ -213,10 +215,10 @@ public void testRecursiveMessageInterpolation() { ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, descriptorBuilder.build(), - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); interpolator = new ResourceBundleMessageInterpolator( @@ -242,10 +244,10 @@ public void testCorrectMessageInterpolationIfParameterCannotBeReplaced() { ConstraintAnnotationDescriptor maxDescriptor = descriptorBuilder.build(); ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl( - new ConstraintHelper(), + ConstraintHelper.forAllBuiltinConstraints(), null, maxDescriptor, - java.lang.annotation.ElementType.FIELD + ConstraintLocationKind.FIELD ); interpolator = new ResourceBundleMessageInterpolator( @@ -278,9 +280,11 @@ private MessageInterpolatorContext createMessageInterpolatorContext(ConstraintDe descriptor, null, null, + null, Collections.emptyMap(), - Collections.emptyMap() - ); + Collections.emptyMap(), + ExpressionLanguageFeatureLevel.BEAN_METHODS, + false ); } 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/messageinterpolation/User.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/User.java index 97e549af83..f233c1cf12 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/User.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/messageinterpolation/User.java @@ -6,7 +6,8 @@ */ package org.hibernate.validator.test.internal.engine.messageinterpolation; -import org.hibernate.validator.constraints.Email; +import javax.validation.constraints.Email; + import org.hibernate.validator.constraints.Range; /** 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..5ab2de4dd5 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 @@ -15,6 +15,7 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; +import java.time.LocalDate; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -41,7 +42,6 @@ import org.hibernate.validator.test.internal.engine.methodvalidation.service.RepositoryBase; import org.hibernate.validator.testutil.TestForIssue; -import org.joda.time.DateMidnight; import org.testng.annotations.Test; /** @@ -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,18 +752,18 @@ public void voidMethodWithReturnValueConstraintCausesConstraintDeclarationExcept @TestForIssue(jiraKey = "HV-601") @Test(expectedExceptions = ConstraintViolationException.class) public void shouldValidateGetterLikeNamedMethodWithParameter() { - customerRepository.getFoo( "" ); + customerRepositoryValidatingProxy.getFoo( "" ); } @Test public void validationOfCrossParameterConstraint() { //given - DateMidnight startDate = new DateMidnight( 2012, 11, 5 ); - DateMidnight endDate = new DateMidnight( 2012, 11, 4 ); + LocalDate startDate = LocalDate.of( 2012, 11, 5 ); + LocalDate endDate = LocalDate.of( 2012, 11, 4 ); 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 ); @@ -787,15 +789,15 @@ public void validationOfCrossParameterConstraint() { assertMethod( constraintViolation, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); } } @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/IllegalMethodParameterConstraintsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java index db20900cab..2ebe164a08 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/IllegalMethodParameterConstraintsTest.java @@ -8,16 +8,17 @@ import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; +import java.time.LocalDate; + import javax.validation.ConstraintDeclarationException; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import org.joda.time.DateMidnight; -import org.testng.annotations.Test; - import org.hibernate.validator.test.internal.engine.methodvalidation.service.ConsistentDateParameters; +import org.testng.annotations.Test; + /** * Integration test for {@link org.hibernate.validator.internal.engine.ValidatorImpl} and * {@link org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl} which @@ -153,7 +154,7 @@ public void zap(@Valid String s) { } private interface Zip { - void zip(DateMidnight start, DateMidnight end); + void zip(LocalDate start, LocalDate end); } private static class ZipImpl implements Zip { @@ -162,7 +163,7 @@ private static class ZipImpl implements Zip { */ @Override @ConsistentDateParameters - public void zip(DateMidnight start, DateMidnight end) { + public void zip(LocalDate start, LocalDate end) { } } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/MethodParameterConstraintsInParallelHierarchyTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/MethodParameterConstraintsInParallelHierarchyTest.java new file mode 100644 index 0000000000..408a91c19e --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/MethodParameterConstraintsInParallelHierarchyTest.java @@ -0,0 +1,72 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.lang.reflect.Method; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +public class MethodParameterConstraintsInParallelHierarchyTest { + + /** + * NOTE: prior to the changes made for HV-1450, this test was failing randomly. + */ + @Test + @TestForIssue(jiraKey = "HV-1450") + public void testDeepParallelHierarchyIsProcessedCorrectly() throws Exception { + + WebServiceImpl service = new WebServiceImpl(); + Method method = WebServiceImpl.class.getMethod( "getEntityVersion", Long.class ); + Object[] params = new Object[] { null }; + + for ( int i = 0; i < 100; i++ ) { + Validator validator = Validation.byDefaultProvider().configure() + .buildValidatorFactory().getValidator(); + + Set> violations = validator.forExecutables().validateParameters( service, method, params ); + + assertThat( violations ).containsOnlyViolations( violationOf( NotNull.class ) ); + } + } + + private class WebServiceImpl extends AbstractWebService implements ExtendedWebService { + + } + + private abstract class AbstractWebService implements WebService { + + @Override + public int getEntityVersion(Long id) { + return id.intValue(); + } + } + + private interface ExtendedWebService extends WebService { + + @Override + int getEntityVersion(Long id); + } + + private interface WebService { + + int getEntityVersion(@NotNull Long id); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/ParameterValidationCycleTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/ParameterValidationCycleTest.java new file mode 100644 index 0000000000..5f19c684c8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/ParameterValidationCycleTest.java @@ -0,0 +1,108 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.HashSet; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.executable.ExecutableValidator; + +import org.hibernate.validator.testutil.ConstraintViolationAssert; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * @author Guillaume Smet + * @author Chris Westmorland + */ +public class ParameterValidationCycleTest { + + @Test + @TestForIssue(jiraKey = "HV-1797") + public void testParameterValidationCycle() throws NoSuchMethodException, SecurityException { + final Parent parent = new Parent(); + parent.setId( 1L ); + + final Child child = new Child(); + child.setId( null ); + child.setParent( parent ); + + parent.getChildren().add( child ); + + ExecutableValidator executableValidator = ValidatorUtil.getValidator().forExecutables(); + Set> violations = executableValidator.validateParameters( new ExecutableHolder(), + ExecutableHolder.class.getDeclaredMethod( "post", Parent.class ), + new Object[]{ parent } ); + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( violationOf( NotNull.class ) ); + } + + private static class ExecutableHolder { + + @SuppressWarnings("unused") + public void post(@Valid @NotNull Parent parent) { + } + } + + @SuppressWarnings("unused") + private static class Parent { + + @NotNull + private Long id; + + @Valid + private Set children = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getChildren() { + return children; + } + + public void setChildren(Set children) { + this.children = children; + } + } + + @SuppressWarnings("unused") + private static class Child { + + @NotNull + private Long id; + + @NotNull + @Valid + private Parent parent; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Parent getParent() { + return parent; + } + + public void setParent(Parent parent) { + this.parent = parent; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java index 0a2324ad3b..09bd15d7c7 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/PureCompositeConstraintsOnReturnValueTest.java @@ -28,13 +28,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import javax.validation.constraintvalidation.SupportedValidationTarget; import javax.validation.constraintvalidation.ValidationTarget; -import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ValidatorUtil; @@ -44,7 +44,6 @@ /** * @author Marko Bekhta */ -@SuppressWarnings("deprecation") public class PureCompositeConstraintsOnReturnValueTest { private Validator validator; private Foo foo; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/TypeVariableMethodParameterResolutionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/TypeVariableMethodParameterResolutionTest.java new file mode 100644 index 0000000000..da14457d59 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/TypeVariableMethodParameterResolutionTest.java @@ -0,0 +1,41 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.methodvalidation; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.constraints.Size; +import javax.validation.executable.ExecutableValidator; + +import org.testng.annotations.Test; + +public class TypeVariableMethodParameterResolutionTest { + + @Test + public void testTypeVariableMethodParameterResolution() throws NoSuchMethodException, SecurityException { + ExecutableValidator validator = Validation.buildDefaultValidatorFactory() + .getValidator().forExecutables(); + + Set> violations = validator.validateParameters( new Bean(), Bean.class.getMethod( "method", List.class ), + new Object[]{ new ArrayList<>() } ); + assertThat( violations ).containsOnlyViolations( violationOf( Size.class ) ); + } + + @SuppressWarnings("unused") + private class Bean { + + public > void method(@Size(min = 1) T myList) { + } + } +} 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/methodvalidation/returnvaluevalidation/ContactBean.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/returnvaluevalidation/ContactBean.java index 327420d330..010c2d7443 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/returnvaluevalidation/ContactBean.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/returnvaluevalidation/ContactBean.java @@ -6,10 +6,9 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.returnvaluevalidation; +import javax.validation.constraints.Email; import javax.validation.constraints.Pattern; -import org.hibernate.validator.constraints.Email; - /** * @author Hardy Ferentschik */ diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java index 326ce079be..a3c9ae047b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/ConsistentDateParametersValidator.java @@ -6,17 +6,17 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.service; +import java.time.LocalDate; + import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.constraintvalidation.SupportedValidationTarget; import javax.validation.constraintvalidation.ValidationTarget; -import org.joda.time.DateMidnight; - /** * @author Gunnar Morling */ -@SupportedValidationTarget( value = ValidationTarget.PARAMETERS) +@SupportedValidationTarget(value = ValidationTarget.PARAMETERS) public class ConsistentDateParametersValidator implements ConstraintValidator { @Override @@ -29,10 +29,10 @@ public boolean isValid(Object[] value, ConstraintValidatorContext context) { return true; } - if ( !( value[0] instanceof DateMidnight ) || !( value[1] instanceof DateMidnight ) ) { + if ( !( value[0] instanceof LocalDate ) || !( value[1] instanceof LocalDate ) ) { throw new IllegalArgumentException( "Unexpected method signature" ); } - return ( (DateMidnight) value[0] ).isBefore( (DateMidnight) value[1] ); + return ( (LocalDate) value[0] ).isBefore( (LocalDate) value[1] ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java index 5a9ce32fcd..e5475ba12f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepository.java @@ -6,16 +6,16 @@ */ package org.hibernate.validator.test.internal.engine.methodvalidation.service; +import java.time.LocalDate; import java.util.List; import java.util.Map; import javax.validation.Valid; import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.test.internal.engine.methodvalidation.model.Customer; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -76,7 +76,7 @@ public interface CustomerRepository extends RepositoryBase { int getFoo(@NotEmpty String s); @ConsistentDateParameters - void methodWithCrossParameterConstraint(@NotNull DateMidnight start, @NotNull DateMidnight end); + void methodWithCrossParameterConstraint(@NotNull LocalDate start, @NotNull LocalDate end); public interface ValidationGroup { } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java index e76ed17cfb..a358f11db7 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/methodvalidation/service/CustomerRepositoryImpl.java @@ -13,6 +13,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.time.LocalDate; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -25,7 +26,6 @@ import javax.validation.constraints.NotNull; import org.hibernate.validator.test.internal.engine.methodvalidation.model.Customer; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -148,7 +148,7 @@ public int getFoo(String s) { } @Override - public void methodWithCrossParameterConstraint(DateMidnight start, DateMidnight end) { + public void methodWithCrossParameterConstraint(LocalDate start, LocalDate end) { } @Constraint(validatedBy = { ValidB2BRepositoryValidator.class }) 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..074645b79f 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 @@ -9,6 +9,7 @@ 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.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; @@ -28,19 +29,21 @@ import javax.validation.constraints.NotNull; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; 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.BeanMetaDataManagerImpl; +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; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; 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 +68,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 +79,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() ); @@ -191,12 +194,12 @@ public void testNonStringMapKey() { public void testCreationOfExecutablePath() throws Exception { Method executable = Container.class.getMethod( "addItem", Key.class, Item.class ); - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java index 24021cf527..12318193c7 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/serialization/CustomConstraintSerializableTest.java @@ -22,10 +22,9 @@ * simple custom Email validation constraint taken from this blog gives a validation result that is not Serializable with * Hibernate Validator 4.0.2.GA with underlying cause that - *

+ *

* {@code org.hibernate.validator.internal.util.annotationfactory.AnnotationProxy} - *

- *

+ *

* Note that Hibernate Validator does not guarantee at all that a * {@link ConstraintViolation} is Serializable because an entity need not be * Serializable, but otherwise there should not be much of a problem (right?). @@ -77,6 +76,7 @@ static class CustomEmail implements Serializable { } + @SuppressWarnings("deprecation") static class HibernateEmail implements Serializable { private static final long serialVersionUID = 7206154160792549270L; 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/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java index ef44331eef..3e1c3e737d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnConstructorTest.java @@ -31,13 +31,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java index 9aca67acc9..6d7db82280 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnFieldTest.java @@ -29,13 +29,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java index a9ad70d37b..3e8f788c76 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnGetterTest.java @@ -29,13 +29,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java index 929d8109c3..2c097f7517 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintOnMethodTest.java @@ -31,13 +31,13 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java index 661f736d05..c0b999a1a1 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidatePropertyTest.java @@ -29,19 +29,19 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import javax.validation.valueextraction.Unwrapping; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** - * Test combination of {@link Optional} and {@link UnwrapValidatedValue} on fields using validate property. + * Test combination of {@link Optional} and {@link Unwrapping.Unwrap} on fields using validate property. * * @author Davide D'Alto */ diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java index ffe0b5f403..754f83347c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/OptionalTypeAnnotationConstraintUsingValidateValueTest.java @@ -28,11 +28,11 @@ import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import javax.validation.Validator; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.Null; import org.hibernate.validator.constraints.CompositionType; import org.hibernate.validator.constraints.ConstraintComposition; -import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java index 3a447017fd..38580e385d 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/valueextraction/UnwrapValidatedValueTest.java @@ -9,7 +9,6 @@ import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import java.lang.annotation.ElementType; import java.lang.reflect.Method; import java.util.Set; @@ -40,7 +39,7 @@ import org.testng.annotations.Test; /** - * Test for unwrapping validated values via {@link org.hibernate.validator.valuehandling.UnwrapValidatedValue}. + * Test for unwrapping validated values via {@link Unwrapping.Unwrap}. * * @author Gunnar Morling */ @@ -162,7 +161,7 @@ public void shouldUnwrapPropertyValueBasedOnProgrammaticConfiguration() { HibernateValidatorConfiguration configuration = ValidatorUtil.getConfiguration(); ConstraintMapping mapping = configuration.createConstraintMapping(); mapping.type( OrderLine.class ) - .property( "id", ElementType.FIELD ) + .field( "id" ) .constraint( new MaxDef().value( 5 ) ); Validator validator = configuration.addMapping( mapping ) @@ -182,7 +181,7 @@ public void shouldTakeIntoAccountUnwrappingConfigurationConstraintOverrideOnProg HibernateValidatorConfiguration configuration = ValidatorUtil.getConfiguration(); ConstraintMapping mapping = configuration.createConstraintMapping(); mapping.type( OrderLine.class ) - .property( "id", ElementType.FIELD ) + .field( "id" ) .constraint( new MaxDef().value( 5 ).payload( Unwrapping.Skip.class ) ); Validator validator = configuration.addMapping( mapping ) @@ -199,7 +198,7 @@ public void shouldThrowAnExceptionInCaseOfInvalidUnwrappingConfiguration() { HibernateValidatorConfiguration configuration = ValidatorUtil.getConfiguration(); ConstraintMapping mapping = configuration.createConstraintMapping(); mapping.type( OrderLine.class ) - .property( "id", ElementType.FIELD ) + .field( "id" ) .constraint( new MaxDef().value( 5 ).payload( Unwrapping.Skip.class, Unwrapping.Unwrap.class ) ); validator = configuration.addMapping( mapping ) 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..d83b3ce7fe 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 @@ -6,6 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotSame; import static org.testng.Assert.assertTrue; @@ -21,14 +22,16 @@ import java.util.List; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; 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.BeanMetaDataManagerImpl; +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; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -46,16 +49,16 @@ public class BeanMetaDataManagerTest { private static final int LOOP_COUNT = 100000; private static final int ARRAY_ALLOCATION_SIZE = 100000; - private BeanMetaDataManager metaDataManager; + private BeanMetaDataManagerImpl metaDataManager; @BeforeMethod public void setUpBeanMetaDataManager() { - metaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + metaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java index 9a0b8d0a3c..02adb7ad19 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/ConsistentDateParametersValidator.java @@ -6,13 +6,13 @@ */ package org.hibernate.validator.test.internal.metadata; +import java.time.LocalDate; + import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.constraintvalidation.SupportedValidationTarget; import javax.validation.constraintvalidation.ValidationTarget; -import org.joda.time.DateMidnight; - /** * @author Gunnar Morling */ @@ -29,10 +29,10 @@ public boolean isValid(Object[] value, ConstraintValidatorContext context) { return true; } - if ( !( value[0] instanceof DateMidnight ) || !( value[1] instanceof DateMidnight ) ) { + if ( !( value[0] instanceof LocalDate ) || !( value[1] instanceof LocalDate ) ) { throw new IllegalArgumentException( "Unexpected method signature" ); } - return ( (DateMidnight) value[0] ).isBefore( (DateMidnight) value[1] ); + return ( (LocalDate) value[0] ).isBefore( (LocalDate) value[1] ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java index 2f112710c0..2c138fd24f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepository.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata; +import java.time.LocalDate; import java.util.Set; import javax.validation.Valid; @@ -15,7 +16,6 @@ import javax.validation.groups.Default; import org.hibernate.validator.constraints.ScriptAssert; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -31,7 +31,7 @@ public CustomerRepository() { } @ConsistentDateParameters - public CustomerRepository(DateMidnight start, DateMidnight end) { + public CustomerRepository(LocalDate start, LocalDate end) { } public Customer createCustomer(CharSequence firstName, @NotNull String lastName) { @@ -69,7 +69,7 @@ public void zap(@Max(1) int i) { } @ConsistentDateParameters(groups = ValidationGroup.class) - public void methodWithCrossParameterConstraint(DateMidnight start, DateMidnight end) { + public void methodWithCrossParameterConstraint(LocalDate start, LocalDate end) { } public void methodWithParameterGroupConversion( diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java index 38182d02a6..627790bb70 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/CustomerRepositoryExt.java @@ -12,6 +12,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.time.LocalDate; import javax.validation.Constraint; import javax.validation.ConstraintValidator; @@ -25,7 +26,6 @@ import org.hibernate.validator.test.internal.metadata.Customer.CustomerBasic; import org.hibernate.validator.test.internal.metadata.Customer.CustomerComplex; -import org.joda.time.DateMidnight; /** * @author Gunnar Morling @@ -57,7 +57,7 @@ public CustomerRepositoryExt(int bar) { } @Valid - public CustomerRepositoryExt(DateMidnight start, DateMidnight end) { + public CustomerRepositoryExt(LocalDate start, LocalDate end) { } public CustomerRepositoryExt(long l) { @@ -123,7 +123,7 @@ public int zip(int i) { } @Override - public void methodWithCrossParameterConstraint(DateMidnight start, DateMidnight end) { + public void methodWithCrossParameterConstraint(LocalDate start, LocalDate end) { } @Constraint(validatedBy = { ValidB2BRepositoryValidator.class }) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java index 17577ead3a..7272eb738b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/Person.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata; -import org.hibernate.validator.constraints.NotEmpty; +import javax.validation.constraints.NotEmpty; /** * @author Hardy Ferentschik 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..caf2eb5849 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 @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.aggregated; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -14,6 +15,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.time.LocalDate; import java.util.Collections; import java.util.UUID; @@ -22,15 +24,19 @@ import javax.validation.groups.Default; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; 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.BeanMetaDataManagerImpl; +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; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.Signature; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -39,7 +45,6 @@ import org.hibernate.validator.test.internal.metadata.CustomerRepository.ValidationGroup; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt; import org.hibernate.validator.testutil.TestForIssue; -import org.joda.time.DateMidnight; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -55,12 +60,12 @@ public class ExecutableMetaDataTest { @BeforeMethod public void setupBeanMetaData() { - beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() @@ -148,7 +153,7 @@ public void getIdentifierForMethod() throws Exception { ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ).containsOnly( - "createCustomer(java.lang.CharSequence,java.lang.String)" + new Signature( "createCustomer", CharSequence.class, String.class ) ); } @@ -157,7 +162,7 @@ public void getIdentifierForParameterlessMethod() throws Exception { Method method = CustomerRepositoryExt.class.getMethod( "foo" ); ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); - assertThat( methodMetaData.getSignatures() ).containsOnly( "foo()" ); + assertThat( methodMetaData.getSignatures() ).containsOnly( new Signature( "foo" ) ); } @Test @@ -165,7 +170,7 @@ public void getIdentifierForConstructor() throws Exception { Constructor constructor = CustomerRepositoryExt.class.getConstructor( String.class ); ExecutableMetaData constructorMetaData = beanMetaData.getMetaDataFor( constructor ).get(); - assertThat( constructorMetaData.getSignatures() ).containsOnly( "CustomerRepositoryExt(java.lang.String)" ); + assertThat( constructorMetaData.getSignatures() ).containsOnly( new Signature( "CustomerRepositoryExt", String.class ) ); } @Test @@ -176,8 +181,8 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); assertThat( methodMetaData.getSignatures() ) - .describedAs( "Expecting super-type and sub-type method signatures" ) - .containsOnly( "createJob(java.lang.Object)", "createJob(java.util.UUID)" ); + .describedAs( "Expecting super-type and sub-type method signatures" ) + .containsOnly( new Signature( "createJob", Object.class ), new Signature( "createJob", UUID.class ) ); method = JobRepository.class.getMethod( "createJob", Object.class ); beanMetaData = beanMetaDataManager.getBeanMetaData( JobRepository.class ); @@ -185,7 +190,7 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { assertThat( methodMetaData.getSignatures() ) .describedAs( "Expecting only super-type method signature" ) - .containsOnly( "createJob(java.lang.Object)" ); + .containsOnly( new Signature( "createJob", Object.class ) ); method = SpecialJobRepositoryImpl.class.getMethod( "createJob", Object.class ); beanMetaData = beanMetaDataManager.getBeanMetaData( SpecialJobRepositoryImpl.class ); @@ -193,7 +198,7 @@ public void getIdentifierForOverridingGenericMethod() throws Exception { assertThat( methodMetaData.getSignatures() ) .describedAs( "Expecting method signatures from super-types" ) - .containsOnly( "createJob(java.lang.Object)", "createJob(java.util.UUID)" ); + .containsOnly( new Signature( "createJob", Object.class ), new Signature( "createJob", UUID.class ) ); } @Test @@ -205,7 +210,7 @@ public void getIdentifierForOverloadedMethod() throws Exception { assertThat( methodMetaData.getSignatures() ) .describedAs( "Expecting sub-type method signature which overloads but not overrides super-type methods" ) - .containsOnly( "createJob(java.lang.String)" ); + .containsOnly( new Signature( "createJob", String.class ) ); } @Test @@ -258,8 +263,8 @@ public void methodWithCascadedParameter() throws Exception { public void methodWithCrossParameterConstraint() throws Exception { Method method = CustomerRepositoryExt.class.getMethod( "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); ExecutableMetaData methodMetaData = beanMetaData.getMetaDataFor( method ).get(); 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..e9a97a095e 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 @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.aggregated; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -24,15 +25,18 @@ import javax.validation.groups.Default; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; 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.BeanMetaDataManagerImpl; +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; -import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -55,12 +59,12 @@ public class ParameterMetaDataTest { @BeforeMethod public void setupBeanMetaData() { - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() @@ -125,12 +129,12 @@ public void parameterNameInInheritanceHierarchy() throws Exception { // // The failure rate on my current VM before fixing the bug is 50%. // Running it in a loop does not improve the odds of failure: all tests will pass or fail for all loop run. - BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + BeanMetaDataManager beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new SkewedParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), + 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..f73cb77d7d 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 @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.aggregated; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import java.util.Collections; import java.util.Set; @@ -17,13 +18,16 @@ import javax.validation.groups.Default; import org.hibernate.validator.internal.engine.DefaultParameterNameProvider; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; 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.BeanMetaDataManagerImpl; +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; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.TypeResolutionHelper; @@ -39,12 +43,12 @@ public class PropertyMetaDataTest { @BeforeMethod public void setupBeanMetaDataManager() { - beanMetaDataManager = new BeanMetaDataManager( - new ConstraintHelper(), + beanMetaDataManager = new BeanMetaDataManagerImpl( + getDummyConstraintCreationContext(), new ExecutableHelper( new TypeResolutionHelper() ), - new TypeResolutionHelper(), new ExecutableParameterNameProvider( new DefaultParameterNameProvider() ), - new ValueExtractorManager( Collections.emptySet() ), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), + new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), new MethodValidationConfiguration.Builder().build() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/bytebuddy/ByteBuddyWrapperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/bytebuddy/ByteBuddyWrapperTest.java new file mode 100644 index 0000000000..41bdc52424 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/bytebuddy/ByteBuddyWrapperTest.java @@ -0,0 +1,377 @@ +/* + * 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.metadata.bytebuddy; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; + +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; +import org.hibernate.validator.internal.util.StringHelper; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; +import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.implementation.bytecode.ByteCodeAppender; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveBoxingDelegate; +import net.bytebuddy.implementation.bytecode.assign.reference.ReferenceTypeAwareAssigner; +import net.bytebuddy.jar.asm.Label; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.jar.asm.Type; +import net.bytebuddy.matcher.ElementMatchers; +import org.testng.annotations.Test; + +/** + * Note that this implementation is not complete and is only for testing purposes. + *

+ * It typically doesn't implement the support for instrumented parent classes. + * + * @author Marko Bekhta + */ +public class ByteBuddyWrapperTest { + + @Test + public void testByteBuddy() throws Exception { + Class clazz = Foo.class; + + ClassLoader classLoader = new ByteArrayClassLoader( + ClassLoadingStrategy.BOOTSTRAP_LOADER, + ClassFileLocator.ForClassLoader.readToNames( Foo.class, HibernateValidatorEnhancedBean.class, + MyContracts.class, StringHelper.class ) ); + + Class aClass = new ByteBuddy().rebase( clazz ) + .implement( HibernateValidatorEnhancedBean.class ) + .method( + named( HibernateValidatorEnhancedBean.GET_FIELD_VALUE_METHOD_NAME ) + .and( ElementMatchers.takesArguments( String.class ) ) + .and( ElementMatchers.returns( Object.class ) ) + ) + .intercept( new Implementation.Simple( new GetFieldValue( clazz ) ) ) + .method( + named( HibernateValidatorEnhancedBean.GET_GETTER_VALUE_METHOD_NAME ) + .and( ElementMatchers.takesArguments( String.class ) ) + .and( ElementMatchers.returns( Object.class ) ) + ) + .intercept( new Implementation.Simple( new GetGetterValue( clazz ) ) ) + .make() + .load( classLoader, ClassLoadingStrategy.Default.INJECTION ) + .getLoaded(); + + Object object = aClass.newInstance(); + + Method getFieldValue = aClass.getMethod( HibernateValidatorEnhancedBean.GET_FIELD_VALUE_METHOD_NAME, String.class ); + + assertThat( getFieldValue.invoke( object, "num" ) ).isEqualTo( -1 ); + assertThat( getFieldValue.invoke( object, "string" ) ).isEqualTo( "test" ); + assertThat( getFieldValue.invoke( object, "looooong" ) ).isEqualTo( 100L ); + + Method getGetterValue = aClass.getMethod( HibernateValidatorEnhancedBean.GET_GETTER_VALUE_METHOD_NAME, String.class ); + + assertThat( getGetterValue.invoke( object, "getMessage" ) ).isEqualTo( "messssssage" ); + assertThat( getGetterValue.invoke( object, "getKey" ) ).isEqualTo( false ); + } + + private static class GetFieldValue implements ByteCodeAppender { + + @SuppressWarnings("rawtypes") + private final Class clazz; + + private final Field[] fields; + + @SuppressWarnings("rawtypes") + public GetFieldValue(Class clazz) { + this.clazz = clazz; + this.fields = clazz.getDeclaredFields(); + } + + @Override + public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) { + try { + // Contracts.assertNotEmpty(propertyName, "Property cannot be blank"); + Label contractsPropertyNameCheckLabel = new Label(); + methodVisitor.visitLabel( contractsPropertyNameCheckLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( "Property cannot be blank" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getType( MyContracts.class ).getInternalName(), + "assertNotEmpty", + Type.getType( MyContracts.class.getDeclaredMethod( "assertNotEmpty", String.class, String.class ) ).getDescriptor(), + false + ); + + Label l1 = new Label(); + methodVisitor.visitLabel( l1 ); + int index = 0; + for ( Field field : fields ) { + String fieldName = field.getName(); + + if ( index > 0 ) { + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + } + + // if (propertyName.equals(field_name_goes_here)) { + // return field; + // } + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( fieldName ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + Type.getType( String.class ).getInternalName(), + "equals", + Type.getType( String.class.getDeclaredMethod( "equals", Object.class ) ).getDescriptor(), + false + ); + + Label ifCheckLabel = new Label(); + methodVisitor.visitJumpInsn( Opcodes.IFEQ, ifCheckLabel ); + + Label returnFieldLabel = new Label(); + methodVisitor.visitLabel( returnFieldLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); + methodVisitor.visitFieldInsn( + Opcodes.GETFIELD, + Type.getInternalName( clazz ), + fieldName, + Type.getDescriptor( field.getType() ) + ); + if ( field.getType().isPrimitive() ) { + PrimitiveBoxingDelegate.forPrimitive( new TypeDescription.ForLoadedType( field.getType() ) ) + .assignBoxedTo( + TypeDescription.Generic.OBJECT, + ReferenceTypeAwareAssigner.INSTANCE, + Assigner.Typing.STATIC + ) + .apply( methodVisitor, implementationContext ); + } + methodVisitor.visitInsn( Opcodes.ARETURN ); + methodVisitor.visitLabel( ifCheckLabel ); + + index++; + } + + // throw new IllegalArgumentException("No property was found for a given name"); + + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + methodVisitor.visitTypeInsn( Opcodes.NEW, Type.getInternalName( IllegalArgumentException.class ) ); + methodVisitor.visitInsn( Opcodes.DUP ); + methodVisitor.visitLdcInsn( "No property was found for a given name" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESPECIAL, + Type.getInternalName( IllegalArgumentException.class ), + "", + Type.getType( IllegalArgumentException.class.getDeclaredConstructor( String.class ) ).getDescriptor(), + false + ); + methodVisitor.visitInsn( Opcodes.ATHROW ); + + Label label = new Label(); + methodVisitor.visitLabel( label ); + methodVisitor.visitLocalVariable( + "this", + Type.getDescriptor( clazz ), + null, + contractsPropertyNameCheckLabel, + label, + 0 + ); + methodVisitor.visitLocalVariable( + "name", + Type.getDescriptor( String.class ), + null, + contractsPropertyNameCheckLabel, + label, + 1 + ); + methodVisitor.visitMaxs( 3, 2 ); + + return new Size( 6, instrumentedMethod.getStackSize() ); + } + catch (NoSuchMethodException e) { + throw new IllegalArgumentException( e ); + } + } + } + + private static class GetGetterValue implements ByteCodeAppender { + + @SuppressWarnings("rawtypes") + private final Class clazz; + + private final Method[] methods; + + @SuppressWarnings("rawtypes") + public GetGetterValue(Class clazz) { + this.clazz = clazz; + this.methods = clazz.getDeclaredMethods(); + } + + @Override + public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) { + try { + // Contracts.assertNotEmpty(propertyName, "Property cannot be blank"); + Label contractsPropertyNameCheckLabel = new Label(); + methodVisitor.visitLabel( contractsPropertyNameCheckLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( "Property cannot be blank" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getType( MyContracts.class ).getInternalName(), + "assertNotEmpty", + Type.getType( MyContracts.class.getDeclaredMethod( "assertNotEmpty", String.class, String.class ) ).getDescriptor(), + false + ); + + Label l1 = new Label(); + methodVisitor.visitLabel( l1 ); + int index = 0; + for ( Method method : methods ) { + String fieldName = method.getName(); + + if ( index > 0 ) { + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + } + + // if (propertyName.equals(field_name_goes_here)) { + // return field; + // } + methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 ); + methodVisitor.visitLdcInsn( fieldName ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + Type.getType( String.class ).getInternalName(), + "equals", + Type.getType( String.class.getDeclaredMethod( "equals", Object.class ) ).getDescriptor(), + false + ); + + Label ifCheckLabel = new Label(); + methodVisitor.visitJumpInsn( Opcodes.IFEQ, ifCheckLabel ); + + Label returnFieldLabel = new Label(); + methodVisitor.visitLabel( returnFieldLabel ); + methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + Type.getInternalName( clazz ), + method.getName(), + Type.getMethodDescriptor( method ), + method.getDeclaringClass().isInterface() + ); + if ( method.getReturnType().isPrimitive() ) { + PrimitiveBoxingDelegate.forPrimitive( new TypeDescription.ForLoadedType( method.getReturnType() ) ) + .assignBoxedTo( + TypeDescription.Generic.OBJECT, + ReferenceTypeAwareAssigner.INSTANCE, + Assigner.Typing.STATIC + ) + .apply( methodVisitor, implementationContext ); + } + methodVisitor.visitInsn( Opcodes.ARETURN ); + methodVisitor.visitLabel( ifCheckLabel ); + + index++; + } + + // throw new IllegalArgumentException("No property was found for a given name"); + + methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null ); + methodVisitor.visitTypeInsn( Opcodes.NEW, Type.getInternalName( IllegalArgumentException.class ) ); + methodVisitor.visitInsn( Opcodes.DUP ); + methodVisitor.visitLdcInsn( "No property was found for a given name" ); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESPECIAL, + Type.getInternalName( IllegalArgumentException.class ), + "", + Type.getType( IllegalArgumentException.class.getDeclaredConstructor( String.class ) ).getDescriptor(), + false + ); + methodVisitor.visitInsn( Opcodes.ATHROW ); + + Label label = new Label(); + methodVisitor.visitLabel( label ); + methodVisitor.visitLocalVariable( + "this", + Type.getDescriptor( clazz ), + null, + contractsPropertyNameCheckLabel, + label, + 0 + ); + methodVisitor.visitLocalVariable( + "name", + Type.getDescriptor( String.class ), + null, + contractsPropertyNameCheckLabel, + label, + 1 + ); + methodVisitor.visitMaxs( 3, 2 ); + + return new Size( 6, instrumentedMethod.getStackSize() ); + } + catch (NoSuchMethodException e) { + throw new IllegalArgumentException( e ); + } + } + } + + + @SuppressWarnings("unused") + public static class Foo { + private String string; + private Integer num; + private long looooong; + + public Foo() { + this( "test", -1 ); + this.looooong = 100L; + } + + public Foo(String string, Integer num) { + this.string = string; + this.num = num; + } + + public String getMessage() { + return "messssssage"; + } + + public boolean getKey() { + return false; + } + } + + @SuppressWarnings("unused") + public static class Bar extends Foo { + private final List strings; + + public Bar() { + super(); + this.strings = Collections.emptyList(); + } + } + + public static final class MyContracts { + public static void assertNotEmpty(String s, String message) { + if ( StringHelper.isNullOrEmptyString( s ) ) { + throw new IllegalArgumentException( message ); + } + } + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java index 2eb7604455..4708fae57b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/ConstraintHelperTest.java @@ -31,7 +31,7 @@ public class ConstraintHelperTest { @BeforeClass public static void init() { - constraintHelper = new ConstraintHelper(); + constraintHelper = ConstraintHelper.forAllBuiltinConstraints(); } @Test diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java index 29cfcc5550..11f91dc1b5 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/core/MetaConstraintTest.java @@ -6,7 +6,7 @@ */ package org.hibernate.validator.test.internal.metadata.core; -import static java.lang.annotation.ElementType.METHOD; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintValidatorInitializationContext; import static org.testng.Assert.assertEquals; import java.lang.reflect.Method; @@ -14,15 +14,21 @@ import javax.validation.constraints.NotNull; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; 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.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -33,14 +39,16 @@ public class MetaConstraintTest { private ConstraintHelper constraintHelper; private TypeResolutionHelper typeResolutionHelper; private ValueExtractorManager valueExtractorManager; + private ConstraintValidatorManager constraintValidatorManager; private Method barMethod; private ConstraintAnnotationDescriptor constraintAnnotationDescriptor; @BeforeClass public void setUp() throws Exception { - constraintHelper = new ConstraintHelper(); + constraintHelper = ConstraintHelper.forAllBuiltinConstraints(); typeResolutionHelper = new TypeResolutionHelper(); valueExtractorManager = new ValueExtractorManager( Collections.emptySet() ); + constraintValidatorManager = new ConstraintValidatorManagerImpl( new ConstraintValidatorFactoryImpl(), getDummyConstraintValidatorInitializationContext() ); barMethod = Foo.class.getMethod( "getBar" ); constraintAnnotationDescriptor = new ConstraintAnnotationDescriptor.Builder<>( barMethod.getAnnotation( NotNull.class ) ).build(); } @@ -48,18 +56,20 @@ public void setUp() throws Exception { @Test @TestForIssue(jiraKey = "HV-930") public void two_meta_constraints_for_the_same_constraint_should_be_equal() throws Exception { + JavaBeanGetter javaBeanGetter = new JavaBeanGetter( Foo.class, Foo.class.getDeclaredMethod( "getBar" ), "bar", "bar" ); ConstraintDescriptorImpl constraintDescriptor1 = new ConstraintDescriptorImpl<>( - constraintHelper, barMethod, constraintAnnotationDescriptor, METHOD + constraintHelper, javaBeanGetter, constraintAnnotationDescriptor, ConstraintLocationKind.METHOD ); ConstraintLocation location1 = ConstraintLocation.forClass( Foo.class ); - MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor1, location1 ); - + MetaConstraint metaConstraint1 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintValidatorManager, + constraintDescriptor1, location1 ); ConstraintDescriptorImpl constraintDescriptor2 = new ConstraintDescriptorImpl<>( - constraintHelper, barMethod, constraintAnnotationDescriptor, METHOD + constraintHelper, javaBeanGetter, constraintAnnotationDescriptor, ConstraintLocationKind.METHOD ); ConstraintLocation location2 = ConstraintLocation.forClass( Foo.class ); - MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintDescriptor2, location2 ); + MetaConstraint metaConstraint2 = MetaConstraints.create( typeResolutionHelper, valueExtractorManager, constraintValidatorManager, + constraintDescriptor2, location2 ); assertEquals( metaConstraint1, metaConstraint2, "Two MetaConstraint instances for the same constraint should be equal" diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java index 2ab01b0016..30e44cc64e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/BeanDescriptorTest.java @@ -16,10 +16,12 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import java.time.LocalDate; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; + import javax.validation.ConstraintDeclarationException; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -31,15 +33,13 @@ import javax.validation.metadata.ParameterDescriptor; import javax.validation.metadata.PropertyDescriptor; -import org.joda.time.DateMidnight; -import org.testng.annotations.Test; - import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.test.internal.metadata.Customer; import org.hibernate.validator.test.internal.metadata.CustomerRepository; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt; import org.hibernate.validator.test.internal.metadata.IllegalCustomerRepositoryExt; import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; /** * Unit test for {@link BeanDescriptor} and its creation. @@ -313,7 +313,7 @@ public void testGetConstrainedConstructors() { Arrays.>asList( String.class ), Arrays.>asList( String.class, Customer.class ), Collections.>emptyList(), - Arrays.>asList( DateMidnight.class, DateMidnight.class ) + Arrays.>asList( LocalDate.class, LocalDate.class ) ); } @@ -463,7 +463,7 @@ public void foo(String foo) { } } - @ScriptAssert(lang = "some lang", script = "some script") + @ScriptAssert(lang = "groovy", script = "true") private static class ClassLevelConstrainedType { } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java index bd13aaa6a9..17e4fd46de 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/CrossParameterDescriptorTest.java @@ -14,13 +14,13 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import java.time.LocalDate; import java.util.Set; import javax.validation.groups.Default; import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.CrossParameterDescriptor; import javax.validation.metadata.Scope; -import org.joda.time.DateMidnight; import org.testng.annotations.Test; import org.hibernate.validator.test.internal.metadata.ConsistentDateParameters; @@ -38,8 +38,8 @@ public void testGetElementClass() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertEquals( @@ -53,8 +53,8 @@ public void testGetConstraintDescriptorsForMethod() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertConstraintTypes( descriptor.getConstraintDescriptors(), @@ -66,8 +66,8 @@ public void testGetConstraintDescriptorsForMethod() { public void testGetConstraintDescriptorsForConstructor() { CrossParameterDescriptor descriptor = getConstructorDescriptor( CustomerRepository.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertConstraintTypes( descriptor.getConstraintDescriptors(), @@ -80,8 +80,8 @@ public void testGetConstraintDescriptorsForMethodConsidersConstraintsFromSuperTy CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertConstraintTypes( descriptor.getConstraintDescriptors(), @@ -93,8 +93,8 @@ public void testGetConstraintDescriptorsForMethodConsidersConstraintsFromSuperTy public void testGetConstraintDescriptorsForConstructorDoesNotConsiderConstraintsFromSuperType() { CrossParameterDescriptor descriptor = getConstructorDescriptor( CustomerRepositoryExt.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.getConstraintDescriptors().isEmpty() @@ -115,8 +115,8 @@ public void testHasConstraintsForMethod() { descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.hasConstraints(), @@ -136,8 +136,8 @@ public void testHasConstraintsForConstructor() { descriptor = getConstructorDescriptor( CustomerRepository.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.hasConstraints(), @@ -150,8 +150,8 @@ public void testHasConstraintsForMethodConsidersConstraintsFromSuperType() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( descriptor.hasConstraints(), @@ -163,8 +163,8 @@ public void testHasConstraintsForMethodConsidersConstraintsFromSuperType() { public void testHasConstraintsForConstructorDoesNotConsiderConstraintsFromSuperType() { CrossParameterDescriptor descriptor = getConstructorDescriptor( CustomerRepositoryExt.class, - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertFalse( descriptor.hasConstraints(), @@ -177,8 +177,8 @@ public void testFindConstraintsMatchingGroups() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); assertTrue( @@ -199,8 +199,8 @@ public void testFindConstraintsLookingAt() { CrossParameterDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ).getCrossParameterDescriptor(); Set> constraintDescriptors = descriptor.findConstraints() diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java index 3537d2c331..96ce622fc4 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/descriptor/MethodDescriptorTest.java @@ -13,8 +13,10 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; +import java.time.LocalDate; import java.util.List; import java.util.Set; + import javax.validation.ConstraintDeclarationException; import javax.validation.constraints.NotNull; import javax.validation.metadata.ConstraintDescriptor; @@ -22,15 +24,13 @@ import javax.validation.metadata.ParameterDescriptor; import javax.validation.metadata.Scope; -import org.joda.time.DateMidnight; -import org.testng.annotations.Test; - import org.hibernate.validator.test.internal.metadata.Customer; import org.hibernate.validator.test.internal.metadata.CustomerRepository; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt; import org.hibernate.validator.test.internal.metadata.CustomerRepositoryExt.CustomerExtension; import org.hibernate.validator.test.internal.metadata.IllegalCustomerRepositoryExt; import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; /** * @author Gunnar Morling @@ -75,8 +75,8 @@ public void testHasConstraints() { descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); assertFalse( descriptor.hasConstraints(), @@ -106,8 +106,8 @@ public void testGetConstraintDescriptors() { descriptor = getMethodDescriptor( CustomerRepository.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); assertTrue( descriptor.getConstraintDescriptors().isEmpty() ); } @@ -117,8 +117,8 @@ public void testFindConstraintsMatchingGroups() { MethodDescriptor descriptor = getMethodDescriptor( CustomerRepositoryExt.class, "methodWithCrossParameterConstraint", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); assertTrue( @@ -264,7 +264,7 @@ public void testAreParametersConstrainedForParameterCascadedMethod() { public void testAreParametersConstrainedForCrossParameterConstrainedMethod() { MethodDescriptor methodDescriptor = getMethodDescriptor( CustomerRepositoryExt.class, - "methodWithCrossParameterConstraint", DateMidnight.class, DateMidnight.class + "methodWithCrossParameterConstraint", LocalDate.class, LocalDate.class ); assertThat( methodDescriptor.hasConstrainedParameters() ).isTrue(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java index 8e424bbd4a..b83a97c1fc 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/location/ConstraintLocationTest.java @@ -11,6 +11,7 @@ import java.lang.reflect.Method; import org.hibernate.validator.internal.metadata.location.ConstraintLocation; +import org.hibernate.validator.internal.properties.javabean.JavaBeanGetter; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.Test; @@ -32,8 +33,8 @@ public void two_constraint_locations_for_the_same_type_should_be_equal() { @TestForIssue(jiraKey = "HV-930") public void two_constraint_locations_for_the_same_member_should_be_equal() throws Exception { Method getter = Foo.class.getMethod( "getBar" ); - ConstraintLocation location1 = ConstraintLocation.forGetter( getter ); - ConstraintLocation location2 = ConstraintLocation.forGetter( getter ); + ConstraintLocation location1 = ConstraintLocation.forGetter( new JavaBeanGetter( Foo.class, getter, "bar", "bar" ) ); + ConstraintLocation location2 = ConstraintLocation.forGetter( new JavaBeanGetter( Foo.class, getter, "bar", "bar" ) ); assertEquals( location1, location2, "Two constraint locations for the same type should be equal" ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java index 9953a487f8..1f4016a66f 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTest.java @@ -10,13 +10,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.assertj.core.api.Assertions.assertThat; import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import java.util.Collections; +import java.time.LocalDate; import java.util.Map; import javax.validation.Constraint; @@ -29,10 +29,10 @@ import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraints.ScriptAssert; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; 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.ConstraintLocationKind; import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConfigurationSource; @@ -40,9 +40,9 @@ 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.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.hibernate.validator.testutil.TestForIssue; -import org.joda.time.DateMidnight; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -58,9 +58,8 @@ public class AnnotationMetaDataProviderTest extends AnnotationMetaDataProviderTe @BeforeMethod public void setUpProvider() { provider = new AnnotationMetaDataProvider( - new ConstraintHelper(), - new TypeResolutionHelper(), - new ValueExtractorManager( Collections.emptySet() ), + getDummyConstraintCreationContext(), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new AnnotationProcessingOptionsImpl() ); } @@ -78,7 +77,7 @@ public void testGetConstructorMetaData() throws Exception { MetaConstraint constraint = constructor.getConstraints().iterator().next(); assertThat( constraint.getDescriptor().getAnnotation().annotationType() ).isEqualTo( NotNull.class ); - assertThat( constraint.getElementType() ).isEqualTo( ElementType.CONSTRUCTOR ); + assertThat( constraint.getConstraintLocationKind() ).isEqualTo( ConstraintLocationKind.CONSTRUCTOR ); } @Test @@ -90,8 +89,8 @@ public void testGetCrossParameterMetaData() throws Exception { beanConfiguration, Calendar.class, "createEvent", - DateMidnight.class, - DateMidnight.class + LocalDate.class, + LocalDate.class ); //then @@ -101,12 +100,10 @@ public void testGetCrossParameterMetaData() throws Exception { assertThat( createEvent.getConstraints() ).as( "No return value constraints expected" ).isEmpty(); assertThat( createEvent.getCrossParameterConstraints() ).hasSize( 1 ); - assertThat( createEvent.getExecutable() ).isEqualTo( - Calendar.class.getMethod( - "createEvent", - DateMidnight.class, - DateMidnight.class - ) + assertThat( createEvent.getCallable() ).isEqualTo( + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ) + .findDeclaredMethod( Calendar.class, "createEvent", LocalDate.class, LocalDate.class ) + .get() ); MetaConstraint constraint = createEvent.getCrossParameterConstraints().iterator().next(); @@ -315,7 +312,7 @@ public Foo(@NotNull String foo) { private static class Calendar { @ConsistentDateParameters - public void createEvent(DateMidnight start, DateMidnight end) { + public void createEvent(LocalDate start, LocalDate end) { } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java index fca67eae25..a75be4b083 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/AnnotationMetaDataProviderTestBase.java @@ -6,16 +6,21 @@ */ package org.hibernate.validator.test.internal.metadata.provider; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Member; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement; import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind; 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.properties.Constrainable; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; /** * @author Gunnar Morling @@ -56,17 +61,28 @@ protected ConstrainedType findConstrainedType(BeanConfiguration beanConfi throw new RuntimeException( "Found no constrained element for type " + type ); } - protected ConstrainedElement findConstrainedElement(BeanConfiguration beanConfiguration, - Member member) { + protected ConstrainedElement findConstrainedElement(BeanConfiguration beanConfiguration, Member member) { + JavaBeanHelper javaBeanHelper = new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ); + Constrainable constrainable; + if ( member instanceof Field ) { + constrainable = javaBeanHelper.findDeclaredField( member.getDeclaringClass(), member.getName() ).get(); + } + else if ( member instanceof Constructor ) { + constrainable = javaBeanHelper.findDeclaredConstructor( member.getDeclaringClass(), ( (Constructor) member ).getParameterTypes() ).get(); + } + else { + Executable executable = (Executable) member; + constrainable = javaBeanHelper.findDeclaredMethod( member.getDeclaringClass(), executable.getName(), executable.getParameterTypes() ).get(); + } for ( ConstrainedElement constrainedElement : beanConfiguration.getConstrainedElements() ) { if ( member instanceof Executable && constrainedElement instanceof ConstrainedExecutable ) { - if ( member.equals( ( (ConstrainedExecutable) constrainedElement ).getExecutable() ) ) { + if ( constrainable.equals( ( (ConstrainedExecutable) constrainedElement ).getCallable() ) ) { return constrainedElement; } } - else if ( member instanceof Field && constrainedElement instanceof ConstrainedField ) { - if ( member.equals( ( (ConstrainedField) constrainedElement ).getField() ) ) { + else if ( constrainedElement instanceof ConstrainedField ) { + if ( constrainable.equals( ( (ConstrainedField) constrainedElement ).getField() ) ) { return constrainedElement; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java index 75e1aaba1b..e7895e5f3e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/provider/TypeAnnotationMetaDataRetrievalTest.java @@ -7,6 +7,7 @@ package org.hibernate.validator.test.internal.metadata.provider; import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import java.lang.annotation.Annotation; import java.util.Collection; @@ -15,19 +16,19 @@ import java.util.stream.Collectors; import javax.validation.Valid; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.NotBlank; -import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; 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.provider.AnnotationMetaDataProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.metadata.raw.ConstrainedExecutable; import org.hibernate.validator.internal.metadata.raw.ConstrainedField; import org.hibernate.validator.internal.metadata.raw.ConstrainedParameter; -import org.hibernate.validator.internal.util.TypeResolutionHelper; +import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -43,9 +44,8 @@ public class TypeAnnotationMetaDataRetrievalTest extends AnnotationMetaDataProvi @BeforeClass public void setup() { provider = new AnnotationMetaDataProvider( - new ConstraintHelper(), - new TypeResolutionHelper(), - new ValueExtractorManager( Collections.emptySet() ), + getDummyConstraintCreationContext(), + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), new AnnotationProcessingOptionsImpl() ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/properties/javabean/JavaBeanExecutableTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/properties/javabean/JavaBeanExecutableTest.java new file mode 100644 index 0000000000..e2929b4967 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/properties/javabean/JavaBeanExecutableTest.java @@ -0,0 +1,82 @@ +/* + * 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.properties.javabean; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.ParameterizedType; +import java.util.List; +import java.util.Map; + +import org.hibernate.validator.internal.properties.javabean.JavaBeanConstructor; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; + +public class JavaBeanExecutableTest { + + @Test + @TestForIssue(jiraKey = "HV-1634") + public void testGenericTypeParametersWithImplicitParameters() throws NoSuchMethodException, SecurityException { + JavaBeanConstructor constructor = new JavaBeanConstructor( Bean.class.getDeclaredConstructors()[0] ); + + assertThat( constructor.getParameters() ).hasSize( 3 ); + + ParameterizedType parameterizedType1 = (ParameterizedType) constructor.getParameterGenericType( 1 ); + assertThat( parameterizedType1.getRawType() ).isEqualTo( Map.class ); + + ParameterizedType parameterizedType2 = (ParameterizedType) constructor.getParameterGenericType( 2 ); + assertThat( parameterizedType2.getRawType() ).isEqualTo( List.class ); + } + + @Test + @TestForIssue(jiraKey = "HV-1634") + public void testGenericTypeParametersWithoutImplicitParameters() throws NoSuchMethodException, SecurityException { + JavaBeanConstructor constructor = new JavaBeanConstructor( StaticBean.class.getDeclaredConstructors()[0] ); + + assertThat( constructor.getParameters() ).hasSize( 2 ); + + ParameterizedType parameterizedType0 = (ParameterizedType) constructor.getParameterGenericType( 0 ); + assertThat( parameterizedType0.getRawType() ).isEqualTo( Map.class ); + + ParameterizedType parameterizedType1 = (ParameterizedType) constructor.getParameterGenericType( 1 ); + assertThat( parameterizedType1.getRawType() ).isEqualTo( List.class ); + } + + @Test + @TestForIssue(jiraKey = "HV-1634") + public void testGenericTypeParametersWithSyntheticParameters() throws NoSuchMethodException, SecurityException { + JavaBeanConstructor constructor = new JavaBeanConstructor( MyEnum.class.getDeclaredConstructors()[0] ); + + assertThat( constructor.getParameters() ).hasSize( 4 ); + + ParameterizedType parameterizedType1 = (ParameterizedType) constructor.getParameterGenericType( 2 ); + assertThat( parameterizedType1.getRawType() ).isEqualTo( Map.class ); + + ParameterizedType parameterizedType2 = (ParameterizedType) constructor.getParameterGenericType( 3 ); + assertThat( parameterizedType2.getRawType() ).isEqualTo( List.class ); + } + + private class Bean { + + private Bean(Map map, List list) { + } + } + + private static class StaticBean { + + private StaticBean(Map map, List list) { + } + } + + private enum MyEnum { + + ; + + private MyEnum(Map map, List list) { + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/IdentitySetTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/IdentitySetTest.java deleted file mode 100644 index 76090e2325..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/IdentitySetTest.java +++ /dev/null @@ -1,71 +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.util; - -import static org.testng.Assert.assertTrue; - -import java.util.HashSet; -import java.util.Set; - -import org.hibernate.validator.internal.util.IdentitySet; -import org.testng.annotations.Test; - -/** - * @author Hardy Ferentschik - */ -public class IdentitySetTest { - - @SuppressWarnings("unchecked") - @Test - public void testAddIdenticalInstance() { - Set identitySet = new IdentitySet(); - Set hashSet = new HashSet(); - assertTrue( identitySet.size() == 0 ); - assertTrue( hashSet.size() == 0 ); - - Object o1 = new Object() { - int counter = 0; - - @Override - public int hashCode() { - return counter++; - } - - @Override - public boolean equals(Object other) { - return false; - } - }; - identitySet.add( o1 ); - hashSet.add( o1 ); - assertTrue( identitySet.size() == 1 ); - assertTrue( hashSet.size() == 1 ); - - identitySet.add( o1 ); - hashSet.add( o1 ); - assertTrue( identitySet.size() == 1 ); - assertTrue( hashSet.size() == 2 ); - - Object o2 = new Object() { - int counter = 0; - - @Override - public int hashCode() { - return counter++; - } - - @Override - public boolean equals(Object other) { - return false; - } - }; - identitySet.add( o2 ); - hashSet.add( o2 ); - assertTrue( identitySet.size() == 2 ); - assertTrue( hashSet.size() == 3 ); - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java index 784a47a9b9..d50b9046ee 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/ReflectionHelperTest.java @@ -12,7 +12,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; @@ -25,7 +24,7 @@ import java.util.TreeSet; import org.hibernate.validator.internal.util.ReflectionHelper; -import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.Test; /** @@ -161,16 +160,6 @@ public void testGetIndexedValueForNull() { assertNull( value ); } - @Test - @TestForIssue(jiraKey = "HV-622") - public void testIsGetterMethod() throws Exception { - Method method = Bar.class.getMethod( "getBar" ); - assertTrue( ReflectionHelper.isGetterMethod( method ) ); - - method = Bar.class.getMethod( "getBar", String.class ); - assertFalse( ReflectionHelper.isGetterMethod( method ) ); - } - private void doTestGetIndexedValueForArray(Object array, Object firstValue, Object secondValue) { Object value = ReflectionHelper.getIndexedValue( array, 0 ); assertEquals( value, firstValue, "We should be able to retrieve the indexed object" ); 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..1e1dcf547a 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 @@ -40,9 +40,11 @@ import java.util.Map; import java.util.Set; +import org.hibernate.validator.engine.HibernateValidatorEnhancedBean; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; import org.hibernate.validator.internal.util.TypeHelper; import org.hibernate.validator.testutil.TestForIssue; + import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -858,7 +860,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 ); @@ -875,6 +877,25 @@ public void testTypeHelperDoesntGoIntoInfiniteLoop() { assertTrue( TypeHelper.isAssignable( parentEntityType, childEntityType ) ); } + @Test + public void testIsHibernateValidatorEnhancedBean() { + class Foo { + } + class Bar implements HibernateValidatorEnhancedBean { + @Override + public Object $$_hibernateValidator_getFieldValue(String name) { + return null; + } + + @Override + public Object $$_hibernateValidator_getGetterValue(String name) { + return null; + } + } + assertTrue( TypeHelper.isHibernateValidatorEnhancedBean( Bar.class ) ); + assertFalse( TypeHelper.isHibernateValidatorEnhancedBean( Foo.class ) ); + } + private static void assertAsymmetricallyAssignable(Type supertype, Type type) { assertAssignable( supertype, type ); assertUnassignable( type, supertype ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java index 1f679862c4..2e001f6d4e 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/util/privilegedactions/GetAnnotationsParameterTest.java @@ -21,7 +21,7 @@ import org.testng.annotations.Test; /** - * Unit test for {@link GetAnnotationsParameter}. + * Unit test for {@link GetAnnotationAttribute}. * * @author Gunnar Morling * 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..0609a452a2 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 @@ -7,13 +7,13 @@ package org.hibernate.validator.test.internal.xml; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; +import static org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper.getDummyConstraintCreationContext; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.io.InputStream; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -22,11 +22,12 @@ import javax.validation.ValidationException; import javax.validation.constraints.DecimalMin; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; +import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorDescriptor; -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.properties.DefaultGetterPropertySelectionStrategy; +import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper; +import org.hibernate.validator.internal.xml.mapping.MappingXmlParser; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -37,20 +38,21 @@ public class MappingXmlParserTest { private MappingXmlParser xmlMappingParser; - private ConstraintHelper constraintHelper; + private ConstraintCreationContext constraintCreationContext; @BeforeMethod public void setupParserHelper() { - constraintHelper = new ConstraintHelper(); + constraintCreationContext = getDummyConstraintCreationContext(); xmlMappingParser = new MappingXmlParser( - constraintHelper, new TypeResolutionHelper(), new ValueExtractorManager( Collections.emptySet() ), null + constraintCreationContext, + new JavaBeanHelper( new DefaultGetterPropertySelectionStrategy(), new DefaultPropertyNodeNameProvider() ), null ); } @Test @TestForIssue(jiraKey = "HV-782") public void testAdditionalConstraintValidatorsGetAddedAndAreLastInList() { - List> validatorDescriptors = constraintHelper.getAllValidatorDescriptors( + List> validatorDescriptors = constraintCreationContext.getConstraintHelper().getAllValidatorDescriptors( DecimalMin.class ); @@ -62,7 +64,7 @@ public void testAdditionalConstraintValidatorsGetAddedAndAreLastInList() { xmlMappingParser.parse( mappingStreams ); - validatorDescriptors = constraintHelper.getAllValidatorDescriptors( DecimalMin.class ); + validatorDescriptors = constraintCreationContext.getConstraintHelper().getAllValidatorDescriptors( DecimalMin.class ); assertFalse( validatorDescriptors.isEmpty(), "Wrong number of default validators" ); assertEquals( getIndex( validatorDescriptors, DecimalMinValidatorForFoo.class ), validatorDescriptors.size() - 1, "The custom validator must be last" ); 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..288c437014 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,11 +9,11 @@ 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; import java.io.InputStream; -import java.lang.annotation.ElementType; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -141,7 +141,7 @@ public void testConstraintsFromXmlAndProgrammaticApiAddUp() { //given final ConstraintMapping programmaticMapping = configuration.createConstraintMapping(); programmaticMapping.type( Customer.class ) - .property( "firstName", ElementType.FIELD ) + .field( "firstName" ) .constraint( new SizeDef().min( 2 ).max( 10 ) ); final InputStream xmlMapping = XmlMappingTest.class.getResourceAsStream( "hv-480-mapping.xml" ); @@ -376,4 +376,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/test/parameternameprovider/ReflectionParameterNameProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/parameternameprovider/ReflectionParameterNameProviderTest.java index 81a9767e99..603ec4bbc9 100644 --- a/engine/src/test/java/org/hibernate/validator/test/parameternameprovider/ReflectionParameterNameProviderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/parameternameprovider/ReflectionParameterNameProviderTest.java @@ -28,6 +28,7 @@ public class ReflectionParameterNameProviderTest { private ParameterNameProvider parameterNameProvider; @BeforeClass + @SuppressWarnings("deprecation") public void setup() { parameterNameProvider = new ReflectionParameterNameProvider(); } diff --git a/engine/src/test/java/org/hibernate/validator/test/predefinedscope/LocaleResolverTest.java b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/LocaleResolverTest.java new file mode 100644 index 0000000000..a3aa187008 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/LocaleResolverTest.java @@ -0,0 +1,136 @@ +/* + * 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.predefinedscope; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Locale.LanguageRange; + +import javax.validation.MessageInterpolator; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.spi.messageinterpolation.LocaleResolver; +import org.hibernate.validator.spi.messageinterpolation.LocaleResolverContext; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; + +@TestForIssue(jiraKey = "HV-1749") +public class LocaleResolverTest { + + @Test + public void testLanguageRangeSupport() throws NoSuchMethodException, SecurityException { + ValidatorFactory validatorFactory = getValidatorFactoryWithInitializedLocales( Locale.FRANCE, new Locale( "es", "ES" ) ); + MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); + + StaticFieldLocaleResolver.acceptLanguage = "fr-FR,fr;q=0.9"; + + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "doit avoir la valeur faux" ); + } + + @Test + public void testCascadePriorities() { + ValidatorFactory validatorFactory = getValidatorFactoryWithInitializedLocales( Locale.FRANCE, Locale.forLanguageTag( "es" ) ); + MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); + + StaticFieldLocaleResolver.acceptLanguage = "hr-HR,hr;q=0.9,es;q=0.7"; + + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "debe ser falso" ); + } + + @Test + public void testFallbackToDefault() throws NoSuchMethodException, SecurityException { + // Defaults to en when we don't define a default as we launch Surefire with the en locale + ValidatorFactory validatorFactory = getValidatorFactoryWithInitializedLocales( new Locale( "es", "ES" ) ); + MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); + + StaticFieldLocaleResolver.acceptLanguage = "hr-HR,hr;q=0.9"; + + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "must be false" ); + + // Defaults to fr_FR if we define it as the default locale + validatorFactory = getValidatorFactoryWithDefaultLocaleAndInitializedLocales( Locale.FRANCE, Locale.forLanguageTag( "es_ES" ) ); + messageInterpolator = validatorFactory.getMessageInterpolator(); + + assertThat( messageInterpolator.interpolate( "{javax.validation.constraints.AssertFalse.message}", new TestContext() ) ) + .isEqualTo( "doit avoir la valeur faux" ); + } + + private static ValidatorFactory getValidatorFactoryWithInitializedLocales(Locale... locales) { + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .localeResolver( new StaticFieldLocaleResolver() ) + .initializeLocales( new HashSet<>( Arrays.asList( locales ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( Bean.class ) ) ) + .buildValidatorFactory(); + + return validatorFactory; + } + + private static ValidatorFactory getValidatorFactoryWithDefaultLocaleAndInitializedLocales(Locale defaultLocale, Locale... locales) { + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .localeResolver( new StaticFieldLocaleResolver() ) + .initializeLocales( new HashSet<>( Arrays.asList( locales ) ) ) + .defaultLocale( defaultLocale ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( Bean.class ) ) ) + .buildValidatorFactory(); + + return validatorFactory; + } + + private static class Bean { + } + + private static class StaticFieldLocaleResolver implements LocaleResolver { + + private static String acceptLanguage; + + @Override + public Locale resolve(LocaleResolverContext context) { + List localePriorities = LanguageRange.parse( acceptLanguage ); + if ( localePriorities.isEmpty() ) { + return context.getDefaultLocale(); + } + + List resolvedLocales = Locale.filter( localePriorities, context.getSupportedLocales() ); + if ( resolvedLocales.size() > 0 ) { + return resolvedLocales.get( 0 ); + } + + return context.getDefaultLocale(); + } + } + + private static class TestContext implements MessageInterpolator.Context { + + @Override + public ConstraintDescriptor getConstraintDescriptor() { + return null; + } + + @Override + public Object getValidatedValue() { + return null; + } + + @SuppressWarnings("unchecked") + @Override + public T unwrap(Class type) { + return (T) this; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeConstraintDefinitionContributorTest.java b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeConstraintDefinitionContributorTest.java new file mode 100644 index 0000000000..5f540bcc50 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeConstraintDefinitionContributorTest.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.predefinedscope; + +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.util.HashSet; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.test.constraintvalidator.MustMatch; +import org.hibernate.validator.test.constraintvalidator.MustNotMatch; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Test related to constraint validator discovery in the predefined scope case. + * + * @author Hardy Ferentschik + * @author Guillaume Smet + */ +@TestForIssue(jiraKey = "HV-1721") +public class PredefinedScopeConstraintDefinitionContributorTest { + + private Validator validator; + + @BeforeMethod + public void setUp() { + validator = getValidator(); + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Foo.class ); + beanMetaDataToInitialize.add( Quz.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + validator = validatorFactory.getValidator(); + } + + @Test + public void constraint_definitions_can_be_configured_via_service_loader() { + Set> constraintViolations = validator.validate( new Foo() ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( MustMatch.class ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-953") + public void constraints_defined_via_constraint_definition_contributor_can_have_default_message() { + Set> constraintViolations = validator.validate( new Foo() ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( MustMatch.class ).withMessage( "MustMatch default message" ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-953") + public void user_can_override_default_message_of_constraint_definition_contributor() { + Set> constraintViolations = validator.validate( new Quz() ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( MustNotMatch.class ).withMessage( "MustNotMatch user message" ) + ); + } + + public class Foo { + // constraint validator defined in service file! + @MustMatch("Foo") + String getFoo() { + return "Bar"; + } + } + + public class Quz { + // constraint validator defined in service file! + @MustNotMatch("Foo") + String getFoo() { + return "Foo"; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java new file mode 100644 index 0000000000..d92a8362d5 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/predefinedscope/PredefinedScopeValidatorFactoryTest.java @@ -0,0 +1,493 @@ +/* + * 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.predefinedscope; + +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.fail; + +import java.lang.annotation.ElementType; +import java.util.AbstractCollection; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.MessageInterpolator; +import javax.validation.Path; +import javax.validation.Path.Node; +import javax.validation.TraversableResolver; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotNull; + +import org.assertj.core.api.Assertions; +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.Test; + +@TestForIssue(jiraKey = "HV-1667") +public class PredefinedScopeValidatorFactoryTest { + + @Test + public void testValidation() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.validate( new Bean( "property", "test@example.com" ) ); + assertNoViolations( violations ); + + violations = validator.validate( new Bean( null, "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "property" ), + violationOf( Email.class ).withProperty( "email" ) ); + + violations = validator.forExecutables() + .validateParameters( new Bean(), Bean.class.getMethod( "setEmail", String.class ), + new Object[]{ "invalid" } ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ) + .withPropertyPath( pathWith().method( "setEmail" ).parameter( "email", 0 ) ) ); + + violations = validator.forExecutables() + .validateReturnValue( new Bean(), Bean.class.getMethod( "getEmail" ), "invalid" ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ) + .withPropertyPath( pathWith().method( "getEmail" ).returnValue() ) ); + + violations = validator.forExecutables() + .validateConstructorParameters( Bean.class.getConstructor( String.class, String.class ), + new Object[]{ null, "invalid" } ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).parameter( "property", 0 ) ), + violationOf( Email.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).parameter( "email", 1 ) ) ); + + violations = validator.forExecutables() + .validateConstructorReturnValue( Bean.class.getConstructor( String.class, String.class ), + new Bean( null, "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).returnValue() + .property( "property" ) ), + violationOf( Email.class ) + .withPropertyPath( pathWith().constructor( Bean.class ).returnValue() + .property( "email" ) ) ); + } + + @Test + public void testValidationOnUnknownBean() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.validate( new UnknownBean() ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanMethodParameter() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.validate( new UnknownBean() ); + assertNoViolations( violations ); + + violations = validator.forExecutables() + .validateParameters( new UnknownBean(), UnknownBean.class.getMethod( "setMethod", String.class ), + new Object[]{ "" } ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanMethodReturnValue() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.forExecutables() + .validateReturnValue( new UnknownBean(), UnknownBean.class.getMethod( "getMethod" ), "" ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanConstructorParameters() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.forExecutables() + .validateConstructorParameters( UnknownBean.class.getConstructor( String.class ), + new Object[]{ "" } ); + assertNoViolations( violations ); + } + + @Test + public void testValidationOnUnknownBeanConstructorReturnValue() throws NoSuchMethodException, SecurityException { + Validator validator = getValidator(); + + Set> violations = validator.forExecutables() + .validateConstructorReturnValue( UnknownBean.class.getConstructor( String.class ), new UnknownBean() ); + assertNoViolations( violations ); + } + + @Test + public void testExistingInitializedLocale() { + Locale defaultLocale = Locale.getDefault(); + + try { + Locale.setDefault( Locale.FRANCE ); + + Validator validator = getValidatorWithInitializedLocale( Locale.FRANCE ); + + Set> violations = validator.validate( new Bean( "", "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ).withProperty( "email" ).withMessage( "doit être une adresse électronique syntaxiquement correcte" ) ); + } + finally { + Locale.setDefault( defaultLocale ); + } + } + + @Test + public void testUnavailableInitializedLocale() { + Locale defaultLocale = Locale.getDefault(); + + try { + Locale georgianLocale = new Locale( "ka", "GE" ); + + Locale.setDefault( georgianLocale ); + + Validator validator = getValidatorWithInitializedLocale( georgianLocale ); + + Set> violations = validator.validate( new Bean( "", "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ).withProperty( "email" ).withMessage( "must be a well-formed email address" ) ); + } + finally { + Locale.setDefault( defaultLocale ); + } + } + + @TestForIssue(jiraKey = "HV-1681") + @Test + public void testValidOnUnknownBean() { + Set> violations = getValidator().validate( new AnotherBean() ); + assertNoViolations( violations ); + } + + @Test(expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000250:.*") + public void testUninitializedLocale() { + Locale defaultLocale = Locale.getDefault(); + + try { + Locale.setDefault( Locale.FRANCE ); + + ValidatorFactory validatorFactory = getValidatorFactoryWithInitializedLocale( Locale.ENGLISH ); + + Validator validator = validatorFactory + .usingContext() + .messageInterpolator( new MessageInterpolator() { + + @Override + public String interpolate(String messageTemplate, Context context, Locale locale) { + return validatorFactory.getMessageInterpolator().interpolate( messageTemplate, context, locale ); + } + + @Override + public String interpolate(String messageTemplate, Context context) { + return validatorFactory.getMessageInterpolator().interpolate( messageTemplate, context, Locale.GERMAN ); + } + } ).getValidator(); + + Set> violations = validator.validate( new Bean( "", "invalid" ) ); + assertThat( violations ).containsOnlyViolations( + violationOf( Email.class ).withProperty( "email" ).withMessage( "doit être une adresse électronique syntaxiquement correcte" ) ); + } + finally { + Locale.setDefault( defaultLocale ); + } + } + + @Test + public void testBeanMetaDataClassNormalizerNoNormalizer() { + // In this case, as we haven't registered any metadata for the hierarchy, even if we have constraints, + // we won't have any violations. + Set> violations = getValidator().validate( new BeanProxy() ); + assertNoViolations( violations ); + + // Now let's register the metadata for Bean and see how it goes + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .initializeBeanMetaData( beanMetaDataToInitialize ) + .locales( Collections.singleton( Locale.getDefault() ) ) + .buildValidatorFactory(); + + // As we don't have any metadata for BeanProxy, we consider it is not constrained at all. + violations = validatorFactory.getValidator().validate( new BeanProxy() ); + assertNoViolations( violations ); + } + + @Test + public void testBeanMetaDataClassNormalizer() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) + .initializeBeanMetaData( beanMetaDataToInitialize ) + .locales( Collections.singleton( Locale.getDefault() ) ) + .beanMetaDataClassNormalizer( new MyProxyInterfaceBeanMetaDataClassNormalizer() ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + + Set> violations = validator.validate( new BeanProxy() ); + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "property" ) ); + } + + @Test + public void validatorSpecificTraversableResolver() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + beanMetaDataToInitialize.add( AnotherBean.class ); + beanMetaDataToInitialize.add( SomeEnum.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + + try { + Validator validator = validatorFactory.usingContext().traversableResolver( new ThrowExceptionTraversableResolver() ) + .getValidator(); + validator.validate( new Bean() ); + fail(); + } + catch (ValidationException e) { + Assertions.assertThat( e ).hasCauseExactlyInstanceOf( ValidatorSpecificTraversableResolverUsedException.class ); + } + } + + @Test + public void variousObjectTypes() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + beanMetaDataToInitialize.add( AnotherBean.class ); + beanMetaDataToInitialize.add( SomeEnum.class ); + beanMetaDataToInitialize.add( Values.Itr.class ); + + ValidatorFactory validatorFactory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + validator.validate( new Bean() ); + } + + @Test + public void customizingMessageInterpolator() { + ValidatorFactory validatorFactory = getValidatorFactory(); + + Validator validator = validatorFactory.getValidator(); + Set> violations = validator.validate( new Bean( null, null ) ); + assertThat( violations ).containsOnlyViolations( violationOf( NotNull.class ).withMessage( "must not be null" ) ); + + validator = validatorFactory.usingContext() + .messageInterpolator( new MessageInterpolator() { + + @Override + public String interpolate(String messageTemplate, Context context, Locale locale) { + return "another string"; + } + + @Override + public String interpolate(String messageTemplate, Context context) { + return "another string"; + } + } ).getValidator(); + + violations = validator.validate( new Bean( null, null ) ); + assertThat( violations ).containsOnlyViolations( violationOf( NotNull.class ).withMessage( "another string" ) ); + } + + private static ValidatorFactory getValidatorFactory() { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + beanMetaDataToInitialize.add( AnotherBean.class ); + + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) + .initializeBeanMetaData( beanMetaDataToInitialize ) + .buildValidatorFactory(); + } + + private static Validator getValidator() { + return getValidatorFactory().getValidator(); + } + + private static ValidatorFactory getValidatorFactoryWithInitializedLocale(Locale locale) { + Set> beanMetaDataToInitialize = new HashSet<>(); + beanMetaDataToInitialize.add( Bean.class ); + + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( Email.class.getName(), NotNull.class.getName() ) ) ) + .initializeBeanMetaData( beanMetaDataToInitialize ) + .locales( Collections.singleton( locale ) ) + .buildValidatorFactory(); + } + + private static Validator getValidatorWithInitializedLocale(Locale locale) { + return getValidatorFactoryWithInitializedLocale( locale ).getValidator(); + } + + private enum SomeEnum { + VALUE; + } + + final class Values extends AbstractCollection implements Collection { + public Iterator iterator() { + return new Itr( null ); + } + + public int size() { + return 0; + } + + public boolean isEmpty() { + return true; + } + + final class Itr implements Iterator { + private final Iterator iterator; + + Itr(final Iterator iterator) { + this.iterator = iterator; + } + + public boolean hasNext() { + return iterator.hasNext(); + } + + public String next() { + return ""; + } + } + } + + private static class Bean { + + @NotNull + private String property; + + @Email + private String email; + + public Bean() { + } + + @Valid + public Bean(@NotNull String property, @Email String email) { + this.property = property; + this.email = email; + } + + @SuppressWarnings("unused") + public String getProperty() { + return property; + } + + public @Email String getEmail() { + return email; + } + + @SuppressWarnings("unused") + public void setEmail(@Email String email) { + this.email = email; + } + } + + private static class AnotherBean { + + @Valid + private final UnknownBean bean; + + + private AnotherBean() { + bean = new UnknownBean(); + } + } + + private static class UnknownBean { + + public UnknownBean() { + } + + @SuppressWarnings("unused") + public UnknownBean(String parameter) { + } + + @SuppressWarnings("unused") + public String getMethod() { + return null; + } + + @SuppressWarnings("unused") + public void setMethod(String parameter) { + } + } + + 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 { + } + + private static class ThrowExceptionTraversableResolver implements TraversableResolver { + + @Override + public boolean isReachable(Object traversableObject, Node traversableProperty, Class rootBeanType, Path pathToTraversableObject, + ElementType elementType) { + throw new ValidatorSpecificTraversableResolverUsedException(); + } + + @Override + public boolean isCascadable(Object traversableObject, Node traversableProperty, Class rootBeanType, Path pathToTraversableObject, + ElementType elementType) { + throw new ValidatorSpecificTraversableResolverUsedException(); + } + } + + private static class ValidatorSpecificTraversableResolverUsedException extends RuntimeException { + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertySelectionStrategyTest.java b/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertySelectionStrategyTest.java new file mode 100644 index 0000000000..816b82ee7b --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/properties/GetterPropertySelectionStrategyTest.java @@ -0,0 +1,283 @@ +/* + * 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.properties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.spi.properties.ConstrainableExecutable; +import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; +import org.hibernate.validator.testutil.ConstraintViolationAssert; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +public class GetterPropertySelectionStrategyTest { + + @Test + public void testGetterPropertySelectionStrategy() throws Exception { + GetterPropertySelectionStrategy strategy = new FooGetterPropertySelectionStrategy(); + + ConstrainableExecutableImpl fooMethod = new ConstrainableExecutableImpl( Foo.class.getDeclaredMethod( "fooMethod" ) ); + ConstrainableExecutableImpl getMethod = new ConstrainableExecutableImpl( Foo.class.getDeclaredMethod( "getMethod" ) ); + + assertThat( strategy.getProperty( fooMethod ) ).isPresent(); + assertThat( strategy.getProperty( getMethod ) ).isNotPresent(); + + assertThat( strategy.getProperty( fooMethod ).get() ).isEqualTo( "method" ); + } + + @Test + public void testConfigureGetterPropertySelectionStrategy() throws Exception { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .getterPropertySelectionStrategy( new FooGetterPropertySelectionStrategy() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new Foo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ) + ); + } + + @Test + public void testConfigureGetterPropertySelectionStrategyWithProperty() throws Exception { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .addProperty( HibernateValidatorConfiguration.GETTER_PROPERTY_SELECTION_STRATEGY_CLASSNAME, FooGetterPropertySelectionStrategy.class.getName() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new Foo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ) + ); + } + + @Test + public void testNoPrefixGetterPropertySelectionStrategy() { + Validator validator = Validation.byProvider( HibernateValidator.class ) + .configure() + .getterPropertySelectionStrategy( new NoPrefixGetterPropertySelectionStrategy() ) + .buildValidatorFactory() + .getValidator(); + Set> violations = validator.validate( new NoPrefixFoo() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( Min.class ).withProperty( "test" ), + violationOf( NotBlank.class ).withProperty( "name" ) + ); + } + + @Test + public void testGetterAndFieldWithSamePropertyNameButDifferentTypes() { + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new Bar() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( NotEmpty.class ).withProperty( "tags" ), + violationOf( Size.class ).withProperty( "tags" ) + ); + } + + @Test + public void testStrangePropertyNames() { + class StrangeProeprties { + @AssertTrue + public boolean isHasGet() { + return false; + } + + @AssertTrue + public boolean hasIsGet() { + return false; + } + + @NotNull + public String getHasIs() { + return null; + } + } + + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new StrangeProeprties() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( AssertTrue.class ).withProperty( "hasGet" ), + violationOf( AssertTrue.class ).withProperty( "isGet" ), + violationOf( NotNull.class ).withProperty( "hasIs" ) + ); + } + + @Test + @TestForIssue(jiraKey = "HV-622") + public void testIsGetterMethod() throws Exception { + class Bar { + @NotNull + public String getBar() { + return null; + } + + @NotEmpty + public String getBar(String param) { + return null; + } + } + Validator validator = ValidatorUtil.getValidator(); + + Set> violations = validator.validate( new Bar() ); + + ConstraintViolationAssert.assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withProperty( "bar" ) + ); + } + + private static class Bar { + + @Size(min = 10, max = 100) + private final String tags = ""; + + @NotEmpty + public List getTags() { + return Arrays.stream( tags.split( "," ) ) + .filter( tag -> !tag.trim().isEmpty() ) + .collect( Collectors.toList() ); + } + } + + private static class Foo { + + @Min(10) + public int fooMethod() { + return 1; + } + + @Max(-10) + public int getMethod() { + return 1; + } + } + + private static class NoPrefixFoo { + + @Min(10) + public int test() { + return 1; + } + + @NotBlank + public String name() { + return ""; + } + + @NotBlank + public String getName() { + return ""; + } + + @AssertTrue + public boolean isTest() { + return false; + } + + @Max(-10) + public int getTest() { + return 1; + } + } + + public static class NoPrefixGetterPropertySelectionStrategy implements GetterPropertySelectionStrategy { + + @Override + public Optional getProperty(ConstrainableExecutable executable) { + if ( executable.getReturnType() == void.class + || executable.getParameterTypes().length > 0 + || executable.getName().startsWith( "is" ) + || executable.getName().startsWith( "get" ) ) { + return Optional.empty(); + } + + return Optional.of( executable.getName() ); + } + + @Override + public Set getGetterMethodNameCandidates(String propertyName) { + return Collections.singleton( propertyName ); + } + } + + public static class FooGetterPropertySelectionStrategy implements GetterPropertySelectionStrategy { + + @Override + public Optional getProperty(ConstrainableExecutable executable) { + if ( executable.getParameterTypes().length > 0 + || !executable.getName().startsWith( "foo" ) ) { + return Optional.empty(); + } + + char[] chars = executable.getName().substring( 3 ).toCharArray(); + chars[0] = Character.toLowerCase( chars[0] ); + return Optional.of( new String( chars ) ); + } + + @Override + public Set getGetterMethodNameCandidates(String propertyName) { + return Collections.singleton( "foo" + Character.toUpperCase( propertyName.charAt( 0 ) ) + propertyName.substring( 1 ) ); + } + } + + private static class ConstrainableExecutableImpl implements ConstrainableExecutable { + + private final Method method; + + private ConstrainableExecutableImpl(Method method) { + this.method = method; + } + + @Override + public Class getReturnType() { + return method.getReturnType(); + } + + @Override + public String getName() { + return method.getName(); + } + + @Override + public Class[] getParameterTypes() { + return method.getParameterTypes(); + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/AnnotationPropertyNodeNameProvider.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/AnnotationPropertyNodeNameProvider.java new file mode 100644 index 0000000000..c7215ea880 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/AnnotationPropertyNodeNameProvider.java @@ -0,0 +1,90 @@ +/* + * 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.spi.nodenameprovider; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; +import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; +import org.hibernate.validator.spi.nodenameprovider.Property; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; + +/** + * An example of how a name can be resolved from an annotation + * + * @author Damir Alibegovic + */ +class AnnotationPropertyNodeNameProvider implements PropertyNodeNameProvider, Serializable { + private static final String VALUE = "value"; + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private final Class annotationType; + private final String annotationMemberName; + + AnnotationPropertyNodeNameProvider(Class annotationType) { + this( annotationType, VALUE ); + } + + AnnotationPropertyNodeNameProvider(Class annotationType, String annotationMemberName) { + this.annotationType = Objects.requireNonNull( annotationType ); + this.annotationMemberName = Objects.requireNonNull( annotationMemberName ); + } + + @Override + public String getName(Property property) { + if ( property instanceof JavaBeanProperty ) { + return getJavaBeanPropertyName( (JavaBeanProperty) property ); + } + + return getDefaultName( property ); + } + + private String getJavaBeanPropertyName(JavaBeanProperty property) { + Optional field = getField( property ); + + if ( field.isPresent() && field.get().isAnnotationPresent( annotationType ) ) { + return getAnnotationMemberValue( field.get(), annotationMemberName ) + .orElse( getDefaultName( property ) ); + } + else { + return getDefaultName( property ); + } + } + + private Optional getField(JavaBeanProperty property) { + return Arrays.stream( property.getDeclaringClass().getFields() ) + .peek( field -> field.setAccessible( true ) ) + .filter( field -> property.getName().equals( field.getName() ) ) + .findFirst(); + } + + private Optional getAnnotationMemberValue(Field field, String annotationMemberName) { + Annotation annotation = field.getAnnotation( annotationType ); + + try { + return Optional.of( + (String) annotation.annotationType().getMethod( annotationMemberName ).invoke( annotation ) ); + } + catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + LOG.error( "Unable to get annotation member value", e ); + + return Optional.empty(); + } + } + + private String getDefaultName(Property property) { + return property.getName(); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/PropertyNodeNameProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/PropertyNodeNameProviderTest.java new file mode 100644 index 0000000000..ba94821cfe --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/PropertyNodeNameProviderTest.java @@ -0,0 +1,241 @@ +/* + * 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.spi.nodenameprovider; + +import static org.testng.Assert.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Min; +import javax.validation.constraints.Size; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Damir Alibegovic + */ +@TestForIssue(jiraKey = "HV-823") +public class PropertyNodeNameProviderTest { + private static final String INVALID_BRAND_NAME = "BMW"; + private static final String VALID_BRAND_NAME = "Mercedes"; + + private Validator validator; + + @BeforeMethod + public void setUp() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new AnnotationPropertyNodeNameProvider( PropertyName.class ) ) + .buildValidatorFactory(); + + validator = validatorFactory.getValidator(); + } + + @Test + public void nameIsResolvedFromCustomAnnotationByUsingValidate() { + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.brand_name" ); + } + + @Test + public void nameIsResolvedFromCustomAnnotationByUsingValidateProperty() { + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = validator.validateProperty( testInstance, "brand.name" ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.brand_name" ); + } + + @Test + public void nameIsResolvedFromCustomAnnotationByUsingValidateValue() { + Set> violations = validator.validateValue( Brand.class, "name", INVALID_BRAND_NAME ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand_name" ); + } + + @Test + public void nameIsResolvedFromCustomAnnotationWithConstraintOnGetter() { + int horsePower = 125; + int speedInRpm = 500; + Airplane testInstance = new Airplane( new Engine( horsePower, speedInRpm ) ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "turbojet_engine.speed_in_rpm" ); + } + + @Test + public void propertyCanBeOfTypeMap() { + int horsePower = 0; + int speedInRpm = 1000; + Car testInstance = new Car( VALID_BRAND_NAME ); + testInstance.addComponent( "engine", new Engine( horsePower, speedInRpm ) ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "components[engine].horse_power" ); + + } + + @Test + public void defaultValidatorFactoryUsesDefaultPropertyNodeNameProvider() { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + Validator val = factory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Test + public void defaultProviderUsesDefaultPropertyNodeNameProvider() { + ValidatorFactory validatorFactory = Validation.byDefaultProvider() + .configure() + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Test + public void hibernateValidatorUsesDefaultPropertyNodeProvider() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Test + public void hibernateValidatorCanUseCustomPropertyNodeNameProvider() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new AnnotationPropertyNodeNameProvider( PropertyName.class ) ) + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.brand_name" ); + } + + @Test + public void hibernateValidatorFallsBackToDefaultPropertyNodeNameProvider() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( null ) + .buildValidatorFactory(); + Validator val = validatorFactory.getValidator(); + + Car testInstance = new Car( INVALID_BRAND_NAME ); + + Set> violations = val.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "brand.name" ); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD, ElementType.METHOD }) + public @interface PropertyName { + String value(); + } + + private class Car { + @PropertyName("components") + public final Map comps = new HashMap<>(); + + @Valid + public final Brand brand; + + Car(String brand) { + this.brand = new Brand( brand ); + } + + public void addComponent(String name, Object component) { + comps.put( name, component ); + } + } + + private class Brand { + @PropertyName("brand_name") + @Size(min = 4) + public final String name; + + Brand(String name) { + this.name = name; + } + } + + private class Engine { + @PropertyName("horse_power") + @Min(1) + public final int horsePower; + + @PropertyName("speed_in_rpm") + public final int speedInRpm; + + public Engine(int horsePower, int speedInRpm) { + this.horsePower = horsePower; + this.speedInRpm = speedInRpm; + } + + @Min(1000) + public int getSpeedInRpm() { + return speedInRpm; + } + } + + private class Airplane { + @Valid + @PropertyName("turbojet_engine") + public final Engine engine; + + public Airplane(Engine engine) { + this.engine = engine; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProvider.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProvider.java new file mode 100644 index 0000000000..829b5ad5f8 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProvider.java @@ -0,0 +1,50 @@ +/* + * 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.spi.nodenameprovider.jackson; + +import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; +import org.hibernate.validator.spi.nodenameprovider.Property; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +/** + * An example of how a name can be resolved from a Jackson annotation. + * + * @author Damir Alibegovic + */ +public class JacksonAnnotationPropertyNodeNameProvider implements PropertyNodeNameProvider { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public String getName(Property property) { + if ( property instanceof JavaBeanProperty ) { + return getJavaBeanPropertyName( (JavaBeanProperty) property ); + } + + return getDefaultName( property ); + } + + private String getJavaBeanPropertyName(JavaBeanProperty property) { + JavaType type = objectMapper.constructType( property.getDeclaringClass() ); + BeanDescription desc = objectMapper.getSerializationConfig().introspect( type ); + + return desc.findProperties() + .stream() + .filter( prop -> prop.getInternalName().equals( property.getName() ) ) + .map( BeanPropertyDefinition::getName ) + .findFirst() + .orElse( property.getName() ); + } + + private String getDefaultName(Property property) { + return property.getName(); + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProviderTest.java new file mode 100644 index 0000000000..b10f1c3767 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/spi/nodenameprovider/jackson/JacksonAnnotationPropertyNodeNameProviderTest.java @@ -0,0 +1,99 @@ +/* + * 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.spi.nodenameprovider.jackson; + +import static org.testng.Assert.assertEquals; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.testutil.TestForIssue; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * @author Damir Alibegovic + */ +@TestForIssue(jiraKey = "HV-823") +public class JacksonAnnotationPropertyNodeNameProviderTest { + private static final int VALID_HORSE_POWER = 150; + private static final int INVALID_HORSE_POWER = 250; + + private Validator validator; + + @BeforeMethod + public void setUp() { + ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) + .configure() + .propertyNodeNameProvider( new JacksonAnnotationPropertyNodeNameProvider() ) + .buildValidatorFactory(); + + validator = validatorFactory.getValidator(); + } + + @Test + public void jsonPropertyOnFieldIsUsedForPathResolution() { + Car testInstance = new Car( new Engine( INVALID_HORSE_POWER ) ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "engine.horse_power" ); + } + + @Test + public void jsonPropertyOnGetterIsUsedForPathResolution() { + int invalidNumberOfSeats = 0; + Car testInstance = new Car( new Engine( VALID_HORSE_POWER ), invalidNumberOfSeats ); + + Set> violations = validator.validate( testInstance ); + ConstraintViolation violation = violations.iterator().next(); + + assertEquals( violation.getPropertyPath().toString(), "number_of_seats" ); + } + + private class Car { + @Valid + private final Engine engine; + + @Min(1) + private final int numberOfSeats; + + Car(Engine engine) { + this( engine, 4 ); + } + + Car(Engine engine, int numberOfSeats) { + this.engine = engine; + this.numberOfSeats = numberOfSeats; + } + + @JsonProperty("number_of_seats") + public int getNumberOfSeats() { + return numberOfSeats; + } + } + + private class Engine { + @JsonProperty("horse_power") + @Max(200) + private final int horsePower; + + Engine(int horsePower) { + this.horsePower = horsePower; + } + } +} diff --git a/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java b/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java index ea3e4b917d..86ab72ad86 100644 --- a/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java +++ b/engine/src/test/java/org/hibernate/validator/testutils/ConstraintValidatorInitializationHelper.java @@ -8,16 +8,23 @@ import java.lang.annotation.Annotation; import java.time.Duration; +import java.util.Collections; import javax.validation.ClockProvider; import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.engine.DefaultClockProvider; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory; +import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; +import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind; +import org.hibernate.validator.internal.util.TypeResolutionHelper; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.spi.scripting.ScriptEvaluator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -27,7 +34,7 @@ */ public class ConstraintValidatorInitializationHelper { - private static final ConstraintHelper CONSTRAINT_HELPER = new ConstraintHelper(); + private static final ConstraintHelper CONSTRAINT_HELPER = ConstraintHelper.forAllBuiltinConstraints(); private static final HibernateConstraintValidatorInitializationContext DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT = getConstraintValidatorInitializationContext( new DefaultScriptEvaluatorFactory( null ), DefaultClockProvider.INSTANCE, Duration.ZERO ); @@ -40,7 +47,7 @@ public static ConstraintDescriptor descriptorFrom(Cons CONSTRAINT_HELPER, null, annotationDescriptor, - null + ConstraintLocationKind.GETTER ); } @@ -65,6 +72,13 @@ public static HibernateConstraintValidatorInitializationContext getDummyConstrai return DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT; } + public static ConstraintCreationContext getDummyConstraintCreationContext() { + return new ConstraintCreationContext( ConstraintHelper.forAllBuiltinConstraints(), + new ConstraintValidatorManagerImpl( new ConstraintValidatorFactoryImpl(), getDummyConstraintValidatorInitializationContext() ), + new TypeResolutionHelper(), + new ValueExtractorManager( Collections.emptySet() ) ); + } + public static HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext( ScriptEvaluatorFactory scriptEvaluatorFactory, ClockProvider clockProvider, Duration duration ) { 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..b91f10aad5 100644 --- a/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java +++ b/engine/src/test/java/org/hibernate/validator/testutils/ValidatorUtil.java @@ -28,6 +28,7 @@ import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; import org.hibernate.validator.internal.engine.DefaultClockProvider; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; +import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.testutil.DummyTraversableResolver; import org.hibernate.validator.testutil.ValidationInvocationHandler; @@ -235,6 +236,7 @@ public static T getValidatingProxy(I implementor, Validator exe } public static HibernateConstraintValidatorContext getConstraintValidatorContext() { - return new ConstraintValidatorContextImpl( null, DefaultClockProvider.INSTANCE, null, null, null ); + return new ConstraintValidatorContextImpl( DefaultClockProvider.INSTANCE, null, null, null, ExpressionLanguageFeatureLevel.BEAN_PROPERTIES, + ExpressionLanguageFeatureLevel.NONE ); } } diff --git a/engine/src/test/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor b/engine/src/test/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor deleted file mode 100644 index 78eaaae0e1..0000000000 --- a/engine/src/test/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor +++ /dev/null @@ -1 +0,0 @@ -org.hibernate.validator.test.internal.engine.cascaded.GuavaOptionalValueExtractor 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..6289842165 --- /dev/null +++ b/engine/src/test/resources/log4j2.properties @@ -0,0 +1,26 @@ +# 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 + +logger.constraintvalidatorcontextimpl.name = org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl +logger.constraintvalidatorcontextimpl.level = info +logger.constraintvalidatorcontextimpl.appenderRef.list.ref = List \ No newline at end of file diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-bean-methods.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-bean-methods.xml new file mode 100644 index 0000000000..671eed5699 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-bean-methods.xml @@ -0,0 +1,16 @@ + + + + + bean-methods + diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-default.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-default.xml new file mode 100644 index 0000000000..cc05e3311c --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-constraints-default.xml @@ -0,0 +1,16 @@ + + + + + default + diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-bean-methods.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-bean-methods.xml new file mode 100644 index 0000000000..ecd0155803 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-bean-methods.xml @@ -0,0 +1,16 @@ + + + + + bean-methods + diff --git a/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-default.xml b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-default.xml new file mode 100644 index 0000000000..62a07bde5a --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/el/validation-custom-violations-default.xml @@ -0,0 +1,16 @@ + + + + + default + 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/engine/messageinterpolation/locale-resolver-validation.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/messageinterpolation/locale-resolver-validation.xml new file mode 100644 index 0000000000..46c9c62df7 --- /dev/null +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/messageinterpolation/locale-resolver-validation.xml @@ -0,0 +1,16 @@ + + + + + org.hibernate.validator.test.internal.engine.messageinterpolation.LocaleResolverTest$StaticFieldLocaleResolver + diff --git a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml index 439487b288..fc903f4809 100644 --- a/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml +++ b/engine/src/test/resources/org/hibernate/validator/test/internal/engine/methodvalidation/xml/method-validation-mapping.xml @@ -113,12 +113,12 @@ - + [XML] - must not be null - + [XML] - must not be null 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..e5e5d31b93 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -11,7 +11,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.2.0.Final ../pom.xml @@ -21,10 +21,10 @@ Hibernate Validator WildFly integration tests. - ${project.build.directory}/wildfly-${wildfly.version} + ${project.build.directory}/wildfly-${version.wildfly} ${wildfly.target-dir}/modules/system/layers/base - ${project.build.directory}/wildfly-${wildfly-secondary.version} + ${project.build.directory}/wildfly-${version.wildfly.secondary} ${wildfly-secondary.target-dir}/modules/system/layers/base .. @@ -43,8 +43,8 @@ test - log4j - log4j + io.rest-assured + rest-assured test @@ -62,11 +62,6 @@ joda-time test - - org.jsoup - jsoup - test - javax.money money-api @@ -76,6 +71,24 @@ org.javamoney moneta test + + + javax.annotation + javax.annotation-api + + + + + + jakarta.annotation + jakarta.annotation-api + test + + + + jakarta.ws.rs + jakarta.ws.rs-api + provided org.jboss.arquillian.testng @@ -91,15 +104,37 @@ org.jboss.weld weld-core-impl test + + + org.jboss.spec.javax.interceptor + + jboss-interceptors-api_1.2_spec + + + + javax.enterprise + cdi-api + + + + + jakarta.interceptor + jakarta.interceptor-api + test - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + jakarta.enterprise + jakarta.enterprise.cdi-api test - org.jboss.spec.javax.ejb - jboss-ejb-api_3.2_spec + jakarta.persistence + jakarta.persistence-api + test + + + jakarta.ejb + jakarta.ejb-api test @@ -120,7 +155,7 @@ ${project.groupId} hibernate-validator-modules - wildfly-${wildfly.version}-patch + wildfly-${version.wildfly}-patch zip test @@ -157,6 +192,7 @@ wildfly-current-integration-test integration-test + verify @@ -165,38 +201,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 @@ -207,16 +218,7 @@ org.wildfly wildfly-dist - ${wildfly.version} - tar.gz - false - ${project.build.directory} - - - - org.wildfly - wildfly-dist - ${wildfly-secondary.version} + ${version.wildfly} tar.gz false ${project.build.directory} @@ -225,7 +227,7 @@ - copy-patch + copy-patch-current pre-integration-test copy @@ -237,16 +239,7 @@ ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly.version}-patch - zip - ${project.build.directory} - - - - ${project.groupId} - hibernate-validator-modules - ${project.version} - wildfly-${wildfly-secondary.version}-patch + wildfly-${version.wildfly}-patch zip ${project.build.directory} @@ -254,7 +247,7 @@ - copy-javamoney + copy-javamoney-current pre-integration-test copy @@ -265,28 +258,15 @@ javax.money money-api - ${javax-money.version} + ${version.javax.money} ${wildfly.modules-dir}/javax/money/api/main/ org.javamoney moneta - ${moneta.version} + ${version.org.javamoney.moneta} ${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 +292,6 @@ - - - copy-wildfly-secondary-resources - pre-integration-test - - copy-resources - - - ${wildfly-secondary.modules-dir} - - - src/test/modules - true - - - - @@ -344,26 +307,10 @@ true - ${project.build.directory}/wildfly-${wildfly.version}/ - false - - patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly.version}-patch.zip - - - - - - apply-wildfly-secondary-patch-file - pre-integration-test - - execute-commands - - - true - ${project.build.directory}/wildfly-${wildfly-secondary.version}/ + ${project.build.directory}/wildfly-${version.wildfly}/ false - patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly-secondary.version}-patch.zip + patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${version.wildfly}-patch.zip @@ -379,8 +326,152 @@ - --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED + --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 + ${version.wildfly.secondary} + tar.gz + false + ${project.build.directory} + + + + + + copy-patch-secondary + pre-integration-test + + copy + + + + + + ${project.groupId} + hibernate-validator-modules + ${project.version} + wildfly-${version.wildfly.secondary}-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-${version.wildfly.secondary}/ + false + + patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${version.wildfly.secondary}-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/MyConstraintMappingContributor.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/MyConstraintMappingContributor.java index 991438e6a7..fec466f1a9 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/MyConstraintMappingContributor.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/MyConstraintMappingContributor.java @@ -6,8 +6,6 @@ */ package org.hibernate.validator.integration.wildfly; -import java.lang.annotation.ElementType; - import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.spi.cfg.ConstraintMappingContributor; @@ -20,7 +18,7 @@ public class MyConstraintMappingContributor implements ConstraintMappingContribu public void createConstraintMappings(ConstraintMappingBuilder builder) { builder.addConstraintMapping() .type( Broomstick.class ) - .property( "brand", ElementType.FIELD ) + .field( "brand" ) .constraint( new NotNullDef() ); } } diff --git a/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java b/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java index 52c4f6218e..ab6d7246d5 100644 --- a/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java +++ b/integration/src/test/java/org/hibernate/validator/integration/wildfly/OptionalConstraintsIT.java @@ -18,7 +18,6 @@ import javax.validation.constraints.Future; import org.hibernate.validator.constraints.Currency; -import org.hibernate.validator.constraints.SafeHtml; import org.hibernate.validator.integration.AbstractArquillianIT; import org.javamoney.moneta.Money; import org.jboss.arquillian.container.test.api.Deployment; @@ -28,7 +27,7 @@ import org.testng.annotations.Test; /** - * Asserts that the constraints based on the JodaTime and JSoup server modules can be used. + * Asserts that the constraints based on the JodaTime and Javax Money server modules can be used. * * @author Gunnar Morling * @author Guillaume Smet @@ -57,14 +56,6 @@ public void canUseJodaTimeConstraintValidator() { assertThat( violations.iterator().next().getConstraintDescriptor().getAnnotation().annotationType() ).isEqualTo( Future.class ); } - @Test - public void canUseJsoupBasedConstraint() { - Set> violations = validator.validate( new Item() ); - - assertThat( violations.size() ).isEqualTo( 1 ); - assertThat( violations.iterator().next().getConstraintDescriptor().getAnnotation().annotationType() ).isEqualTo( SafeHtml.class ); - } - @Test public void canUseJavaMoneyBasedConstraint() { Set> violations = validator.validate( new Order( Money.of( 1200.0, "EUR" ) ) ); @@ -84,12 +75,6 @@ private static class Shipment { public DateTime deliveryDate = new DateTime( 2014, 10, 21, 0, 0, 0, 0 ); } - private static class Item { - - @SafeHtml - public String descriptionHtml = " + ${wildfly-main.patched.target-dir} @@ -148,7 +143,7 @@ - + ${wildfly-secondary.patched.target-dir} @@ -173,7 +168,7 @@ org.wildfly wildfly-dist - ${wildfly.version} + ${version.wildfly} tar.gz false ${project.build.directory}/wildfly-original @@ -181,7 +176,7 @@ org.wildfly wildfly-dist - ${wildfly.version} + ${version.wildfly} tar.gz false ${project.build.directory}/wildfly-patched @@ -190,7 +185,7 @@ org.wildfly wildfly-dist - ${wildfly-secondary.version} + ${version.wildfly.secondary} tar.gz false ${project.build.directory}/wildfly-original @@ -198,7 +193,7 @@ org.wildfly wildfly-dist - ${wildfly-secondary.version} + ${version.wildfly.secondary} tar.gz false ${project.build.directory}/wildfly-patched @@ -217,13 +212,13 @@ - javax.validation - validation-api - ${bv.api.version} + jakarta.validation + jakarta.validation-api + ${version.jakarta.validation-api} false ${wildfly-main.patched.target-dir}/modules/system/layers/base/javax/validation/api/main - validation-api-${bv.api.version}.jar + jakarta.validation-api-${version.jakarta.validation-api}.jar ${project.groupId} @@ -243,13 +238,13 @@ - javax.validation - validation-api - ${bv.api.version} + jakarta.validation + jakarta.validation-api + ${version.jakarta.validation-api} false ${wildfly-secondary.patched.target-dir}/modules/system/layers/base/javax/validation/api/main - validation-api-${bv.api.version}.jar + jakarta.validation-api-${version.jakarta.validation-api}.jar ${project.groupId} @@ -284,7 +279,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 +292,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 +302,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..5b759320a2 100644 --- a/modules/src/main/modules/wildfly-current-patch.xml +++ b/modules/src/main/modules/wildfly-current-patch.xml @@ -6,11 +6,11 @@ ~ See the license.txt file in the root directory or . --> - wildfly-${wildfly.version}-hibernate-validator-${project.version} - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly.version} installation - + wildfly-${version.wildfly}-hibernate-validator-${project.version} + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly} installation + - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly.version} installation + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly} installation diff --git a/modules/src/main/modules/wildfly-secondary-patch.xml b/modules/src/main/modules/wildfly-secondary-patch.xml index 8692b9b7f8..5700566477 100644 --- a/modules/src/main/modules/wildfly-secondary-patch.xml +++ b/modules/src/main/modules/wildfly-secondary-patch.xml @@ -6,11 +6,11 @@ ~ See the license.txt file in the root directory or . --> - wildfly-${wildfly-secondary.version}-hibernate-validator-${project.version} - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly-secondary.version} installation - + wildfly-${version.wildfly.secondary}-hibernate-validator-${project.version} + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly.secondary} installation + - This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${wildfly-secondary.version} installation + This patch upgrades Hibernate Validator to ${project.version} within a WildFly ${version.wildfly.secondary} installation diff --git a/modules/src/script/setupModules.groovy b/modules/src/script/setupModules.groovy index f4763c3c88..dbc2d2ea48 100644 --- a/modules/src/script/setupModules.groovy +++ b/modules/src/script/setupModules.groovy @@ -17,15 +17,19 @@ def appendDependency(File file, String dependencyToAppend, boolean optional) { file.write( file.text.replaceAll( /<\/dependencies>/, ' \n ' ) ) } -// BV API +def removeDependency(File file, String dependencyToRemove) { + file.write( file.text.replaceAll( //, '' ) ) +} + +// Jakarta Bean Validation 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'; -println "[INFO] Using BV version " + bvArtifactName; +def bvArtifactName = 'jakarta.validation-api-' + project.properties['version.jakarta.validation-api'] + '.jar'; +println "[INFO] Using Jakarta Bean Validation version " + bvArtifactName; processFileInplace( bvModuleXml ) { text -> - text.replaceAll( /validation-api.*jar/, bvArtifactName ) + text.replaceAll( / 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 ) diff --git a/osgi/felixtest/pom.xml b/osgi/felixtest/pom.xml index 7ce49d4fb6..258006ca94 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.2.0.Final ../pom.xml @@ -28,7 +28,7 @@ org.jboss.arquillian arquillian-bom - ${arquillian.version} + ${version.org.jboss.arquillian} import pom @@ -48,13 +48,13 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api provided - javax.enterprise - cdi-api + jakarta.enterprise + jakarta.enterprise.cdi-api provided @@ -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,8 +85,8 @@ fish.payara.arquillian - arquillian-payara-server-4-managed - ${payara-arquillian.version} + arquillian-payara-server-managed + ${version.fish.payara.arquillian} test @@ -131,7 +141,7 @@ fish.payara.distributions payara - ${payara.version} + ${version.fish.payara} zip false ${project.build.directory} @@ -165,6 +175,15 @@ hibernate-validator-cdi.jar true + + jakarta.validation + jakarta.validation-api + ${bv.api.version} + jar + ${project.build.directory}/payara5/glassfish/modules + validation-api.jar + true + 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..a2eb8cd20d 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.2.0.Final ../pom.xml @@ -31,8 +31,8 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api test @@ -64,6 +64,18 @@ org.javamoney moneta test + + + javax.annotation + javax.annotation-api + + + + + + jakarta.annotation + jakarta.annotation-api + test @@ -77,13 +89,13 @@ org.ops4j.pax.exam pax-exam-container-karaf - ${pax.exam.version} + ${version.org.ops4j.pax.exam} test org.apache.karaf apache-karaf - ${apache.karaf.version} + ${version.org.apache.karaf} tar.gz test + true + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + + hv-6.1 + + + validator + hv-6.1 + + + + Hibernate Validator + 6.1.2.Final + + + + jakarta.validation + jakarta.validation-api + + + ${project.groupId} + hibernate-validator + ${beanvalidation-impl.version} + + + org.glassfish + jakarta.el log4j @@ -202,13 +246,15 @@ + 2.0.1.Final Hibernate Validator - 6.0.8.Final + 6.0.19.Final javax.validation validation-api + ${validation-api.version} ${project.groupId} @@ -218,6 +264,7 @@ org.glassfish javax.el + 3.0.1-b11 log4j @@ -245,7 +292,7 @@ 1.1.0.Final Hibernate Validator - 5.4.2.Final + 5.4.3.Final @@ -261,6 +308,7 @@ org.glassfish javax.el + 3.0.1-b11 log4j 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-bv2/org/hibernate/validator/performance/simple/SimpleComposingConstraintValidation.java b/performance/src/main/java-bv2/org/hibernate/validator/performance/simple/SimpleComposingConstraintValidation.java new file mode 100644 index 0000000000..46c21ce1bc --- /dev/null +++ b/performance/src/main/java-bv2/org/hibernate/validator/performance/simple/SimpleComposingConstraintValidation.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.performance.simple; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.validation.Constraint; +import javax.validation.ConstraintViolation; +import javax.validation.Payload; +import javax.validation.ReportAsSingleViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +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 Marko Bekhta + */ +public class SimpleComposingConstraintValidation { + + @State(Scope.Benchmark) + public static class ValidationState { + + public volatile Validator validator; + + { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @Fork(value = 1) + @Threads(50) + @Warmup(iterations = 10) + @Measurement(iterations = 20) + public void testSimpleComposingConstraintValidation(ValidationState state, Blackhole bh) { + Foo foo = new Foo( "" ); + Set> violations = state.validator.validate( foo ); + bh.consume( violations ); + } + + public static class Foo { + + @ComposingConstraint + private final String foo; + + public Foo(String foo) { + this.foo = foo; + } + } + + @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) + @Retention(RUNTIME) + @Documented + @Constraint(validatedBy = { }) + @ReportAsSingleViolation + @NotNull + @Size(min = 1) + @NotBlank + @NotEmpty + @interface ComposingConstraint { + + String message() default "message"; + + Class[] groups() default { }; + + Class[] payload() default { }; + } +} diff --git a/performance/src/main/java/org/hibernate/validator/performance/simple/ExecutableValidation.java b/performance/src/main/java/org/hibernate/validator/performance/simple/ExecutableValidation.java new file mode 100644 index 0000000000..f1948d189f --- /dev/null +++ b/performance/src/main/java/org/hibernate/validator/performance/simple/ExecutableValidation.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.performance.simple; + +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.ValidatorFactory; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.executable.ExecutableValidator; + +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 Hardy Ferentschik + */ +public class ExecutableValidation { + + private static final String[] names = { + null, + "Jacob", + "Isabella", + "Ethan", + "Sophia", + "Michael", + "Emma", + "Jayden", + "Olivia", + "William" + }; + + @State(Scope.Benchmark) + public static class ValidationState { + public volatile ExecutableValidator validator; + public volatile ThreadLocalRandom random; + public volatile Driver[] drivers; + + { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator().forExecutables(); + 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 testExecutableValidation(ValidationState state, Blackhole bh) throws NoSuchMethodException, SecurityException { + Driver driver = state.nextDriver(); + Set> violations = state.validator.validateParameters( new DriverFactory(), + DriverFactory.class.getMethod( "createDriver", String.class, int.class, boolean.class ), + new Object[]{ driver.name, driver.age, driver.hasDrivingLicense } ); + assert driver.getExpectedViolationCount() == violations.size(); + bh.consume( violations ); + } + + public static class DriverFactory { + + public Driver createDriver(@NotNull String name, @Min(18) int age, @AssertTrue boolean hasDrivingLicense) { + return new Driver( name, age, hasDrivingLicense, age ); + } + } + + public static class Driver { + private String name; + + private int age; + + private 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 + 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 int expectedViolationCount; + private Driver driver; + + public DriverSetup(ThreadLocalRandom random) { + expectedViolationCount = 0; + + String name = names[random.nextInt( 10 )]; + if ( name == null ) { + expectedViolationCount++; + } + + int randomAge = random.nextInt( 100 ); + if ( randomAge < 18 ) { + expectedViolationCount++; + } + + int rand = random.nextInt( 2 ); + boolean hasLicense = rand == 1; + if ( !hasLicense ) { + expectedViolationCount++; + } + + driver = new Driver( name, randomAge, hasLicense, expectedViolationCount ); + } + + public Driver getDriver() { + return driver; + } + } +} 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..df7c0c6cfb 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.2.0.Final pom Hibernate Validator Aggregator @@ -86,144 +86,223 @@ test-utils build-config engine - cdi - modules tck-runner annotation-processor - integration performance - osgi - 1.8 - 1.8 + + + 6.0.10.Final + + + + http://beanvalidation.org/2.0/spec/ + http://docs.oracle.com/javase/8/docs/api + http://docs.oracle.com/javase/8/docs/technotes + http://docs.oracle.com/javaee/7/api + http://docs.oracle.com/javase/8/javase-clienttechnologies.htm + http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api + http://javamoney.github.io/apidocs + org.hibernate.validator org.hibernate.validator.cdi - - org.hibernate.validator.hibernate-validator - org.hibernate.validator.hibernate-validator-cdi - - UTF-8 - UTF-8 - - true + - 2.0.1.Final - 2.0.2.Final - - - 6.0.4.Final + 2.0.2 + 2.0.6 - 2.8 - 3.0.1-b09 - 3.3.2.Final - 2.1.0.Final + 2.8 + 3.0.3 + 3.4.1.Final + 2.2.1.Final - 12.0.0.Final + 19.0.0.Final - 11.0.0.Final + 18.0.1.Final - ${wildfly.version} + ${version.wildfly} - 1.3.4 - 1.8.3 - 2.9.7 - 1.7.22 - 1.0.2.Final + 1.5.1 + 2.9.7 + 1.7.22 + 2.13.3 + 2.2.3 - 2.0 - 3.0.3.Final - 2.1.0.Final - 1.0.1.Final + 2.0.1 + 3.1.1.Final + 2.2.0.Final + 3.2.5 + 1.2.4 + 1.3.5 + 2.1.6 - 1.0.1 - 1.1 - - 1.2 + 1.0.1 + 1.1 - - 2.0.1.Alpha5 - 5.0.3 - 1.2.1.Final - 4.0.0.Final + + 1.2 + + + 11.0.2 - 1.1.11.Final - 6.8 + 1.6.0.Final + 6.14.3 - 3.8.0 - 4.12 - 3.4 + 3.8.0 + 4.13.1 + 3.4 + 4.1.2 + 2.4.12 + 27.1-jre + 4.3.10.RELEASE + 1.0.0.Final + 2.10.3 + 2.10.3 + 1.10.16 - 4.11.0 - 2.5.2 - 4.1.1 - 6.0.0 - 5.Beta1 - 1.0.Beta2 + 4.2.0 + 4.12.0 + 2.5.4 + 6.0.0 + 5.2020.2 + 2.3.1 - 8.1 + + 8.38 - 2.4.12 + - 23.0 + 1.5.6 + 1.0.3.Final + 1.0.3.Final + 9.1.15.0 + 1.6.0-alpha.5 + 1.5.0-alpha.16 - 4.3.10.RELEASE + - - 1.0.1.Final - 1.0.3.Final - 1.5.5 - 9.1.8.0 - 1.6.0-alpha.5 - 1.5.0-alpha.16 + 1.8 + 3.1.0 + 3.0.0 + 3.5.0 + 3.1.1 + 3.0.0 + 3.8.1 + 0.0.6 + 3.0.2 + 1.4.0 + 2.8.2 + 3.0.0-M1 + 2.5 + 1.6 + 2.5.2 + 0.11.0 + 3.0.2 + 1.3.0 + 3.0.1 + 3.0 + 2.5.3 + 3.0.2 + 3.1.0 + 1.2 + 3.0.1 + 2.21.0 + ${version.surefire.plugin} + 1.2.1.Final + 5.0.0.Final + 2.0.1.Final + 5.0.3 - - http://beanvalidation.org/2.0/spec/ - http://docs.oracle.com/javase/8/docs/api - http://docs.oracle.com/javase/8/docs/technotes - http://docs.oracle.com/javaee/7/api - http://docs.oracle.com/javase/8/javase-clienttechnologies.htm - http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api - http://javamoney.github.io/apidocs + + forbidden-junit.txt + + + + jboss-releases-repository + https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ + jboss-snapshots-repository + https://repository.jboss.org/nexus/content/repositories/snapshots/ + + - 2.20.1 + https://repo.maven.apache.org/maven2/ - - - - + + . + + UTF-8 + UTF-8 + + + true + + 1.8 + 1.8 + ${maven.compiler.target} + ${maven.compiler.source} - java.xml.bind - java.xml.bind + ${maven.compiler.target} + ${maven.compiler.source} + ${maven.compiler.testTarget} + ${maven.compiler.testSource} - - + 10 - - forbidden-junit.txt + + + + + + + -Dmaven.repo.local=${settings.localRepository} + + ${surefire.jvm.args.additional} ${surefire.jvm.args.add-opens} ${surefire.jvm.args.illegal-access} ${surefire.jvm.args.shrinkwrap} + ${surefire.jvm.args.additional} ${surefire.jvm.args.add-opens} ${surefire.jvm.args.illegal-access} ${surefire.jvm.args.shrinkwrap} - . + + default + + + + ${arquillian.wildfly.jvm.args.add-opens} ${arquillian.wildfly.jvm.args.add-modules} + + + ${maven.compiler.argument.source} + 3.3.1 @@ -252,162 +331,178 @@ ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly.version}-patch + wildfly-${version.wildfly}-patch zip ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly-secondary.version}-patch + wildfly-${version.wildfly.secondary}-patch zip - javax.validation - validation-api - ${bv.api.version} + jakarta.validation + jakarta.validation-api + ${version.jakarta.validation-api} org.jboss.logging jboss-logging - ${jboss.logging.version} + ${version.org.jboss.logging.jboss-logging} org.jboss.logging jboss-logging-processor - ${jboss.logging.processor.version} + ${version.org.jboss.logging.jboss-logging-tools} org.jboss.logging jboss-logging-annotations - ${jboss.logging.processor.version} + ${version.org.jboss.logging.jboss-logging-tools} org.glassfish - javax.el - ${javax.el.version} + jakarta.el + ${version.org.glassfish.jakarta.el} com.fasterxml classmate - ${classmate.version} + ${version.com.fasterxml.classmate} joda-time joda-time - ${joda-time.version} + ${version.joda-time} javax.money money-api - ${javax-money.version} + ${version.javax.money} org.javamoney moneta - ${moneta.version} + ${version.org.javamoney.moneta} + + + net.bytebuddy + byte-buddy + ${version.net.bytebuddy.byte-buddy} org.osgi org.osgi.core - ${osgi-core.version} + ${version.org.osgi.core} - org.jsoup - jsoup - ${jsoup.version} + org.apache.logging.log4j + log4j-core + ${version.org.apache.logging.log4j} - log4j - log4j - 1.2.17 + org.apache.logging.log4j + log4j-core + test-jar + ${version.org.apache.logging.log4j} org.slf4j slf4j-api - ${slf4j.version} + ${version.org.slf4j} - org.slf4j - slf4j-log4j12 - ${slf4j.version} + org.apache.logging.log4j + log4j-slf4j-impl + ${version.org.apache.logging.log4j} - org.hibernate.javax.persistence - hibernate-jpa-2.1-api - ${hibernate-jpa-2.1-api.version} + jakarta.persistence + jakarta.persistence-api + ${version.jakarta.persistence-api} junit junit - ${junit.version} + ${version.junit} org.testng testng - ${testng.version} + ${version.org.testng} org.codehaus.groovy groovy-jsr223 - ${groovy.version} + ${version.org.codehaus.groovy} org.easymock easymock - ${easymock.version} + ${version.org.easymock} org.assertj assertj-core - ${assertj-core.version} + ${version.org.assertj.assertj-core} + + + io.rest-assured + rest-assured + ${version.io.rest-assured} org.jboss.arquillian arquillian-bom - ${arquillian.version} + ${version.org.jboss.arquillian} pom import - javax.annotation - javax.annotation-api - 1.2 + jakarta.annotation + jakarta.annotation-api + ${version.jakarta.annotation-api} + + + jakarta.ws.rs + jakarta.ws.rs-api + ${version.jakarta.ws.rs-api} - org.jboss.spec.javax.interceptor - jboss-interceptors-api_1.2_spec - 1.0.0.Final + jakarta.interceptor + jakarta.interceptor-api + ${version.jakarta.interceptor-api} - org.jboss.spec.javax.ejb - jboss-ejb-api_3.2_spec - ${jboss-ejb-api_3.2_spec.version} + jakarta.ejb + jakarta.ejb-api + ${version.jakarta.ejb-api} - javax.enterprise - cdi-api - ${cdi-api.version} + jakarta.enterprise + jakarta.enterprise.cdi-api + ${version.jakarta.enterprise.cdi-api} - javax.interceptor - javax.interceptor-api + jakarta.interceptor + jakarta.interceptor-api - javax.el - javax.el-api + jakarta.el + jakarta.el-api org.jboss.weld weld-core-impl - ${weld.version} + ${version.org.jboss.weld.weld} org.wildfly.arquillian wildfly-arquillian-container-managed - ${wildfly-arquillian.version} + ${version.org.wildfly.arquillian} sun.jdk @@ -418,38 +513,46 @@ org.jboss.arquillian.container arquillian-weld-se-embedded-1.1 - 1.0.0.Final + ${version.org.jboss.arquillian.container.arquillian-weld-se-embedded-1.1} + test com.thoughtworks.paranamer paranamer - ${paranamer.version} + ${version.com.thoughtworks.paranamer} com.google.guava guava - ${guava.version} + ${version.com.google.guava} + test org.springframework spring-expression - ${spring-expression.version} + ${version.org.springframework.spring-expression} + test + + + com.fasterxml.jackson.core + jackson-databind + ${version.com.fasterxml.jackson.core.jackson-databind} + test + + + com.fasterxml.jackson.core + jackson-annotations + ${version.com.fasterxml.jackson.core.jackson-annotations} + test - - - org.apache.maven.wagon - wagon-webdav - 1.0-beta-2 - - maven-enforcer-plugin - 3.0.0-M1 + ${version.enforcer.plugin} enforce-java @@ -459,11 +562,21 @@ - [1.8.0-20,) + [${jdk.min.version},) - 3.3.1 + ${maven.min.version} + + + javax.validation:validation-api + org.glassfish:javax.el + javax.annotation:javax.annotation-api + org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec + javax.enterprise:cdi-api + javax.persistence:javax.persistence-api + + @@ -478,15 +591,15 @@ maven-antrun-plugin - 1.8 + ${version.antrun.plugin} maven-clean-plugin - 3.0.0 + ${version.clean.plugin} maven-jar-plugin - 3.0.2 + ${version.jar.plugin} @@ -501,8 +614,12 @@ maven-compiler-plugin - 3.7.0 + ${version.compiler.plugin} + ${maven.compiler.argument.source} + ${maven.compiler.argument.target} + ${maven.compiler.argument.testSource} + ${maven.compiler.argument.testTarget} -Aorg.jboss.logging.tools.addGeneratedAnnotation=false @@ -511,7 +628,7 @@ maven-checkstyle-plugin - 2.17 + ${version.checkstyle.plugin} ${project.groupId} @@ -532,12 +649,12 @@ org.slf4j jcl-over-slf4j - ${slf4j.version} + ${version.org.slf4j} org.slf4j slf4j-jdk14 - ${slf4j.version} + ${version.org.slf4j} @@ -573,8 +690,9 @@ de.thetaphi forbiddenapis - 2.4.1 + ${version.forbiddenapis.plugin} + ${forbiddenapis.jdk.target} false @@ -606,13 +724,11 @@ verify - + jdk-unsafe jdk-deprecated - jdk-system-out + jdk-non-portable + jdk-internal @@ -624,10 +740,6 @@ verify - jdk-deprecated @@ -637,7 +749,7 @@ com.mycila license-maven-plugin - 3.0 + ${version.license.plugin}

${hibernate-validator-parent.path}/build-config/src/main/resources/license.header
true @@ -675,19 +787,20 @@ maven-surefire-plugin - ${maven-surefire-plugin.version} + ${version.surefire.plugin} once true **/*Test.java - ${maven-surefire-plugin.argLine} + ${surefire.jvm.args} + ${surefire.environment} maven-surefire-report-plugin - ${maven-surefire-plugin.version} + ${version.surefire.plugin} generate-test-report @@ -704,22 +817,23 @@ maven-failsafe-plugin - ${maven-surefire-plugin.version} + ${version.failsafe.plugin} - ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.argLine.add-modules} ${maven-surefire-plugin.argLine.add-opens} + ${failsafe.jvm.args} + ${surefire.environment} maven-dependency-plugin - 3.0.2 + ${version.dependency.plugin} maven-install-plugin - 2.5.2 + ${version.install.plugin} maven-assembly-plugin - 3.1.0 + ${version.assembly.plugin} - 1.5.0 - - - org.codehaus.mojo - jaxb2-maven-plugin - 2.2 - org.asciidoctor asciidoctor-maven-plugin - ${asciidoctor-maven-plugin.version} + ${version.asciidoctor.plugin} org.jruby jruby-complete - ${jruby.version} + ${version.org.jruby} org.asciidoctor asciidoctorj - ${asciidoctorj.version} + ${version.org.asciidoctor.asciidoctorj} org.asciidoctor asciidoctorj-pdf - ${asciidoctorj-pdf.version} + ${version.org.asciidoctor.asciidoctorj-pdf} org.hibernate.infra hibernate-asciidoctor-extensions - ${hibernate-asciidoctor-extensions.version} + ${version.org.hibernate.infra.hibernate-asciidoctor-extensions} ch.mfrey.maven.plugin copy-maven-plugin - 0.0.6 - - - maven-project-info-reports-plugin - 2.9 + ${version.copy.plugin} org.apache.felix maven-bundle-plugin - 3.5.0 + ${version.bundle.plugin} maven-source-plugin - 3.0.1 + ${version.source.plugin} maven-javadoc-plugin - 3.0.0 + ${version.javadoc.plugin} true true @@ -821,11 +917,11 @@ maven-deploy-plugin - 2.8.2 + ${version.deploy.plugin} maven-resources-plugin - 3.0.2 + ${version.resources.plugin} @@ -838,24 +934,24 @@ org.codehaus.gmavenplus gmavenplus-plugin - 1.6 + ${version.gmavenplus.plugin} org.codehaus.groovy groovy-all - ${groovy.version} + ${version.org.codehaus.groovy} org.apache.servicemix.tooling depends-maven-plugin - 1.4.0 + ${version.depends.plugin} org.codehaus.mojo build-helper-maven-plugin - 1.12 + ${version.buildhelper.plugin} com.github.siom79.japicmp japicmp-maven-plugin - 0.11.0 + ${version.japicmp.plugin} @@ -892,50 +988,43 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + ${version.shade.plugin} org.jboss.as patch-gen-maven-plugin - ${wildfly-patch-gen-maven-plugin.version} + ${version.wildfly-patch-gen.plugin} com.fasterxml.woodstox woodstox-core - ${wildfly-patch-gen-maven-plugin.woodstox.version} + ${version.wildfly-patch-gen.plugin.woodstox} org.wildfly.plugins wildfly-maven-plugin - ${wildfly-maven-plugin.version} + ${version.wildfly.plugin} org.wildfly.core wildfly-patching - ${wildfly-core.version} + ${version.org.wildfly.core} - org.wildfly.core wildfly-cli - ${wildfly-core.version} - - - sun.jdk - jconsole - - + ${version.org.wildfly.core} org.netbeans.tools sigtest-maven-plugin - 1.0 + ${version.sigtest.plugin} @@ -1064,18 +1153,18 @@ scm:git:git@github.com:hibernate/hibernate-validator.git http://github.com/hibernate/hibernate-validator HEAD - + - jboss-releases-repository + ${jboss.releases.repo.id} JBoss Releases Repository - https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ + ${jboss.releases.repo.url} - jboss-snapshots-repository + ${jboss.snapshots.repo.id} JBoss Snapshots Repository - https://repository.jboss.org/nexus/content/repositories/snapshots/ + ${jboss.snapshots.repo.url} @@ -1087,6 +1176,8 @@ disableDocumentationBuild !true + + (,12) documentation @@ -1099,6 +1190,8 @@ disableDistributionBuild !true + + (,12) distribution @@ -1135,17 +1228,16 @@ - 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 +1252,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,18 +1261,59 @@ maven-compiler-plugin true - - -J--add-modules=java.xml.ws.annotation - - maven-surefire-plugin - ${maven-surefire-plugin.version} + org.wildfly.plugins + wildfly-maven-plugin - ${maven-surefire-plugin.argLine} ${maven-surefire-plugin.argLine.add-modules} ${maven-surefire-plugin.argLine.add-opens} + + --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.security=ALL-UNNAMED + --add-opens=java.base/java.io=ALL-UNNAMED + + + + + + + + jdk10- + + (,11) + + + osgi + + + + + jdk11- + + (,12) + + + + cdi + modules + integration + + + + jdk11+ + + [11,) + + + + --add-modules=java.se + + + + + org.wildfly.plugins wildfly-maven-plugin @@ -1189,9 +1322,19 @@ --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 + + + @@ -1210,7 +1353,7 @@ com.buschmais.jqassistant jqassistant-maven-plugin - 1.3.0 + ${version.jqassistant.plugin} @@ -1226,5 +1369,32 @@ + + IDEA + + + + 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..57a6fad93f 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.2.0.Final org.hibernate diff --git a/relocation/cdi/pom.xml b/relocation/cdi/pom.xml index 5346ffb489..f24dabd63b 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.2.0.Final org.hibernate diff --git a/relocation/engine/pom.xml b/relocation/engine/pom.xml index f8b6393a23..989eccea18 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.2.0.Final org.hibernate diff --git a/relocation/karaf-features/pom.xml b/relocation/karaf-features/pom.xml index a237283b0a..441f111dfb 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.2.0.Final org.hibernate diff --git a/relocation/pom.xml b/relocation/pom.xml index f8b8ce054d..c76185a5af 100644 --- a/relocation/pom.xml +++ b/relocation/pom.xml @@ -10,7 +10,7 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.2.0.Final 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..93147a432e 100644 --- a/tck-runner/pom.xml +++ b/tck-runner/pom.xml @@ -11,18 +11,18 @@ org.hibernate.validator hibernate-validator-parent - 6.0.10-SNAPSHOT + 6.2.0.Final ../pom.xml hibernate-validator-tck-runner Hibernate Validator TCK Runner - Aggregates dependencies and runs the JSR-380 TCK + Aggregates dependencies and runs the Jakarta Bean Validation TCK ${project.build.directory}/dependency/beanvalidation-tck-tests-suite.xml - ${project.build.directory}/wildfly-${wildfly-tck.version} + ${project.build.directory}/wildfly-${version.wildfly.tck} org.hibernate.validator.HibernateValidator @@ -31,24 +31,20 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api ${project.groupId} hibernate-validator - - ${project.groupId} - hibernate-validator-cdi - org.glassfish - javax.el + jakarta.el - org.hibernate.javax.persistence - hibernate-jpa-2.1-api + jakarta.persistence + jakarta.persistence-api test @@ -58,7 +54,7 @@ org.hibernate.beanvalidation.tck beanvalidation-tck-tests - ${tck.version} + ${version.org.hibernate.beanvalidation.tck} org.jboss.test-audit @@ -105,6 +101,27 @@ 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 +156,6 @@ ${validation.provider} - methods - 4 @@ -176,15 +191,15 @@ LocalSecurityManagerTesting - -DincludeJavaFXTests=true -Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/test.policy + -DincludeJavaFXTests=true -Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/test.policy - + org.hibernate.beanvalidation.tck beanvalidation-standalone-container-adapter - ${tck.version} + ${version.org.hibernate.beanvalidation.tck} @@ -227,7 +242,7 @@ - -DincludeJavaFXTests=true -Xmx1024m -Djava.util.logging.manager=org.jboss.logmanager.LogManager + -DincludeJavaFXTests=true -Xmx1024m -Djava.util.logging.manager=org.jboss.logmanager.LogManager Servlet 3.0 @@ -236,6 +251,13 @@ wildfly-arquillian-container-managed test + + ${project.groupId} + hibernate-validator-modules + ${project.version} + wildfly-${version.wildfly.tck}-patch + zip + @@ -253,7 +275,7 @@ org.wildfly wildfly-dist - ${wildfly-tck.version} + ${version.wildfly.tck} tar.gz false ${project.build.directory} @@ -273,7 +295,7 @@ ${project.groupId} hibernate-validator-modules ${project.version} - wildfly-${wildfly-tck.version}-patch + wildfly-${version.wildfly.tck}-patch zip ${project.build.directory} @@ -296,7 +318,7 @@ true false - patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${wildfly-tck.version}-patch.zip + patch apply ${project.build.directory}/hibernate-validator-modules-${project.version}-wildfly-${version.wildfly.tck}-patch.zip @@ -305,6 +327,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 +418,7 @@ ${wildfly.target-dir}/modules/system/layers/base/ - src/test/modules + src/test/modules/jdk8 true @@ -327,45 +426,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 + + + + linux + + + + macosx + + + mac os x + + + + mac + + + + windows + + + windows + - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED + win diff --git a/tck-runner/src/script/updateStandaloneXml.groovy b/tck-runner/src/script/updateStandaloneXml.groovy index 258f1527f0..766bdff874 100644 --- a/tck-runner/src/script/updateStandaloneXml.groovy +++ b/tck-runner/src/script/updateStandaloneXml.groovy @@ -18,7 +18,7 @@ standaloneXml = new File( getPropertyValue('wildfly.target-dir'), 'standalone/co println "[INFO] Add javafx.api as global module" processFileInplace( standaloneXml ) { text -> - text.replaceAll( //, '\n \n \n ' ) + text.replaceAll( //, '\n \n \n ' ) } println "[INFO] ------------------------------------------------------------------------"; 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..5f9136f89d 100644 --- a/tck-runner/src/test/resources/test.policy +++ b/tck-runner/src/test/resources/test.policy @@ -27,11 +27,10 @@ grant codeBase "file:${localRepository}/org/hibernate/validator/hibernate-valida permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.RuntimePermission "setContextClassLoader"; + permission java.util.PropertyPermission "org.hibernate.validator.force-disable-javafx-integration", "read"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; - // JAXB - permission java.util.PropertyPermission "mapAnyUriToUri", "read"; }; // Used during aggregator builds also building "engine", e.g. mvn clean install -pl tck-runner -am @@ -40,11 +39,10 @@ grant codeBase "file:${basedir}/../engine/target/hibernate-validator-${project.v permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.RuntimePermission "setContextClassLoader"; + permission java.util.PropertyPermission "org.hibernate.validator.force-disable-javafx-integration", "read"; permission org.hibernate.validator.HibernateValidatorPermission "accessPrivateMembers"; - // JAXB - permission java.util.PropertyPermission "mapAnyUriToUri", "read"; }; grant codeBase "file:${localRepository}/com/fasterxml/classmate/-" { @@ -56,11 +54,11 @@ grant codeBase "file:${localRepository}/org/jboss/logging/jboss-logging/-" { permission java.util.PropertyPermission "org.jboss.logging.locale", "read"; }; -/* =================== */ -/* Bean Validation API */ -/* =================== */ +/* =========================== */ +/* Jakarta Bean Validation API */ +/* =========================== */ -grant codeBase "file:${localRepository}/javax/validation/validation-api/-" { +grant codeBase "file:${localRepository}/jakarta/validation/jakarta.validation-api/-" { permission java.io.FilePermission "<>", "read"; // in some tests this property is accessed by the TCK when the API JAR is on the callstack; the TCK doesn't @@ -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.io.FilePermission "${localRepository}/org/hibernate/beanvalidation/tck/beanvalidation-tck-tests/${version.org.hibernate.beanvalidation.tck}/beanvalidation-tck-tests-${version.org.hibernate.beanvalidation.tck}.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..c8bd4c1760 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.2.0.Final hibernate-validator-test-utils @@ -54,12 +54,12 @@ - javax.validation - validation-api + jakarta.validation + jakarta.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..cf234f1bc7 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; @@ -262,6 +263,21 @@ public ConstraintViolationSetAssert describedAs(String description, Object... ar public void containsOnlyViolations(ViolationExpectation... expectedViolations) { isNotNull(); + List actualViolations = getActualViolationExpectations( expectedViolations ); + + Assertions.assertThat( actualViolations ).containsExactlyInAnyOrder( expectedViolations ); + } + + public void containsOneOfViolations(ViolationExpectation... expectedViolations) { + isNotNull(); + + List actualViolations = getActualViolationExpectations( expectedViolations ); + + Assertions.assertThat( actualViolations ).hasSize( 1 ); + Assertions.assertThat( expectedViolations ).contains( actualViolations.get( 0 ) ); + } + + private List getActualViolationExpectations(ViolationExpectation[] expectedViolations) { List actualViolations = new ArrayList<>(); ViolationExpectationPropertiesToTest referencePropertiesToTest; @@ -272,7 +288,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 ) ); } } @@ -282,7 +299,7 @@ public void containsOnlyViolations(ViolationExpectation... expectedViolations) { actualViolations.add( new ViolationExpectation( violation, referencePropertiesToTest ) ); } - Assertions.assertThat( actualViolations ).containsExactlyInAnyOrder( expectedViolations ); + return actualViolations; } public void containsOnlyPaths(PathExpectation... paths) { @@ -309,7 +326,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" ); - } -} - -