diff --git a/engine/src/main/java/org/hibernate/validator/internal/Foo.java b/engine/src/main/java/org/hibernate/validator/internal/Foo.java new file mode 100644 index 0000000000..6014b858f3 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/Foo.java @@ -0,0 +1,32 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * @author Marko Bekhta + */ +public class Foo { + + @NotNull + @NotBlank + @Size(min = 5) + private String string; + + public Foo(String string) { + this.string = string; + } + + @AssertTrue + public boolean isTrue() { + return false; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java index 429d7d711d..d3c85a9865 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/ConstraintLocation.java @@ -40,11 +40,11 @@ static ConstraintLocation forClass(Class declaringClass) { } static ConstraintLocation forField(Field field) { - return new FieldConstraintLocation( field ); + return new FooFieldConstraintLocation(); } static ConstraintLocation forGetter(Method getter) { - return new GetterConstraintLocation( getter ); + return new FooGetterConstraintLocation(); } static ConstraintLocation forTypeArgument(ConstraintLocation delegate, TypeVariable typeParameter, Type typeOfAnnotatedElement) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FooFieldConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FooFieldConstraintLocation.java new file mode 100644 index 0000000000..9013470acb --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FooFieldConstraintLocation.java @@ -0,0 +1,126 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.metadata.location; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.Foo; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.internal.util.StringHelper; + +/** + * Field constraint location. + * + * @author Hardy Ferentschik + * @author Gunnar Morling + */ +public class FooFieldConstraintLocation implements ConstraintLocation { + + /** + * The member the constraint was defined on. + */ + private final MethodHandle property; + private final Field string; + + + /** + * The property name associated with the member. + */ + private final String propertyName; + + /** + * The type to be used for validator resolution for constraints at this location. + */ + private final Type typeForValidatorResolution; + + FooFieldConstraintLocation() { + this.propertyName = "string"; + this.typeForValidatorResolution = String.class; + try { + string = Foo.class.getDeclaredField( "string" ); + string.setAccessible( true ); + property = MethodHandles.lookup().unreflectGetter( string ); + } + catch (IllegalAccessException | NoSuchFieldException e) { + throw new IllegalStateException( e ); + } + } + + @Override + public Class getDeclaringClass() { + return Foo.class; + } + + @Override + public Member getMember() { + return string; + } + + public String getPropertyName() { + return propertyName; + } + + @Override + public Type getTypeForValidatorResolution() { + return typeForValidatorResolution; + } + + @Override + public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { + path.addPropertyNode( propertyName ); + } + + @Override + public Object getValue(Object parent) { + try { + return property.invoke( parent ); + } + catch (Throwable throwable) { + throw new IllegalStateException( throwable ); + } + } + + @Override + public String toString() { + return "FieldConstraintLocation [member=" + StringHelper.toShortString( string ) + ", typeForValidatorResolution=" + + StringHelper.toShortString( typeForValidatorResolution ) + "]"; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + FooFieldConstraintLocation that = (FooFieldConstraintLocation) o; + + if ( string != null ? !string.equals( that.string ) : that.string != null ) { + return false; + } + if ( !typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = string != null ? string.hashCode() : 0; + result = 31 * result + typeForValidatorResolution.hashCode(); + return result; + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FooGetterConstraintLocation.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FooGetterConstraintLocation.java new file mode 100644 index 0000000000..d63f63ee42 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/location/FooGetterConstraintLocation.java @@ -0,0 +1,125 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.metadata.location; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import org.hibernate.validator.internal.Foo; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; +import org.hibernate.validator.internal.util.ReflectionHelper; +import org.hibernate.validator.internal.util.StringHelper; + +/** + * Getter method constraint location. + * + * @author Hardy Ferentschik + * @author Gunnar Morling + */ +public class FooGetterConstraintLocation implements ConstraintLocation { + + /** + * The method the constraint was defined on. + */ + private final MethodHandle property; + private final Method method; + + /** + * The property name associated with the method. + */ + private final String propertyName; + + /** + * The type to be used for validator resolution for constraints at this location. + */ + private final Type typeForValidatorResolution; + + FooGetterConstraintLocation() { + try { + method = Foo.class.getMethod( "isTrue" ); + method.setAccessible( true ); + property = MethodHandles.lookup().unreflect( method ); + } + catch (IllegalAccessException | NoSuchMethodException e) { + throw new IllegalStateException( e ); + } + this.propertyName = ReflectionHelper.getPropertyName( method ); + this.typeForValidatorResolution = ReflectionHelper.boxedType( ReflectionHelper.typeOf( method ) ); + } + + @Override + public Class getDeclaringClass() { + return method.getDeclaringClass(); + } + + @Override + public Method getMember() { + return method; + } + + public String getPropertyName() { + return propertyName; + } + + @Override + public Type getTypeForValidatorResolution() { + return typeForValidatorResolution; + } + + @Override + public void appendTo(ExecutableParameterNameProvider parameterNameProvider, PathImpl path) { + path.addPropertyNode( propertyName ); + } + + @Override + public Object getValue(Object parent) { + try { + return property.invoke( parent ); + } + catch (Throwable throwable) { + throw new IllegalStateException( throwable ); + } + } + + @Override + public String toString() { + return "GetterConstraintLocation [method=" + StringHelper.toShortString( method ) + ", typeForValidatorResolution=" + + StringHelper.toShortString( typeForValidatorResolution ) + "]"; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + FooGetterConstraintLocation that = (FooGetterConstraintLocation) o; + + if ( method != null ? !method.equals( that.method ) : that.method != null ) { + return false; + } + if ( !typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = method.hashCode(); + result = 31 * result + typeForValidatorResolution.hashCode(); + return result; + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/FooTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/FooTest.java new file mode 100644 index 0000000000..f96cd1d37c --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/FooTest.java @@ -0,0 +1,36 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.constraints.annotations; + +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.constraints.AssertTrue; +import javax.validation.constraints.Size; + +import org.hibernate.validator.internal.Foo; + +import org.testng.annotations.Test; + +/** + * @author Marko Bekhta + */ +public class FooTest extends AbstractConstrainedTest { + + @Test + public void testName() throws Exception { + Foo foo = new Foo( "ab" ); + Set> violations = validator.validate( foo ); + assertThat( violations ).containsOnlyViolations( + violationOf( Size.class ), + violationOf( AssertTrue.class ) + ); + } +} diff --git a/performance/pom.xml b/performance/pom.xml index 9f35c61b79..6e72576088 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -57,6 +57,11 @@ validation-api provided + + org.hibernate.validator + hibernate-validator + ${project.version} + diff --git a/performance/src/main/java/org/hibernate/validator/performance/simple/FooValidation.java b/performance/src/main/java/org/hibernate/validator/performance/simple/FooValidation.java new file mode 100644 index 0000000000..a8a7bf195f --- /dev/null +++ b/performance/src/main/java/org/hibernate/validator/performance/simple/FooValidation.java @@ -0,0 +1,77 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.performance.simple; + +import java.util.Random; +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 org.hibernate.validator.internal.Foo; + +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 FooValidation { + + 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 Random random; + + { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + random = new Random(); + } + + } + + @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) { + Foo foo = new Foo( names[state.random.nextInt( names.length )] ); + Set> violations = state.validator.validate( foo ); + bh.consume( violations ); + } + +}