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; + } + } +}