@@ -0,0 +1,79 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.validator.performance.simple;

import java.util.HashSet;
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.NotNull;

import org.junit.Before;
import org.junit.Test;

import static junit.framework.Assert.assertEquals;

/**
* @author Hardy Ferentschik
*/
public class CascadedValidationTest {
private ValidatorFactory factory;
private Validator validator;

@Before
public void setUp() {
factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}

@Test
public void testCascadedValidation() {
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 );

Set<ConstraintViolation<Person>> violations = validator.validate( kermit );
assertEquals( 0, violations.size() );
}

public class Person {
@NotNull
String name;

@Valid
Set<Person> friends = new HashSet<Person>();

public Person(String name) {
this.name = name;
}

public Person addFriend(Person friend) {
friends.add( friend );
return this;
}
}
}



@@ -17,31 +17,59 @@
package org.hibernate.validator.performance.statistical;

import java.lang.annotation.Annotation;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
* @author Hardy Ferentschik
*/
public class StatisticalConstraintValidator implements ConstraintValidator<Annotation, Object> {
private final Random randomGenerator = new Random();
public static final ThreadLocal<AtomicInteger> threadLocalCounter = new ThreadLocal<AtomicInteger>() {
protected AtomicInteger initialValue() {
return new AtomicInteger( 0 );
private static final float FAILURE_RATE = 0.25f;

public static final ThreadLocal<Counter> threadLocalCounter = new ThreadLocal<Counter>() {
protected Counter initialValue() {
return new Counter();
}
};

public void initialize(Annotation constraintAnnotation) {
}

public boolean isValid(Object value, ConstraintValidatorContext context) {
int random = randomGenerator.nextInt( 2 );
if ( random == 0 ) {
threadLocalCounter.get().incrementAndGet();
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();
}
return random != 0;
}
}

@@ -17,7 +17,6 @@
package org.hibernate.validator.performance.statistical;

import java.io.InputStream;
import java.util.Random;
import java.util.Set;
import javax.validation.Configuration;
import javax.validation.ConstraintViolation;
@@ -38,7 +37,6 @@
* @author Hardy Ferentschik
*/
public class StatisticalValidationTest {
private static final Random random = new Random();
private static final int NUMBER_OF_TEST_ENTITIES = 100;

private ValidatorFactory factory;
@@ -63,16 +61,16 @@ public void setUp() throws Exception {
validator = factory.getValidator();

for ( int i = 0; i < NUMBER_OF_TEST_ENTITIES; i++ ) {
entitiesUnderTest[i] = new TestEntity( random, 0 );
entitiesUnderTest[i] = new TestEntity( i % 10 );
}
}

@Test
public void testValidationWithStatisticalGraphDepthAndConstraintValidator() throws Exception {
for ( int i = 0; i < NUMBER_OF_TEST_ENTITIES; i++ ) {
Set<ConstraintViolation<TestEntity>> violations = validator.validate( entitiesUnderTest[i] );
assertEquals( StatisticalConstraintValidator.threadLocalCounter.get().get(), violations.size() );
StatisticalConstraintValidator.threadLocalCounter.get().getAndSet( 0 );
assertEquals( StatisticalConstraintValidator.threadLocalCounter.get().getFailures(), violations.size() );
StatisticalConstraintValidator.threadLocalCounter.get().reset();
}
}
}
@@ -16,53 +16,73 @@
*/
package org.hibernate.validator.performance.statistical;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.GregorianCalendar;
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 = 32;
public static final int MAX_DEPTH = 10;
private static final Calendar cal = GregorianCalendar.getInstance();

public TestEntity(Random random, int depth) {
public TestEntity(int depth) {
if ( depth <= MAX_DEPTH ) {
int randomNumber = random.nextInt( 2 );
if ( randomNumber == 1 ) {
depth++;
testEntity = new TestEntity( random, depth );
}
depth++;
testEntity = new TestEntity( depth );
}
}

@Size
private String value1;
// it is not really necessary to initialise the values
@Null
private String value1 = null;

@Min(0)
private Integer value2;
@NotNull
private String value2 = "";

@Max(100)
private Integer value3;
@Size
private String value3 = "";

@Past
private Date value4;
private Date value4 = cal.getTime();

@Future
private Date value5;
private Date value5 = cal.getTime();

@Null
@Pattern(regexp = ".*")
private String value6;

@NotNull
private String value7;
@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;
@@ -6,7 +6,7 @@ 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.File=hibernate-validator.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

@@ -18,7 +18,7 @@ log4j.appender.socket.locationInfo=true


### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
log4j.rootLogger=debug, stdout,file

log4j.logger.org.hibernate.validator.internal.engine.ValidatorImpl=trace
#log4j.logger.org.hibernate.validator.internal.engine.resolver.JPATraversableResolver=trace
@@ -37,4 +37,29 @@
<value>org.hibernate.validator.performance.statistical.StatisticalConstraintValidator</value>
</validated-by>
</constraint-definition>
<constraint-definition annotation="javax.validation.constraints.DecimalMin">
<validated-by include-existing-validators="false">
<value>org.hibernate.validator.performance.statistical.StatisticalConstraintValidator</value>
</validated-by>
</constraint-definition>
<constraint-definition annotation="javax.validation.constraints.DecimalMax">
<validated-by include-existing-validators="false">
<value>org.hibernate.validator.performance.statistical.StatisticalConstraintValidator</value>
</validated-by>
</constraint-definition>
<constraint-definition annotation="javax.validation.constraints.AssertTrue">
<validated-by include-existing-validators="false">
<value>org.hibernate.validator.performance.statistical.StatisticalConstraintValidator</value>
</validated-by>
</constraint-definition>
<constraint-definition annotation="javax.validation.constraints.AssertFalse">
<validated-by include-existing-validators="false">
<value>org.hibernate.validator.performance.statistical.StatisticalConstraintValidator</value>
</validated-by>
</constraint-definition>
<constraint-definition annotation="javax.validation.constraints.Pattern">
<validated-by include-existing-validators="false">
<value>org.hibernate.validator.performance.statistical.StatisticalConstraintValidator</value>
</validated-by>
</constraint-definition>
</constraint-mappings>