Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -20,6 +20,7 @@
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import org.hibernate.validator.spi.scripting.ScriptEvaluator;
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
Expand Down Expand Up @@ -311,4 +312,16 @@ public interface HibernateValidatorConfiguration extends Configuration<Hibernate
*/
@Incubating
HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload);

/**
* Allows to set a {@link BeanMetaDataClassNormalizer} that will be used to normalize the class before
* accessing the metadata.
*
* @param beanMetaDataClassNormalizer the {@code BeanMetaDataClassNormalizer} to use
* @return {@code this} following the chaining method pattern
*
* @since 6.0.19
*/
@Incubating
HibernateValidatorConfiguration beanMetaDataClassNormalizer(BeanMetaDataClassNormalizer beanMetaDataClassNormalizer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,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.resourceloading.ResourceBundleLocator;
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
Expand Down Expand Up @@ -103,6 +104,7 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi
private ScriptEvaluatorFactory scriptEvaluatorFactory;
private Duration temporalValidationTolerance;
private Object constraintValidatorPayload;
private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;

public ConfigurationImpl(BootstrapState state) {
this();
Expand Down Expand Up @@ -331,6 +333,23 @@ public HibernateValidatorConfiguration externalClassLoader(ClassLoader externalC
return this;
}

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

public BeanMetaDataClassNormalizer getBeanMetaDataClassNormalizer() {
return beanMetaDataClassNormalizer;
}

@Override
public final ValidatorFactory buildValidatorFactory() {
loadValueExtractorsFromServiceLoader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
import org.hibernate.validator.internal.metadata.BeanMetaDataManager;
import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider;
Expand All @@ -59,6 +60,7 @@
import org.hibernate.validator.internal.util.privilegedactions.NewInstance;
import org.hibernate.validator.internal.util.stereotypes.Immutable;
import org.hibernate.validator.internal.util.stereotypes.ThreadSafe;
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
import org.hibernate.validator.spi.cfg.ConstraintMappingContributor;
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;

Expand Down Expand Up @@ -132,6 +134,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory {

private final ValueExtractorManager valueExtractorManager;

private final BeanMetaDataClassNormalizer beanMetadataClassNormalizer;

private final ValidationOrderGenerator validationOrderGenerator;

public ValidatorFactoryImpl(ConfigurationState configurationState) {
Expand Down Expand Up @@ -198,6 +202,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {

this.validationOrderGenerator = new ValidationOrderGenerator();

this.beanMetadataClassNormalizer = hibernateSpecificConfig.getBeanMetaDataClassNormalizer() != null ?
hibernateSpecificConfig.getBeanMetaDataClassNormalizer() :
new DefaultBeanMetaDataClassNormalizer();

if ( LOG.isDebugEnabled() ) {
logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext );
}
Expand Down Expand Up @@ -347,6 +355,7 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
typeResolutionHelper,
validatorFactoryScopedContext.getParameterNameProvider(),
valueExtractorManager,
beanMetadataClassNormalizer,
validationOrderGenerator,
buildDataProviders(),
methodValidationConfiguration
Expand Down
Loading