Issue Type: Bug Bug
Affects Versions: 5.0.0.Alpha1
Assignee: Unassigned
Components: engine
Created: 17/Dec/12 6:03 AM
Description:

Heap analysis of my webapp showed that I have several thousand live instances of ConstraintValidatorManager$CacheKey, with the numbers growing with every subsequent request. This was surprising given that the application had only 3 constraints configured (via XML).

Hooking up the debugger, I could see that the cache lookup on line 92 of ConstraintValidatorManager always fails - even though the validator is already cached, a new instance is initialized and returned.

ConstraintValidatorManager.java
ConstraintValidator<A, V> constraintValidator = (ConstraintValidator<A, V>) constraintValidatorCache.get( key );

I set a breakpoint on the equals method of CacheKey, and found that it always returns false when comparing the annotation member of the two CacheKeys - even when both annotations refer to the same instance.

ConstraintValidatorManager$CacheKey.java
if ( annotation != null ? !annotation.equals( cacheKey.annotation ) : cacheKey.annotation != null ) {
    return false;
}

The annotation in this case isn't a real annotation (because the constraints are defined in XML) - it's a JDK dynamic Proxy which uses the AnnotationProxy class as its InvocationHandler.

AnnotationProxy.java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if ( values.containsKey( method.getName() ) ) {
        return values.get( method.getName() );
    }
    return method.invoke( this, args );
}
When equals() is called via the dynamic Proxy, the eventual invocation is a call to Object.equals(). The problem is that the left-hand argument is *this* - an AnnotationProxy instance, and the right-hand argument is a dynamic Proxy instance. Object.equals() therefore always returns false.

This simple piece of code shows the broken call to equals() clearly.

{code:title=Test.java|borderStyle=solid}
AnnotationDescriptor<DecimalMin> descriptor = 
    new AnnotationDescriptor<DecimalMin>(DecimalMin.class);
descriptor.setValue("message", "some message");
descriptor.setValue("value", "1");
		
AnnotationProxy proxy = new AnnotationProxy(descriptor);
	
Annotation annotation = (Annotation)Proxy.newProxyInstance(
    DecimalMin.class.getClassLoader(),
    new Class[] { DecimalMin.class },
    proxy);

System.out.println(annotation.equals(annotation));

The call annotation.equals(annotation) always returns false.

Environment: hibernate-validator 5.0.0.Alpha1
Java 6
Project: Hibernate Validator
Labels: validation
Priority: Major Major
Reporter: Craig Leonard
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira