/* * 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.internal.cdi; import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collections; import java.util.Set; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.InjectionPoint; import javax.enterprise.inject.spi.PassivationCapable; import javax.validation.BootstrapConfiguration; import javax.validation.ClockProvider; import javax.validation.Configuration; import javax.validation.ConstraintValidatorFactory; import javax.validation.MessageInterpolator; import javax.validation.ParameterNameProvider; import javax.validation.TraversableResolver; import javax.validation.Validation; import javax.validation.ValidatorFactory; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.hibernate.validator.internal.util.privilegedactions.LoadClass; /** * A {@link Bean} representing a {@link ValidatorFactory}. There is one instance of this type representing the default * validator factory and optionally another instance representing the HV validator factory in case the default provider * is not HV. * * @author Hardy Ferentschik * @author Gunnar Morling * @author Guillaume Smet */ public class ValidatorFactoryBean implements Bean<ValidatorFactory>, PassivationCapable { private final BeanManager beanManager; private final Set<DestructibleBeanInstance<?>> destructibleResources; private final ValidationProviderHelper validationProviderHelper; private final Set<Type> types; public ValidatorFactoryBean(BeanManager beanManager, ValidationProviderHelper validationProviderHelper) { this.beanManager = beanManager; this.destructibleResources = newHashSet( 5 ); this.validationProviderHelper = validationProviderHelper; this.types = Collections.unmodifiableSet( CollectionHelper.<Type>newHashSet( ClassHierarchyHelper.getHierarchy( validationProviderHelper.getValidatorFactoryBeanClass() ) ) ); } @Override public Class<?> getBeanClass() { return validationProviderHelper.getValidatorFactoryBeanClass(); } @Override public Set<InjectionPoint> getInjectionPoints() { return Collections.emptySet(); } @Override public String getName() { return null; } @Override public Set<Annotation> getQualifiers() { return validationProviderHelper.getQualifiers(); } @Override public Class<? extends Annotation> getScope() { return ApplicationScoped.class; } @Override public Set<Class<? extends Annotation>> getStereotypes() { return Collections.emptySet(); } @Override public Set<Type> getTypes() { return types; } @Override public boolean isAlternative() { return false; } @Override public boolean isNullable() { return false; } @Override public ValidatorFactory create(CreationalContext<ValidatorFactory> ctx) { Configuration<?> config = getConfiguration(); config.constraintValidatorFactory( createConstraintValidatorFactory( config ) ); config.messageInterpolator( createMessageInterpolator( config ) ); config.traversableResolver( createTraversableResolver( config ) ); config.parameterNameProvider( createParameterNameProvider( config ) ); config.clockProvider( createClockProvider( config ) ); return config.buildValidatorFactory(); } @Override public void destroy(ValidatorFactory instance, CreationalContext<ValidatorFactory> ctx) { for ( DestructibleBeanInstance<?> resource : destructibleResources ) { resource.destroy(); } instance.close(); } private MessageInterpolator createMessageInterpolator(Configuration<?> config) { BootstrapConfiguration bootstrapConfiguration = config.getBootstrapConfiguration(); String messageInterpolatorFqcn = bootstrapConfiguration.getMessageInterpolatorClassName(); if ( messageInterpolatorFqcn == null ) { return config.getDefaultMessageInterpolator(); } @SuppressWarnings("unchecked") Class<? extends MessageInterpolator> messageInterpolatorClass = (Class<? extends MessageInterpolator>) run( LoadClass.action( messageInterpolatorFqcn, null ) ); return createInstance( messageInterpolatorClass ); } private TraversableResolver createTraversableResolver(Configuration<?> config) { BootstrapConfiguration bootstrapConfiguration = config.getBootstrapConfiguration(); String traversableResolverFqcn = bootstrapConfiguration.getTraversableResolverClassName(); if ( traversableResolverFqcn == null ) { return config.getDefaultTraversableResolver(); } @SuppressWarnings("unchecked") Class<? extends TraversableResolver> traversableResolverClass = (Class<? extends TraversableResolver>) run( LoadClass.action( traversableResolverFqcn, null ) ); return createInstance( traversableResolverClass ); } private ParameterNameProvider createParameterNameProvider(Configuration<?> config) { BootstrapConfiguration bootstrapConfiguration = config.getBootstrapConfiguration(); String parameterNameProviderFqcn = bootstrapConfiguration.getParameterNameProviderClassName(); if ( parameterNameProviderFqcn == null ) { return config.getDefaultParameterNameProvider(); } @SuppressWarnings("unchecked") Class<? extends ParameterNameProvider> parameterNameProviderClass = (Class<? extends ParameterNameProvider>) run( LoadClass.action( parameterNameProviderFqcn, null ) ); return createInstance( parameterNameProviderClass ); } private ClockProvider createClockProvider(Configuration<?> config) { BootstrapConfiguration bootstrapConfiguration = config.getBootstrapConfiguration(); String clockProviderFqcn = bootstrapConfiguration.getClockProviderClassName(); if ( clockProviderFqcn == null ) { return config.getDefaultClockProvider(); } @SuppressWarnings("unchecked") Class<? extends ClockProvider> clockProviderClass = (Class<? extends ClockProvider>) run( LoadClass.action( clockProviderFqcn, null ) ); return createInstance( clockProviderClass ); } private ConstraintValidatorFactory createConstraintValidatorFactory(Configuration<?> config) { BootstrapConfiguration configSource = config.getBootstrapConfiguration(); String constraintValidatorFactoryFqcn = configSource.getConstraintValidatorFactoryClassName(); if ( constraintValidatorFactoryFqcn == null ) { // use default return createInstance( InjectingConstraintValidatorFactory.class ); } @SuppressWarnings("unchecked") Class<? extends ConstraintValidatorFactory> constraintValidatorFactoryClass = (Class<? extends ConstraintValidatorFactory>) run( LoadClass.action( constraintValidatorFactoryFqcn, null ) ); return createInstance( constraintValidatorFactoryClass ); } private <T> T createInstance(Class<T> type) { DestructibleBeanInstance<T> destructibleInstance = new DestructibleBeanInstance<T>( beanManager, type ); destructibleResources.add( destructibleInstance ); return destructibleInstance.getInstance(); } private Configuration<?> getConfiguration() { return validationProviderHelper.isDefaultProvider() ? Validation.byDefaultProvider().configure() : Validation.byProvider( org.hibernate.validator.HibernateValidator.class ).configure(); } /** * Runs the given privileged action, using a privileged block if required. * <p> * <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary * privileged actions within HV's protection domain. */ private <T> T run(PrivilegedAction<T> action) { return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run(); } @Override public String getId() { return ValidatorFactoryBean.class.getName() + "_" + ( validationProviderHelper.isDefaultProvider() ? "default" : "hv" ); } @Override public String toString() { return "ValidatorFactoryBean [id=" + getId() + "]"; } }