Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.literal.NamedLiteral;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionPoint;
Expand All @@ -37,12 +39,15 @@
import javax.validation.ValidatorFactory;
import javax.validation.valueextraction.ValueExtractor;

import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.cdi.spi.BeanNames;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper;
import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader;
import org.hibernate.validator.internal.util.privilegedactions.GetInstancesFromServiceLoader;
import org.hibernate.validator.internal.util.privilegedactions.LoadClass;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;

/**
* A {@link Bean} representing a {@link ValidatorFactory}. There is one instance of this type representing the default
Expand Down Expand Up @@ -126,6 +131,22 @@ public ValidatorFactory create(CreationalContext<ValidatorFactory> ctx) {
config.parameterNameProvider( createParameterNameProvider( config ) );
config.clockProvider( createClockProvider( config ) );

if ( config instanceof HibernateValidatorConfiguration ) {
HibernateValidatorConfiguration hvConfig = (HibernateValidatorConfiguration) config;
Instance<BeanMetaDataClassNormalizer> beanMetaDataClassNormalizerInstance =
beanManager.createInstance()
.select(
BeanMetaDataClassNormalizer.class,
NamedLiteral.of( BeanNames.BEAN_META_DATA_CLASS_NORMALIZER )
);
if ( beanMetaDataClassNormalizerInstance.isResolvable() ) {
BeanMetaDataClassNormalizer normalizer = beanMetaDataClassNormalizerInstance.get();
destructibleResources.add( new DestructibleBeanInstance<>( beanManager, normalizer ) );

hvConfig.beanMetaDataClassNormalizer( normalizer );
}
}

addValueExtractorBeans( config );

return config.buildValidatorFactory();
Expand Down
16 changes: 16 additions & 0 deletions cdi/src/main/java/org/hibernate/validator/cdi/spi/BeanNames.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cdi.spi;

public final class BeanNames {

private BeanNames() {
}

public static final String BEAN_META_DATA_CLASS_NORMALIZER = "hibernate-validator-bean-meta-data-class-normalizer";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.cdi.internal.beanmetadataclassnormalizer;

public interface CustomProxy {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.cdi.internal.beanmetadataclassnormalizer;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.literal.NamedLiteral;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.InjectionPoint;

import org.hibernate.validator.cdi.spi.BeanNames;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;


public class CustomProxyBeanMetaDataClassNormalizer
implements BeanMetaDataClassNormalizer, Bean<BeanMetaDataClassNormalizer> {

@Override
public <T> Class<? super T> normalize(Class<T> clazz) {
if ( CustomProxy.class.isAssignableFrom( clazz ) ) {
return clazz.getSuperclass();
}
return clazz;
}

@Override
public Class<?> getBeanClass() {
return BeanMetaDataClassNormalizer.class;
}

@Override
public Set<InjectionPoint> getInjectionPoints() {
return Collections.emptySet();
}

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

@Override
public BeanMetaDataClassNormalizer create(CreationalContext<BeanMetaDataClassNormalizer> creationalContext) {
return new CustomProxyBeanMetaDataClassNormalizer();
}

@Override
public void destroy(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer,
CreationalContext<BeanMetaDataClassNormalizer> creationalContext) {
// Nothing to do
}

@Override
public Set<Type> getTypes() {
return Collections.singleton( BeanMetaDataClassNormalizer.class );
}

@Override
public Set<Annotation> getQualifiers() {
return Collections.singleton( NamedLiteral.of( BeanNames.BEAN_META_DATA_CLASS_NORMALIZER ) );
}

@Override
public Class<? extends Annotation> getScope() {
return ApplicationScoped.class;
}

@Override
public String getName() {
return BeanNames.BEAN_META_DATA_CLASS_NORMALIZER;
}

@Override
public Set<Class<? extends Annotation>> getStereotypes() {
return Collections.emptySet();
}

@Override
public boolean isAlternative() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.cdi.internal.beanmetadataclassnormalizer;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.Extension;

public class CustomProxyBeanMetadataClassNormalizerCdiExtension implements Extension {

public void addEjbProxyNormalizer(@Observes AfterBeanDiscovery afterBeanDiscovery) {
afterBeanDiscovery.addBean( new CustomProxyBeanMetaDataClassNormalizer() );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.cdi.internal.beanmetadataclassnormalizer;

import static org.assertj.core.api.Assertions.assertThat;

import javax.inject.Inject;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.cdi.HibernateValidator;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;

import org.testng.annotations.Test;

public class ExtensionProvidedBeanMetadataClassNormalizerTest extends Arquillian {

@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create( JavaArchive.class )
.addAsManifestResource( EmptyAsset.INSTANCE, "beans.xml" )
// Register the CDI extension that provides the normalizer bean
.addAsManifestResource(
new StringAsset( CustomProxyBeanMetadataClassNormalizerCdiExtension.class.getName() ),
"services/javax.enterprise.inject.spi.Extension"
);
}

@HibernateValidator
@Inject
ValidatorFactory validatorFactory;

@HibernateValidator
@Inject
Validator validator;

@Inject
ValidatorFactory defaultValidatorFactory;

@Inject
Validator defaultValidator;

@Test
public void testProxyMetadataIgnoredWithQualifiedValidator() throws Exception {
assertThat( validator ).isNotNull();
doTest( validator );
}

@Test
public void testProxyMetadataIgnoredWithDefaultValidator() throws Exception {
assertThat( defaultValidator ).isNotNull();
doTest( defaultValidator );
}

@Test
public void testProxyMetadataIgnoredWithQualifiedValidatorFactory() throws Exception {
assertThat( validatorFactory ).isNotNull();
doTest( validatorFactory.getValidator() );
}

@Test
public void testProxyMetadataIgnoredWithDefaultValidatorFactory() throws Exception {
assertThat( defaultValidatorFactory ).isNotNull();
doTest( defaultValidatorFactory.getValidator() );
}

private void doTest(Validator validator) {
assertThat( validator ).isNotNull();
/*
* Even though we pass an instance of the proxy class that has invalid annotations,
* we expect those to be ignored
* because of the class normalizer we defined.
*/
assertThat( validator.validate( new TestEntityProxy() ) ).hasSize( 1 );
}

