Skip to content

Commit

Permalink
HV-589 Introducing UnconstrainedEntityMetaDataSingleton as a placehol…
Browse files Browse the repository at this point in the history
…der for unconstrained types.

This placeholder is used when BeanMetaDataManager#isConstrained is called. When retrieving an instance of BeanMetaData always a proper BeanMetaDataImpl instance is returned.
  • Loading branch information
hferentschik committed Jan 9, 2014
1 parent 1d9f7e7 commit 2b1a759
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 22 deletions.
Expand Up @@ -146,6 +146,10 @@ public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory,
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
Contracts.assertNotNull( object, MESSAGES.validatedObjectMustNotBeNull() );

if ( !beanMetaDataManager.isConstrained( object.getClass() ) ) {
return Collections.emptySet();
}

ValidationOrder validationOrder = determineGroupValidationOrder( groups );
ValidationContext<T> validationContext = getValidationContext().forValidate( object );

Expand All @@ -166,6 +170,10 @@ public final <T> Set<ConstraintViolation<T>> validateProperty(T object, String p
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
ValidationContext<T> context = getValidationContext().forValidateProperty( object );

if ( !beanMetaDataManager.isConstrained( context.getRootBeanClass() ) ) {
return Collections.emptySet();
}

return validatePropertyInContext(
context,
PathImpl.createPathFromString( propertyName ),
Expand All @@ -177,6 +185,10 @@ public final <T> Set<ConstraintViolation<T>> validateProperty(T object, String p
public final <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
Contracts.assertNotNull( beanType, MESSAGES.beanTypeCannotBeNull() );

if ( !beanMetaDataManager.isConstrained( beanType ) ) {
return Collections.emptySet();
}

sanityCheckPropertyPath( propertyName );
ValidationOrder validationOrder = determineGroupValidationOrder( groups );
ValidationContext<T> context = getValidationContext().forValidateValue( beanType );
Expand Down Expand Up @@ -237,6 +249,10 @@ private <T> Set<ConstraintViolation<T>> validateParameters(T object, ExecutableE
parameterValues
);

if ( !beanMetaDataManager.isConstrained( context.getRootBeanClass() ) ) {
return Collections.emptySet();
}

validateParametersInContext( context, parameterValues, validationOrder );

return context.getFailingConstraints();
Expand All @@ -251,6 +267,10 @@ private <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Executable
returnValue
);

if ( !beanMetaDataManager.isConstrained( context.getRootBeanClass() ) ) {
return Collections.emptySet();
}

validateReturnValueInContext( context, object, returnValue, validationOrder );

return context.getFailingConstraints();
Expand Down
Expand Up @@ -25,6 +25,7 @@
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl.BeanMetaDataBuilder;
import org.hibernate.validator.internal.metadata.aggregated.UnconstrainedEntityMetaDataSingleton;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
Expand Down Expand Up @@ -102,7 +103,12 @@ public class BeanMetaDataManager {
* @param executableHelper the executable helper
*/
public BeanMetaDataManager(ConstraintHelper constraintHelper, ExecutableHelper executableHelper) {
this( constraintHelper, executableHelper, new DefaultParameterNameProvider(), Collections.<MetaDataProvider>emptyList() );
this(
constraintHelper,
executableHelper,
new DefaultParameterNameProvider(),
Collections.<MetaDataProvider>emptyList()
);
}

/**
Expand Down Expand Up @@ -141,26 +147,12 @@ public BeanMetaDataManager(ConstraintHelper constraintHelper,
this.metaDataProviders.add( defaultProvider );
}

@SuppressWarnings("unchecked")
public <T> BeanMetaData<T> getBeanMetaData(Class<T> beanClass) {
Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() );

BeanMetaData<T> beanMetaData = (BeanMetaData<T>) beanMetaDataCache.get( beanClass );

// create a new BeanMetaData in case none is cached
if ( beanMetaData == null ) {
beanMetaData = createBeanMetaData( beanClass );

final BeanMetaData<T> cachedBeanMetaData = (BeanMetaData<T>) beanMetaDataCache.putIfAbsent(
beanClass,
beanMetaData
);
if ( cachedBeanMetaData != null ) {
beanMetaData = cachedBeanMetaData;
}
}
public boolean isConstrained(Class<?> beanClass) {
return getOrCreateBeanMetaData( beanClass, true ).hasConstraints();
}

return beanMetaData;
public <T> BeanMetaData<T> getBeanMetaData(Class<T> beanClass) {
return getOrCreateBeanMetaData( beanClass, false );
}

public void clear() {
Expand Down Expand Up @@ -203,4 +195,37 @@ private AnnotationProcessingOptions getAnnotationProcessingOptionsFromNonDefault

return options;
}

@SuppressWarnings("unchecked")
private <T> BeanMetaData<T> getOrCreateBeanMetaData(Class<T> beanClass, boolean allowUnconstrainedTypeSingleton) {
Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() );

BeanMetaData<T> beanMetaData = (BeanMetaData<T>) beanMetaDataCache.get( beanClass );

// create a new BeanMetaData in case none is cached
if ( beanMetaData == null ) {
beanMetaData = createBeanMetaData( beanClass );
if ( !beanMetaData.hasConstraints() && allowUnconstrainedTypeSingleton ) {
beanMetaData = (BeanMetaData<T>) UnconstrainedEntityMetaDataSingleton.getSingleton();
}

final BeanMetaData<T> cachedBeanMetaData = (BeanMetaData<T>) beanMetaDataCache.putIfAbsent(
beanClass,
beanMetaData
);
if ( cachedBeanMetaData != null ) {
beanMetaData = cachedBeanMetaData;
}
}

if ( beanMetaData instanceof UnconstrainedEntityMetaDataSingleton && !allowUnconstrainedTypeSingleton ) {
beanMetaData = createBeanMetaData( beanClass );
beanMetaDataCache.put(
beanClass,
beanMetaData
);
}

return beanMetaData;
}
}
Expand Up @@ -38,6 +38,13 @@ public interface BeanMetaData<T> extends Validatable {
*/
Class<T> getBeanClass();

/**
* Returns {@code true} if the bean class for this bean meta data has any constraints at all, {@code false} otherwise.
*
* @return {@code true} if the bean class for this bean meta data has any constraints at all, {@code false} otherwise.
*/
boolean hasConstraints();

/**
* @return an instance of {@code ElementDescriptor} describing the bean this meta data applies for.
*/
Expand Down
Expand Up @@ -28,6 +28,7 @@
import javax.validation.groups.Default;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstructorDescriptor;
import javax.validation.metadata.MethodType;
import javax.validation.metadata.PropertyDescriptor;

import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
Expand Down Expand Up @@ -197,6 +198,17 @@ public Class<T> getBeanClass() {
return beanClass;
}

@Override
public boolean hasConstraints() {
if ( beanDescriptor.isBeanConstrained()
|| !beanDescriptor.getConstrainedConstructors().isEmpty()
|| !beanDescriptor.getConstrainedMethods( MethodType.NON_GETTER, MethodType.GETTER ).isEmpty() ) {
return true;
}

return false;
}

@Override
public BeanDescriptor getBeanDescriptor() {
return beanDescriptor;
Expand Down
@@ -0,0 +1,98 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2009, 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.internal.metadata.aggregated;

import java.util.List;
import java.util.Set;
import javax.validation.ConstraintDeclarationException;
import javax.validation.metadata.BeanDescriptor;

import org.hibernate.validator.internal.metadata.core.MetaConstraint;
import org.hibernate.validator.internal.metadata.facets.Cascadable;
import org.hibernate.validator.internal.metadata.raw.ExecutableElement;

/**
* A dummy implementation of {@code BeanMetaData} used as a placeholder for unconstrained types.
*
* @author Hardy Ferentschik
*/
public final class UnconstrainedEntityMetaDataSingleton<T> implements BeanMetaData<T> {

private static final UnconstrainedEntityMetaDataSingleton<?> singletonDummy = new UnconstrainedEntityMetaDataSingleton();

private UnconstrainedEntityMetaDataSingleton() {
}

public static UnconstrainedEntityMetaDataSingleton<?> getSingleton() {
return singletonDummy;
}

@Override
public Class<T> getBeanClass() {
throw new UnsupportedOperationException();
}

@Override
public boolean hasConstraints() {
return false;
}

@Override
public BeanDescriptor getBeanDescriptor() {
throw new UnsupportedOperationException();
}

@Override
public PropertyMetaData getMetaDataFor(String propertyName) {
throw new UnsupportedOperationException();
}

@Override
public List<Class<?>> getDefaultGroupSequence(T beanState) {
throw new UnsupportedOperationException();
}

@Override
public boolean defaultGroupSequenceIsRedefined() {
return false;
}

@Override
public Set<MetaConstraint<?>> getMetaConstraints() {
throw new UnsupportedOperationException();
}

@Override
public Set<MetaConstraint<?>> getDirectMetaConstraints() {
throw new UnsupportedOperationException();
}

@Override
public ExecutableMetaData getMetaDataFor(ExecutableElement method) throws ConstraintDeclarationException {
throw new UnsupportedOperationException();
}

@Override
public List<Class<? super T>> getClassHierarchy() {
throw new UnsupportedOperationException();
}

@Override
public Iterable<Cascadable> getCascadables() {
throw new UnsupportedOperationException();
}
}
Expand Up @@ -21,16 +21,20 @@
import java.io.FileInputStream;
import java.io.IOException;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import org.hibernate.validator.internal.metadata.BeanMetaDataManager;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;

import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotSame;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

/**
Expand All @@ -41,10 +45,15 @@ public class BeanMetaDataManagerTest {
// high enough to force a OutOfMemoryError in case references are not freed
private static final int MAX_ENTITY_COUNT = 100000;

private BeanMetaDataManager metaDataManager;

@BeforeMethod
public void setUpBeanMetaDataManager() {
metaDataManager = new BeanMetaDataManager( new ConstraintHelper(), new ExecutableHelper() );
}

@Test
public void testBeanMetaDataCanBeGarbageCollected() throws Exception {
BeanMetaDataManager metaDataManager = new BeanMetaDataManager( new ConstraintHelper(), new ExecutableHelper() );

Class<?> lastIterationsBean = null;
int totalCreatedMetaDataInstances = 0;
int cachedBeanMetaDataInstances = 0;
Expand All @@ -69,6 +78,35 @@ public void testBeanMetaDataCanBeGarbageCollected() throws Exception {
}
}

@Test
public void testIsConstrainedForConstrainedEntity() {
assertTrue( metaDataManager.isConstrained( Engine.class ) );
}

@Test
public void testIsConstrainedForUnConstrainedEntity() {
assertFalse( metaDataManager.isConstrained( UnconstrainedEntity.class ) );
}

@Test
public void testGetMetaDataForConstrainedEntity() {
BeanMetaData beanMetaData = metaDataManager.getBeanMetaData( Engine.class );
assertTrue( beanMetaData instanceof BeanMetaDataImpl );
assertTrue( beanMetaData.hasConstraints() );
}

@Test
public void testGetMetaDataForUnConstrainedEntity() {
assertFalse( metaDataManager.isConstrained( UnconstrainedEntity.class ) );

BeanMetaData beanMetaData = metaDataManager.getBeanMetaData( UnconstrainedEntity.class );
assertTrue(
beanMetaData instanceof BeanMetaDataImpl,
"#getBeanMetaData should always return a valid BeanMetaData instance. Returned class: " + beanMetaData.getClass()
);
assertFalse( beanMetaData.hasConstraints() );
}

public class CustomClassLoader extends ClassLoader {

/**
Expand Down Expand Up @@ -130,4 +168,8 @@ private byte[] loadClassData(String className) throws IOException {
return buff;
}
}

public static class UnconstrainedEntity {
private String foo;
}
}

0 comments on commit 2b1a759

Please sign in to comment.