diff --git a/performance/pom.xml b/performance/pom.xml
index 32f38b780c..707797d8a8 100644
--- a/performance/pom.xml
+++ b/performance/pom.xml
@@ -270,8 +270,7 @@
2.0.2.Final
Hibernate Validator
6.2.5.Final
- ${project.basedir}/src/main/javax
- ${project.basedir}/src/main/javax-bv2
+ ${project.basedir}/src/main/javax-bv2
${project.basedir}/src/main/javax-predefined-scope
@@ -308,8 +307,7 @@
2.0.1.Final
Hibernate Validator
6.1.7.Final
- ${project.basedir}/src/main/javax
- ${project.basedir}/src/main/javax-bv2
+ ${project.basedir}/src/main/javax-bv2
@@ -345,8 +343,7 @@
2.0.1.Final
Hibernate Validator
6.0.23.Final
- ${project.basedir}/src/main/javax
- ${project.basedir}/src/main/javax-bv2
+ ${project.basedir}/src/main/javax-bv2
diff --git a/performance/src/main/jakarta/org/hibernate/validator/performance/simple/SimpleSingleElementValidation.java b/performance/src/main/jakarta/org/hibernate/validator/performance/simple/SimpleSingleElementValidation.java
index c5e4e313e6..45509018f4 100644
--- a/performance/src/main/jakarta/org/hibernate/validator/performance/simple/SimpleSingleElementValidation.java
+++ b/performance/src/main/jakarta/org/hibernate/validator/performance/simple/SimpleSingleElementValidation.java
@@ -56,11 +56,31 @@ public User(String name, String email, int age) {
}
}
+ private static class UserNoInterpolation {
+ @NotNull(message = "must be not null")
+ @Size(min = 3, max = 50, message = "size must be between 3 and 50")
+ private String name;
+
+ @Size(min = 3, max = 50, message = "size must be between 3 and 50")
+ @NotNull(message = "must be not null")
+ private String email;
+
+ @Min(value = 20, message = "must be at least 20")
+ private int age;
+
+ public UserNoInterpolation(String name, String email, int age) {
+ this.name = name;
+ this.email = email;
+ this.age = age;
+ }
+ }
+
@State(Scope.Benchmark)
public static class BenchmarkState {
Validator validator;
User validUser;
User invalidUser;
+ UserNoInterpolation invalidUserNoInterpolation;
@Setup
public void setup() {
@@ -68,6 +88,7 @@ public void setup() {
this.validator = factory.getValidator();
this.validUser = new User( "John Doe", "john.doe@example.com", 25 );
this.invalidUser = new User( "Jo", "invalid-email", 19 );
+ this.invalidUserNoInterpolation = new UserNoInterpolation( "Jo", "invalid-email", 19 );
}
}
@@ -82,4 +103,10 @@ public void invalidObjectValidation(BenchmarkState state, Blackhole blackhole) {
Set> violations = state.validator.validate( state.invalidUser );
blackhole.consume( violations );
}
+
+ @Benchmark
+ public void invalidObjectNoInterpolationValidation(BenchmarkState state, Blackhole blackhole) {
+ Set> violations = state.validator.validate( state.invalidUserNoInterpolation );
+ blackhole.consume( violations );
+ }
}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedValidation.java
new file mode 100644
index 0000000000..c54754dae2
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedValidation.java
@@ -0,0 +1,88 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.validator.performance.cascaded;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+
+import 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 CascadedValidation {
+
+ @State(Scope.Benchmark)
+ public static class CascadedValidationState {
+ public volatile Validator validator;
+ public volatile Person person;
+
+ public CascadedValidationState() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ validator = factory.getValidator();
+
+ // TODO graphs needs to be generated and deeper
+ Person kermit = new Person( "kermit" );
+ Person piggy = new Person( "miss piggy" );
+ Person gonzo = new Person( "gonzo" );
+
+ kermit.addFriend( piggy ).addFriend( gonzo );
+ piggy.addFriend( kermit ).addFriend( gonzo );
+ gonzo.addFriend( kermit ).addFriend( piggy );
+
+ person = kermit;
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.MILLISECONDS)
+ @Fork(value = 1)
+ @Threads(50)
+ @Warmup(iterations = 10)
+ @Measurement(iterations = 20)
+ public void testCascadedValidation(CascadedValidationState state, Blackhole bh) {
+ Set> violations = state.validator.validate( state.person );
+ assertThat( violations ).hasSize( 0 );
+
+ bh.consume( violations );
+ }
+
+ public static class Person {
+ @NotNull
+ String name;
+
+ Set<@Valid Person> friends = new HashSet<>();
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public Person addFriend(Person friend) {
+ friends.add( friend );
+ return this;
+ }
+ }
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndCyclesValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndCyclesValidation.java
new file mode 100644
index 0000000000..8d094e3288
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndCyclesValidation.java
@@ -0,0 +1,102 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.validator.performance.cascaded;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import 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;
+
+public class CascadedWithLotsOfItemsAndCyclesValidation {
+
+ private static final int NUMBER_OF_ARTICLES_PER_SHOP = 2000;
+
+ @State(Scope.Benchmark)
+ public static class CascadedWithLotsOfItemsValidationState {
+ public volatile Validator validator;
+
+ public volatile Shop shop;
+
+ public CascadedWithLotsOfItemsValidationState() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ validator = factory.getValidator();
+
+ shop = createShop();
+ }
+
+ private Shop createShop() {
+ Shop shop = new Shop( 1 );
+
+ for ( int i = 0; i < NUMBER_OF_ARTICLES_PER_SHOP; i++ ) {
+ shop.addArticle( new Article( i ) );
+ }
+
+ return shop;
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.SECONDS)
+ @Fork(value = 1)
+ @Threads(20)
+ @Warmup(iterations = 10)
+ @Measurement(iterations = 20)
+ public void testCascadedValidationWithLotsOfItems(CascadedWithLotsOfItemsValidationState state, Blackhole bh) {
+ Set> violations = state.validator.validate( state.shop );
+ assertThat( violations ).hasSize( 0 );
+
+ bh.consume( violations );
+ }
+
+ public static class Shop {
+ @NotNull
+ private Integer id;
+
+ @NotNull
+ private List<@Valid @NotNull Article> articles = new ArrayList<>();
+
+ public Shop(Integer id) {
+ this.id = id;
+ }
+
+ public void addArticle(Article article) {
+ articles.add( article );
+ article.shops.add( this );
+ }
+ }
+
+ public static class Article {
+ @NotNull
+ private Integer id;
+
+ private Set<@Valid Shop> shops = new HashSet<>();
+
+ public Article(Integer id) {
+ this.id = id;
+ }
+ }
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java
new file mode 100644
index 0000000000..22f19de9f2
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java
@@ -0,0 +1,111 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.validator.performance.cascaded;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import org.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 CascadedWithLotsOfItemsAndMoreConstraintsValidation {
+
+ private static final int NUMBER_OF_ARTICLES_PER_SHOP = 2000;
+
+ @State(Scope.Benchmark)
+ public static class CascadedWithLotsOfItemsValidationState {
+ public volatile Validator validator;
+
+ public volatile Shop shop;
+
+ public CascadedWithLotsOfItemsValidationState() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ validator = factory.getValidator();
+
+ shop = createShop();
+ }
+
+ private Shop createShop() {
+ Shop shop = new Shop( 1, "Shop" );
+
+ for ( int i = 0; i < NUMBER_OF_ARTICLES_PER_SHOP; i++ ) {
+ shop.addArticle( new Article( i, "Article " + i ) );
+ }
+
+ return shop;
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.SECONDS)
+ @Fork(value = 1)
+ @Threads(20)
+ @Warmup(iterations = 10)
+ @Measurement(iterations = 20)
+ public void testCascadedValidationWithLotsOfItems(CascadedWithLotsOfItemsValidationState state, Blackhole bh) {
+ Set> violations = state.validator.validate( state.shop );
+ assertThat( violations ).hasSize( 0 );
+
+ bh.consume( violations );
+ }
+
+ public static class Shop {
+ @NotNull
+ private Integer id;
+
+ @Size(min = 1)
+ private String name;
+
+ @NotNull
+ private List<@Valid @NotNull Article> articles = new ArrayList<>();
+
+ public Shop(Integer id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public void addArticle(Article article) {
+ articles.add( article );
+ }
+ }
+
+ public static class Article {
+ @NotNull
+ private Integer id;
+
+ @Size(min = 1)
+ private String name;
+
+ public Article(Integer id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+ }
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java
new file mode 100644
index 0000000000..59e40bfce0
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java
@@ -0,0 +1,102 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.validator.performance.cascaded;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+
+import 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 CascadedWithLotsOfItemsValidation {
+
+ private static final int NUMBER_OF_ARTICLES_PER_SHOP = 2000;
+
+ @State(Scope.Benchmark)
+ public static class CascadedWithLotsOfItemsValidationState {
+ public volatile Validator validator;
+
+ public volatile Shop shop;
+
+ public CascadedWithLotsOfItemsValidationState() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ validator = factory.getValidator();
+
+ shop = createShop();
+ }
+
+ private Shop createShop() {
+ Shop shop = new Shop( 1 );
+
+ for ( int i = 0; i < NUMBER_OF_ARTICLES_PER_SHOP; i++ ) {
+ shop.addArticle( new Article( i ) );
+ }
+
+ return shop;
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.SECONDS)
+ @Fork(value = 1)
+ @Threads(20)
+ @Warmup(iterations = 10)
+ @Measurement(iterations = 20)
+ public void testCascadedValidationWithLotsOfItems(CascadedWithLotsOfItemsValidationState state, Blackhole bh) {
+ Set> violations = state.validator.validate( state.shop );
+ assertThat( violations ).hasSize( 0 );
+
+ bh.consume( violations );
+ }
+
+ public static class Shop {
+ @NotNull
+ private Integer id;
+
+ @NotNull
+ private List<@Valid @NotNull Article> articles = new ArrayList<>();
+
+ public Shop(Integer id) {
+ this.id = id;
+ }
+
+ public void addArticle(Article article) {
+ articles.add( article );
+ }
+ }
+
+ public static class Article {
+ @NotNull
+ private Integer id;
+
+ public Article(Integer id) {
+ this.id = id;
+ }
+ }
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/ExecutableValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/ExecutableValidation.java
new file mode 100644
index 0000000000..3ddb98c4ec
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/ExecutableValidation.java
@@ -0,0 +1,156 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+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/javax-bv2/org/hibernate/validator/performance/simple/SimpleSingleElementValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/SimpleSingleElementValidation.java
new file mode 100644
index 0000000000..b24b4bd7bc
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/SimpleSingleElementValidation.java
@@ -0,0 +1,112 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.validator.performance.simple;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Min;
+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.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+
+@State(Scope.Benchmark)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Fork(value = 1)
+@Threads(50)
+@Warmup(iterations = 10)
+@Measurement(iterations = 20)
+public class SimpleSingleElementValidation {
+
+ // The class to be validated
+ private static class User {
+ @NotNull
+ @Size(min = 3, max = 50)
+ private String name;
+
+ @Size(min = 3, max = 50)
+ @NotNull
+ private String email;
+
+ @Min(value = 20)
+ private int age;
+
+ public User(String name, String email, int age) {
+ this.name = name;
+ this.email = email;
+ this.age = age;
+ }
+ }
+
+ private static class UserNoInterpolation {
+ @NotNull(message = "must be not null")
+ @Size(min = 3, max = 50, message = "size must be between 3 and 50")
+ private String name;
+
+ @Size(min = 3, max = 50, message = "size must be between 3 and 50")
+ @NotNull(message = "must be not null")
+ private String email;
+
+ @Min(value = 20, message = "must be at least 20")
+ private int age;
+
+ public UserNoInterpolation(String name, String email, int age) {
+ this.name = name;
+ this.email = email;
+ this.age = age;
+ }
+ }
+
+ @State(Scope.Benchmark)
+ public static class BenchmarkState {
+ Validator validator;
+ User validUser;
+ User invalidUser;
+ UserNoInterpolation invalidUserNoInterpolation;
+
+ @Setup
+ public void setup() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ this.validator = factory.getValidator();
+ this.validUser = new User( "John Doe", "john.doe@example.com", 25 );
+ this.invalidUser = new User( "Jo", "invalid-email", 19 );
+ this.invalidUserNoInterpolation = new UserNoInterpolation( "Jo", "invalid-email", 19 );
+ }
+ }
+
+ @Benchmark
+ public void validObjectValidation(BenchmarkState state, Blackhole blackhole) {
+ Set> violations = state.validator.validate( state.validUser );
+ blackhole.consume( violations );
+ }
+
+ @Benchmark
+ public void invalidObjectValidation(BenchmarkState state, Blackhole blackhole) {
+ Set> violations = state.validator.validate( state.invalidUser );
+ blackhole.consume( violations );
+ }
+
+ @Benchmark
+ public void invalidObjectNoInterpolationValidation(BenchmarkState state, Blackhole blackhole) {
+ Set> violations = state.validator.validate( state.invalidUserNoInterpolation );
+ blackhole.consume( violations );
+ }
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/SimpleValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/SimpleValidation.java
new file mode 100644
index 0000000000..5277fa3458
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/simple/SimpleValidation.java
@@ -0,0 +1,150 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+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.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.AssertTrue;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+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 SimpleValidation {
+
+ 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;
+ public 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 testSimpleBeanValidation(ValidationState state, Blackhole bh) {
+ Driver driver = state.nextDriver();
+ Set> violations = state.validator.validate( driver );
+ assert driver.getExpectedViolationCount() == violations.size();
+ bh.consume( violations );
+ }
+
+ public static class Driver {
+ @NotNull
+ private String name;
+
+ @Min(18)
+ private int age;
+
+ @AssertTrue
+ 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/javax-bv2/org/hibernate/validator/performance/statistical/StatisticalConstraintValidator.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/StatisticalConstraintValidator.java
new file mode 100644
index 0000000000..13d0a1fd3a
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/StatisticalConstraintValidator.java
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.validator.performance.statistical;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class StatisticalConstraintValidator implements ConstraintValidator {
+ private static final float FAILURE_RATE = 0.25f;
+
+ public static final ThreadLocal threadLocalCounter = new ThreadLocal() {
+ @Override
+ protected Counter initialValue() {
+ return new Counter();
+ }
+ };
+
+ @Override
+ public void initialize(Annotation constraintAnnotation) {
+ }
+
+ @Override
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
+ return threadLocalCounter.get().incrementCount();
+ }
+
+ public static class Counter {
+ private int totalCount = 0;
+ private int failures = 0;
+
+ public int getFailures() {
+ return failures;
+ }
+
+ public boolean incrementCount() {
+ totalCount++;
+ if ( totalCount * FAILURE_RATE > failures ) {
+ failures++;
+ return false;
+ }
+ return true;
+ }
+
+ public void reset() {
+ totalCount = 0;
+ failures = 0;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append( "Counter" );
+ sb.append( "{totalCount=" ).append( totalCount );
+ sb.append( ", failures=" ).append( failures );
+ sb.append( '}' );
+ return sb.toString();
+ }
+ }
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/StatisticalValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/StatisticalValidation.java
new file mode 100644
index 0000000000..04e9fd22f5
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/StatisticalValidation.java
@@ -0,0 +1,85 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.validator.performance.statistical;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import javax.validation.Configuration;
+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 Hardy Ferentschik
+ */
+public class StatisticalValidation {
+ private static final int NUMBER_OF_TEST_ENTITIES = 100;
+
+
+ @State(Scope.Benchmark)
+ public static class StatisticalValidationState {
+ private volatile Validator validator;
+ private volatile List entitiesUnderTest;
+
+ public StatisticalValidationState() {
+ ValidatorFactory factory = null;
+ final Configuration> configuration = Validation.byDefaultProvider().configure();
+ try ( InputStream mappingStream = StatisticalValidation.class.getResourceAsStream( "mapping.xml" ) ) {
+ configuration.addMapping( mappingStream );
+ factory = configuration.buildValidatorFactory();
+ assertThat( factory ).isNotNull();
+ }
+ catch (IOException e) {
+ throw new IllegalStateException( "Mappings cannot be read. Validation factory cannot be configured correctly.", e );
+ }
+
+ validator = factory.getValidator();
+
+ entitiesUnderTest = IntStream.rangeClosed( 0, NUMBER_OF_TEST_ENTITIES )
+ .mapToObj( index -> new TestEntity( index % 10 ) )
+ .collect( Collectors.toList() );
+ }
+
+ }
+
+ @Benchmark
+ @BenchmarkMode(Mode.Throughput)
+ @OutputTimeUnit(TimeUnit.MILLISECONDS)
+ @Fork(value = 1)
+ @Threads(100)
+ @Warmup(iterations = 10)
+ @Measurement(iterations = 10)
+ public void testValidationWithStatisticalGraphDepthAndConstraintValidator(StatisticalValidationState state, Blackhole bh) throws Exception {
+ state.entitiesUnderTest.forEach(
+ testEntity -> {
+ Set> violations = state.validator.validate( testEntity );
+ assertThat( violations ).hasSize( StatisticalConstraintValidator.threadLocalCounter.get().getFailures() );
+ bh.consume( violations );
+ StatisticalConstraintValidator.threadLocalCounter.get().reset();
+ }
+ );
+ }
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/TestEntity.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/TestEntity.java
new file mode 100644
index 0000000000..773541d3ad
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/statistical/TestEntity.java
@@ -0,0 +1,81 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+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;
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.Future;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Null;
+import javax.validation.constraints.Past;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class TestEntity {
+ public static final int MAX_DEPTH = 10;
+ private static final Calendar cal = GregorianCalendar.getInstance( TimeZone.getTimeZone( ZoneId.of( "GMT" ) ), Locale.ROOT );
+
+ public TestEntity(int depth) {
+ if ( depth <= MAX_DEPTH ) {
+ depth++;
+ testEntity = new TestEntity( depth );
+ }
+ }
+
+ // it is not really necessary to initialize the values
+ @Null
+ private String value1 = null;
+
+ @NotNull
+ private String value2 = "";
+
+ @Size
+ private String value3 = "";
+
+ @Past
+ private Date value4 = cal.getTime();
+
+ @Future
+ private Date value5 = cal.getTime();
+
+ @Pattern(regexp = ".*")
+ private String value6;
+
+ @Min(0)
+ private Integer value7 = 0;
+
+ @Max(100)
+ private Integer value8 = 0;
+
+ @DecimalMin("1.0")
+ private BigDecimal value9 = new BigDecimal( "1.0" );
+
+ @DecimalMin("1.0")
+ private BigDecimal value10 = new BigDecimal( "1.0" );
+
+ @AssertFalse
+ private boolean value11;
+
+ @AssertTrue
+ private boolean value12;
+
+ @Valid
+ private TestEntity testEntity;
+}
diff --git a/performance/src/main/javax-bv2/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java b/performance/src/main/javax-bv2/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java
new file mode 100644
index 0000000000..229ed05d69
--- /dev/null
+++ b/performance/src/main/javax-bv2/org/hibernate/validator/performance/unconstrained/UnconstrainedBeanValidation.java
@@ -0,0 +1,125 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+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;
+ }
+ }
+}