public static class TestEntity {
@NotNull
private String foo;
}

public static class TestEntityProxy extends TestEntity implements CustomProxy {
/*
* This is invalid, but should be ignored because it's defined in a proxy class which gets ignored
* because of the class normalizer we defined.
*/
@DecimalMax(value = "foo")
private String foo;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
Expand Down Expand Up @@ -423,4 +424,7 @@ default S locales(Locale... locales) {
*/
@Incubating
S localeResolver(LocaleResolver localeResolver);

@Incubating
S beanMetaDataClassNormalizer(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import java.util.Locale;
import java.util.Set;

import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;

/**
* Extension of {@link HibernateValidatorConfiguration} with additional methods dedicated to defining the predefined
* scope of bean validation e.g. validated classes, constraint validators...
Expand All @@ -31,7 +29,4 @@ public interface PredefinedScopeHibernateValidatorConfiguration extends BaseHibe
@Incubating
@Deprecated
PredefinedScopeHibernateValidatorConfiguration initializeLocales(Set<Locale> locales);

@Incubating
PredefinedScopeHibernateValidatorConfiguration beanMetaDataClassNormalizer(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.hibernate.validator.internal.xml.config.ValidationBootstrapParameters;
import org.hibernate.validator.internal.xml.config.ValidationXmlParser;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
Expand Down Expand Up @@ -118,6 +119,7 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
private Set<Locale> locales = Collections.emptySet();
private Locale defaultLocale = Locale.getDefault();
private LocaleResolver localeResolver;
private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;

protected AbstractConfigurationImpl(BootstrapState state) {
this();
Expand Down Expand Up @@ -597,6 +599,23 @@ public Set<ValueExtractor<?>> getDefaultValueExtractors() {
return ValueExtractorManager.getDefaultValueExtractors();
}

@Override
public T beanMetaDataClassNormalizer(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer) {
if ( LOG.isDebugEnabled() ) {
if ( beanMetaDataClassNormalizer != null ) {
LOG.debug( "Setting custom BeanMetaDataClassNormalizer of type " + beanMetaDataClassNormalizer.getClass()
.getName() );
}
}
this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer;
return thisAsT();
}

public BeanMetaDataClassNormalizer getBeanMetaDataClassNormalizer() {
return beanMetaDataClassNormalizer;
}


public final Set<DefaultConstraintMapping> getProgrammaticMappings() {
return programmaticMappings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.hibernate.validator.PredefinedScopeHibernateValidatorConfiguration;
import org.hibernate.validator.internal.util.CollectionHelper;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;

/**
* @author Guillaume Smet
Expand All @@ -28,8 +27,6 @@ public class PredefinedScopeConfigurationImpl extends AbstractConfigurationImpl<

private Set<Class<?>> beanClassesToInitialize;

private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;

public PredefinedScopeConfigurationImpl(BootstrapState state) {
super( state );
}
Expand All @@ -55,16 +52,6 @@ public PredefinedScopeHibernateValidatorConfiguration initializeLocales(Set<Loca
return thisAsT();
}

@Override
public PredefinedScopeHibernateValidatorConfiguration beanMetaDataClassNormalizer(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer) {
this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer;
return thisAsT();
}

public BeanMetaDataClassNormalizer getBeanMetaDataClassNormalizer() {
return beanMetaDataClassNormalizer;
}

@Override
protected boolean preloadResourceBundles() {
return true;
Expand Down
Loading