package test; import static org.junit.Assert.assertFalse; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.ParameterNameProvider; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorContext; import javax.validation.ValidatorFactory; import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider; import org.hibernate.validator.internal.metadata.provider.MetaDataProvider; import org.hibernate.validator.internal.metadata.raw.BeanConfiguration; import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.junit.Test; public class HV940 { static class Data { @NotEmpty private String flag; } @Test public void testHV940() { final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); final ValidatorContext validatorContext = validatorFactory.usingContext(); final Validator validator = validatorContext.getValidator(); final Data data = new Data(); final Set> violations_before = validator.validateProperty(data, "flag"); assertFalse("NotNull was not validated", violations_before.isEmpty()); clearAnnotationsAndDisableCaching(validator); final Set> violations_after = validator.validateProperty(data, "flag"); assertFalse("NotNull was not validated", violations_after.isEmpty()); } private void clearAnnotationsAndDisableCaching(final Validator validator) { try { final BeanMetaDataManager beanMetaDataManager = getPrivateField(validator, "beanMetaDataManager"); final BeanMetaDataManager manager_nocache = new BeanMetaDataManagerWithoutCache( // HV940. getPrivateField(beanMetaDataManager, "constraintHelper"), // HV940. getPrivateField(beanMetaDataManager, "executableHelper")); final List metaDataProviders = getSuperPrivateField(manager_nocache, "metaDataProviders"); final AnnotationMetaDataProvider provider = (AnnotationMetaDataProvider) metaDataProviders.get(0); metaDataProviders.clear(); metaDataProviders.add(new AnnotationMetaDataProviderWithoutCache( // HV940. getPrivateField(provider, "constraintHelper"), // HV940. getPrivateField(provider, "parameterNameProvider"), // HV940. getPrivateField(provider, "annotationProcessingOptions"))); clearFlagAnnotations(); setPrivateField(validator, "beanMetaDataManager", manager_nocache); } catch (final Exception ex) { System.err.println("Could not manipulate validator" + ex.getMessage()); } } private static void clearFlagAnnotations() { try { final Field flag = Data.class.getDeclaredField("flag"); setPrivateField(flag, "declaredAnnotations", null); } catch (final Exception ex) { System.err.println("Could not clear annotations. " + ex.getMessage()); } } @SuppressWarnings("unchecked") private static T getPrivateField(final Object object, final String fieldName) throws Exception { final Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); return (T) field.get(object); } @SuppressWarnings("unchecked") private static T getSuperPrivateField(final Object object, final String fieldName) throws Exception { final Field field = object.getClass().getSuperclass().getDeclaredField(fieldName); field.setAccessible(true); return (T) field.get(object); } private static void setPrivateField(final Object object, final String fieldName, final T value) throws Exception { final Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } private static class BeanMetaDataManagerWithoutCache extends BeanMetaDataManager { public BeanMetaDataManagerWithoutCache(final ConstraintHelper constraintHelper, final ExecutableHelper executableHelper) { super(constraintHelper, executableHelper); } @Override public BeanMetaData getBeanMetaData(final Class beanClass) { clear(); clearFlagAnnotations(); return super.getBeanMetaData(beanClass); } @Override public boolean isConstrained(final Class beanClass) { clear(); clearFlagAnnotations(); return super.isConstrained(beanClass); } } private static class AnnotationMetaDataProviderWithoutCache extends AnnotationMetaDataProvider { public AnnotationMetaDataProviderWithoutCache(final ConstraintHelper constraintHelper, final ParameterNameProvider parameterNameProvider, final AnnotationProcessingOptions annotationProcessingOptions) { super(constraintHelper, parameterNameProvider, annotationProcessingOptions); } @Override @SuppressWarnings("unchecked") public List> getBeanConfigurationForHierarchy(final Class beanClass) { final List> configurations = new ArrayList>(); for (final Class hierarchyClass : ClassHierarchyHelper.getHierarchy(beanClass)) { BeanConfiguration configuration; try { final Method retrieveBeanConfigurationMethod = AnnotationMetaDataProvider.class.getDeclaredMethod( "retrieveBeanConfiguration", Class.class); retrieveBeanConfigurationMethod.setAccessible(true); configuration = (BeanConfiguration) retrieveBeanConfigurationMethod.invoke(this, hierarchyClass); } catch (final Exception e) { throw new RuntimeException(e); } if (configuration != null) { configurations.add(configuration); } } return configurations; } } }