HHH-7990 - Bootstrapping Hibernate fails if javax.validation API is on classpath but no provider

(cherry picked from commit 898bab28ca)
This commit is contained in:
Steve Ebersole 2013-02-22 12:22:12 -06:00
parent 5a0a511142
commit 3bb451b10b
6 changed files with 415 additions and 462 deletions

View File

@ -0,0 +1,70 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cfg.beanvalidation;
import java.util.Set;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/**
* Defines the context needed to call the {@link TypeSafeActivator}
*
* @author Steve Ebersole
*/
public interface ActivationContext {
/**
* Access the requested validation mode(s).
* <p/>
* IMPL NOTE : the legacy code allowed multiple mode values to be specified, so that is why it is multi-valued here.
* However, I cannot find any good reasoning why it was defined that way and even JPA states it should be a single
* value. For 4.1 (in maintenance) I think it makes the most sense to not mess with it. Discuss for
* 4.2 and beyond.
*
* @return The requested validation modes
*/
public Set<ValidationMode> getValidationModes();
/**
* Access the Configuration
*
* @return The Hibernate Configuration object
*/
public Configuration getConfiguration();
/**
* Access the SessionFactory being built to trigger this BV activation
*
* @return The SessionFactory being built
*/
public SessionFactoryImplementor getSessionFactory();
/**
* Access the ServiceRegistry specific to the SessionFactory being built.
*
* @return The SessionFactoryServiceRegistry
*/
public SessionFactoryServiceRegistry getServiceRegistry();
}

View File

@ -25,23 +25,15 @@ package org.hibernate.cfg.beanvalidation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
@ -50,7 +42,10 @@ import org.hibernate.service.spi.SessionFactoryServiceRegistry;
* @author Steve Ebersole
*/
public class BeanValidationIntegrator implements Integrator {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, BeanValidationIntegrator.class.getName());
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
BeanValidationIntegrator.class.getName()
);
public static final String APPLY_CONSTRAINTS = "hibernate.validator.apply_to_ddl";
@ -58,16 +53,22 @@ public class BeanValidationIntegrator implements Integrator {
public static final String MODE_PROPERTY = "javax.persistence.validation.mode";
private static final String ACTIVATOR_CLASS = "org.hibernate.cfg.beanvalidation.TypeSafeActivator";
private static final String DDL_METHOD = "applyDDL";
private static final String ACTIVATE_METHOD = "activateBeanValidation";
private static final String VALIDATE_METHOD = "validateFactory";
private static final String ACTIVATOR_CLASS_NAME = "org.hibernate.cfg.beanvalidation.TypeSafeActivator";
private static final String VALIDATE_SUPPLIED_FACTORY_METHOD_NAME = "validateSuppliedFactory";
private static final String ACTIVATE_METHOD_NAME = "activate";
/**
* Used to validate the type of an explicitly passed ValidatorFactory instance
*
* @param object The supposed ValidatorFactory instance
*/
@SuppressWarnings("unchecked")
public static void validateFactory(Object object) {
try {
final Class activatorClass = BeanValidationIntegrator.class.getClassLoader().loadClass( ACTIVATOR_CLASS );
// this direct usage of ClassLoader should be fine since the classes exist in the same jar
final Class activatorClass = BeanValidationIntegrator.class.getClassLoader().loadClass( ACTIVATOR_CLASS_NAME );
try {
final Method validateMethod = activatorClass.getMethod( VALIDATE_METHOD, Object.class );
final Method validateMethod = activatorClass.getMethod( VALIDATE_SUPPLIED_FACTORY_METHOD_NAME, Object.class );
if ( ! validateMethod.isAccessible() ) {
validateMethod.setAccessible( true );
}
@ -101,291 +102,114 @@ public class BeanValidationIntegrator implements Integrator {
@Override
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
// determine requested validation modes.
final Configuration configuration,
final SessionFactoryImplementor sessionFactory,
final SessionFactoryServiceRegistry serviceRegistry) {
// IMPL NOTE : see the comments on ActivationContext.getValidationModes() as to why this is multi-valued...
final Set<ValidationMode> modes = ValidationMode.getModes( configuration.getProperties().get( MODE_PROPERTY ) );
if ( modes.size() > 1 ) {
LOG.multipleValidationModes( ValidationMode.loggable( modes ) );
}
if ( modes.size() == 1 && modes.contains( ValidationMode.NONE ) ) {
// we have nothing to do; just return
return;
}
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
Dialect dialect = serviceRegistry.getService( JdbcServices.class ).getDialect();
// try to locate a BV class to see if it is available on the classpath
boolean isBeanValidationAvailable;
// see if the Bean Validation API is available on the classpath
if ( isBeanValidationApiAvailable( classLoaderService ) ) {
// and if so, call out to the TypeSafeActivator
try {
classLoaderService.classForName( BV_CHECK_CLASS );
isBeanValidationAvailable = true;
}
catch ( Exception e ) {
isBeanValidationAvailable = false;
}
// locate the type safe activator class
final Class typeSafeActivatorClass = loadTypeSafeActivatorClass( serviceRegistry );
// todo : if this works out, probably better to simply alter TypeSafeActivator into a single method...
applyRelationalConstraints(
modes,
isBeanValidationAvailable,
typeSafeActivatorClass,
configuration,
dialect
);
applyHibernateListeners(
modes,
isBeanValidationAvailable,
typeSafeActivatorClass,
configuration,
sessionFactory,
serviceRegistry
);
}
/**
* {@inheritDoc}
*
* @see org.hibernate.integrator.spi.Integrator#integrate(org.hibernate.metamodel.source.MetadataImplementor, org.hibernate.engine.spi.SessionFactoryImplementor, org.hibernate.service.spi.SessionFactoryServiceRegistry)
*/
final Class typeSafeActivatorClass = loadTypeSafeActivatorClass( classLoaderService );
@SuppressWarnings("unchecked")
final Method activateMethod = typeSafeActivatorClass.getMethod( ACTIVATE_METHOD_NAME, ActivationContext.class );
final ActivationContext activationContext = new ActivationContext() {
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
// Properties props = sessionFactory.getProperties();
// final Set<ValidationMode> modes = ValidationMode.getModes(props.get(MODE_PROPERTY));
// final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
// // try to locate a BV class to see if it is available on the classpath
// boolean isBeanValidationAvailable;
// try {
// classLoaderService.classForName( BV_CHECK_CLASS );
// isBeanValidationAvailable = true;
// } catch (Exception error) {
// isBeanValidationAvailable = false;
// }
// // locate the type safe activator class
// final Class typeSafeActivatorClass = loadTypeSafeActivatorClass(serviceRegistry);
// // todo : if this works out, probably better to simply alter TypeSafeActivator into a single method...
// applyRelationalConstraints(modes, isBeanValidationAvailable, typeSafeActivatorClass, props, metadata);
// applyHibernateListeners(modes, isBeanValidationAvailable, typeSafeActivatorClass, sessionFactory, serviceRegistry);
}
private Class loadTypeSafeActivatorClass(SessionFactoryServiceRegistry serviceRegistry) {
try {
return serviceRegistry.getService( ClassLoaderService.class ).classForName( ACTIVATOR_CLASS );
}
catch (Exception e) {
return null;
}
}
private void applyRelationalConstraints(
Set<ValidationMode> modes,
boolean beanValidationAvailable,
Class typeSafeActivatorClass,
Configuration configuration,
Dialect dialect) {
if ( ! ConfigurationHelper.getBoolean( APPLY_CONSTRAINTS, configuration.getProperties(), true ) ){
LOG.debug( "Skipping application of relational constraints from legacy Hibernate Validator" );
return;
}
if ( ! ( modes.contains( ValidationMode.DDL ) || modes.contains( ValidationMode.AUTO ) ) ) {
return;
}
if ( ! beanValidationAvailable ) {
if ( modes.contains( ValidationMode.DDL ) ) {
throw new HibernateException( "Bean Validation not available in the class path but required in " + MODE_PROPERTY );
}
else if (modes.contains( ValidationMode.AUTO ) ) {
//nothing to activate
return;
}
}
try {
Method applyDDLMethod = typeSafeActivatorClass.getMethod( DDL_METHOD, Collection.class, Properties.class, Dialect.class );
try {
applyDDLMethod.invoke(
null,
configuration.createMappings().getClasses().values(),
configuration.getProperties(),
dialect
);
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new HibernateException( "Error applying BeanValidation relational constraints", e );
}
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new HibernateException( "Unable to locate TypeSafeActivator#applyDDL method", e );
}
}
// private void applyRelationalConstraints( Set<ValidationMode> modes,
// boolean beanValidationAvailable,
// Class typeSafeActivatorClass,
// Properties properties,
// MetadataImplementor metadata ) {
// if (!ConfigurationHelper.getBoolean(APPLY_CONSTRAINTS, properties, true)){
// LOG.debug("Skipping application of relational constraints from legacy Hibernate Validator");
// return;
// }
// if (!(modes.contains(ValidationMode.DDL) || modes.contains(ValidationMode.AUTO))) return;
// if (!beanValidationAvailable) {
// if (modes.contains(ValidationMode.DDL))
// throw new HibernateException("Bean Validation not available in the class path but required in " + MODE_PROPERTY);
// if(modes.contains(ValidationMode.AUTO)) return; //nothing to activate
// }
// try {
// Method applyDDLMethod = typeSafeActivatorClass.getMethod(DDL_METHOD, Iterable.class, Properties.class, ClassLoaderService.class);
// try {
// applyDDLMethod.invoke(null, metadata.getEntityBindings(), properties,
// metadata.getServiceRegistry().getService(ClassLoaderService.class));
// } catch (HibernateException error) {
// throw error;
// } catch (Exception error) {
// throw new HibernateException("Error applying BeanValidation relational constraints", error);
// }
// } catch (HibernateException error) {
// throw error;
// } catch (Exception error) {
// throw new HibernateException("Unable to locate TypeSafeActivator#applyDDL method", error);
// }
// }
private void applyHibernateListeners(
Set<ValidationMode> modes,
boolean beanValidationAvailable,
Class typeSafeActivatorClass,
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
// de-activate not-null tracking at the core level when Bean Validation is present unless the user explicitly
// asks for it
if ( configuration.getProperty( Environment.CHECK_NULLABILITY ) == null ) {
sessionFactory.getSettings().setCheckNullability( false );
}
if ( ! ( modes.contains( ValidationMode.CALLBACK ) || modes.contains( ValidationMode.AUTO ) ) ) {
return;
}
if ( ! beanValidationAvailable ) {
if ( modes.contains( ValidationMode.CALLBACK ) ) {
throw new HibernateException( "Bean Validation not available in the class path but required in " + MODE_PROPERTY );
}
else if (modes.contains( ValidationMode.AUTO ) ) {
//nothing to activate
return;
}
}
try {
Method activateMethod = typeSafeActivatorClass.getMethod( ACTIVATE_METHOD, EventListenerRegistry.class, Configuration.class );
try {
activateMethod.invoke(
null,
serviceRegistry.getService( EventListenerRegistry.class ),
configuration
);
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new HibernateException( "Error applying BeanValidation relational constraints", e );
}
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new HibernateException( "Unable to locate TypeSafeActivator#applyDDL method", e );
}
}
// private void applyHibernateListeners( Set<ValidationMode> modes,
// boolean beanValidationAvailable,
// Class typeSafeActivatorClass,
// SessionFactoryImplementor sessionFactory,
// SessionFactoryServiceRegistry serviceRegistry ) {
// // de-activate not-null tracking at the core level when Bean Validation is present unless the user explicitly
// // asks for it
// if (sessionFactory.getProperties().getProperty(Environment.CHECK_NULLABILITY) == null)
// sessionFactory.getSettings().setCheckNullability( false );
// if (!(modes.contains( ValidationMode.CALLBACK) || modes.contains(ValidationMode.AUTO))) return;
// if (!beanValidationAvailable) {
// if (modes.contains(ValidationMode.CALLBACK))
// throw new HibernateException("Bean Validation not available in the class path but required in " + MODE_PROPERTY);
// if (modes.contains(ValidationMode.AUTO)) return; //nothing to activate
// }
// try {
// Method activateMethod = typeSafeActivatorClass.getMethod(ACTIVATE_METHOD, EventListenerRegistry.class);
// try {
// activateMethod.invoke(null, serviceRegistry.getService(EventListenerRegistry.class));
// }
// catch (HibernateException e) {
// throw e;
// }
// catch (Exception e) {
// throw new HibernateException( "Error applying BeanValidation relational constraints", e );
// }
// }
// catch (HibernateException e) {
// throw e;
// }
// catch (Exception e) {
// throw new HibernateException( "Unable to locate TypeSafeActivator#applyDDL method", e );
// }
// }
// Because the javax validation classes might not be on the runtime classpath
private static enum ValidationMode {
AUTO,
CALLBACK,
NONE,
DDL;
public static Set<ValidationMode> getModes(Object modeProperty) {
Set<ValidationMode> modes = new HashSet<ValidationMode>(3);
if (modeProperty == null) {
modes.add(ValidationMode.AUTO);
}
else {
final String[] modesInString = modeProperty.toString().split( "," );
for ( String modeInString : modesInString ) {
modes.add( getMode(modeInString) );
}
}
if ( modes.size() > 1 && ( modes.contains( ValidationMode.AUTO ) || modes.contains( ValidationMode.NONE ) ) ) {
StringBuilder message = new StringBuilder( "Incompatible validation modes mixed: " );
for (ValidationMode mode : modes) {
message.append( mode ).append( ", " );
}
throw new HibernateException( message.substring( 0, message.length() - 2 ) );
}
public Set<ValidationMode> getValidationModes() {
return modes;
}
private static ValidationMode getMode(String modeProperty) {
if (modeProperty == null || modeProperty.length() == 0) {
return AUTO;
@Override
public Configuration getConfiguration() {
return configuration;
}
@Override
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
@Override
public SessionFactoryServiceRegistry getServiceRegistry() {
return serviceRegistry;
}
};
try {
activateMethod.invoke( null, activationContext );
}
catch (InvocationTargetException e) {
if ( HibernateException.class.isInstance( e.getTargetException() ) ) {
throw ( (HibernateException) e.getTargetException() );
}
throw new IntegrationException( "Error activating Bean Validation integration", e.getTargetException() );
}
catch (Exception e) {
throw new IntegrationException( "Error activating Bean Validation integration", e );
}
}
catch (NoSuchMethodException e) {
throw new HibernateException( "Unable to locate TypeSafeActivator#activate method", e );
}
}
else {
// otherwise check the validation modes
// todo : in many ways this duplicates thew checks done on the TypeSafeActivator when a ValidatorFactory could not be obtained
validateMissingBeanValidationApi( modes );
}
}
private boolean isBeanValidationApiAvailable(ClassLoaderService classLoaderService) {
try {
return valueOf( modeProperty.trim().toUpperCase() );
classLoaderService.classForName( BV_CHECK_CLASS );
return true;
}
catch ( IllegalArgumentException e ) {
throw new HibernateException( "Unknown validation mode in " + MODE_PROPERTY + ": " + modeProperty );
catch (Exception e) {
return false;
}
}
/**
* Used to validate the case when the Bean Validation API is not available.
*
* @param modes The requested validation modes.
*/
private void validateMissingBeanValidationApi(Set<ValidationMode> modes) {
if ( modes.contains( ValidationMode.CALLBACK ) ) {
throw new IntegrationException( "Bean Validation API was not available, but 'callback' validation was requested" );
}
if ( modes.contains( ValidationMode.DDL ) ) {
throw new IntegrationException( "Bean Validation API was not available, but 'ddl' validation was requested" );
}
}
private Class loadTypeSafeActivatorClass(ClassLoaderService classLoaderService) {
try {
return classLoaderService.classForName( ACTIVATOR_CLASS_NAME );
}
catch (Exception e) {
throw new HibernateException( "Unable to load TypeSafeActivator class", e );
}
}
@Override
public void integrate(
MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
}
@Override

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cfg.beanvalidation;
import org.hibernate.HibernateException;
/**
* Indicates a problem integrating Hibernate and the Bean Validation spec.
*
* @author Steve Ebersole
*/
public class IntegrationException extends HibernateException {
public IntegrationException(String message) {
super( message );
}
public IntegrationException(String message, Throwable root) {
super( message, root );
}
}

View File

@ -23,15 +23,6 @@
*/
package org.hibernate.cfg.beanvalidation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.Digits;
@ -42,19 +33,29 @@ import javax.validation.constraints.Size;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.PropertyDescriptor;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
@ -64,6 +65,7 @@ import org.hibernate.mapping.SingleTableSubclass;
/**
* @author Emmanuel Bernard
* @author Hardy Ferentschik
* @author Steve Ebersole
*/
class TypeSafeActivator {
@ -71,51 +73,97 @@ class TypeSafeActivator {
private static final String FACTORY_PROPERTY = "javax.persistence.validation.factory";
/**
* Used to validate a supplied ValidatorFactory instance as being castable to ValidatorFactory.
*
* @param object The supplied ValidatorFactory instance.
*/
@SuppressWarnings( {"UnusedDeclaration"})
public static void validateFactory(Object object) {
public static void validateSuppliedFactory(Object object) {
if ( ! ValidatorFactory.class.isInstance( object ) ) {
throw new HibernateException(
throw new IntegrationException(
"Given object was not an instance of " + ValidatorFactory.class.getName()
+ "[" + object.getClass().getName() + "]"
);
}
}
@SuppressWarnings("UnusedDeclaration")
public static void activate(ActivationContext activationContext) {
final Properties properties = activationContext.getConfiguration().getProperties();
final ValidatorFactory factory;
try {
factory = getValidatorFactory( properties );
}
catch (IntegrationException e) {
if ( activationContext.getValidationModes().contains( ValidationMode.CALLBACK ) ) {
throw new IntegrationException( "Bean Validation provider was not available, but 'callback' validation was requested", e );
}
if ( activationContext.getValidationModes().contains( ValidationMode.DDL ) ) {
throw new IntegrationException( "Bean Validation provider was not available, but 'ddl' validation was requested", e );
}
LOG.debug( "Unable to acquire Bean Validation ValidatorFactory, skipping activation" );
return;
}
applyRelationalConstraints( factory, activationContext );
applyCallbackListeners( factory, activationContext );
}
@SuppressWarnings( {"UnusedDeclaration"})
public static void activateBeanValidation(EventListenerRegistry listenerRegistry, Configuration configuration) {
final Properties properties = configuration.getProperties();
ValidatorFactory factory = getValidatorFactory( properties );
BeanValidationEventListener listener = new BeanValidationEventListener(
factory, properties
public static void applyCallbackListeners(ValidatorFactory validatorFactory, ActivationContext activationContext) {
final Set<ValidationMode> modes = activationContext.getValidationModes();
if ( ! ( modes.contains( ValidationMode.CALLBACK ) || modes.contains( ValidationMode.AUTO ) ) ) {
return;
}
// de-activate not-null tracking at the core level when Bean Validation is present unless the user explicitly
// asks for it
if ( activationContext.getConfiguration().getProperty( Environment.CHECK_NULLABILITY ) == null ) {
activationContext.getSessionFactory().getSettings().setCheckNullability( false );
}
final BeanValidationEventListener listener = new BeanValidationEventListener(
validatorFactory,
activationContext.getConfiguration().getProperties()
);
final EventListenerRegistry listenerRegistry = activationContext.getServiceRegistry()
.getService( EventListenerRegistry.class );
listenerRegistry.addDuplicationStrategy( DuplicationStrategyImpl.INSTANCE );
listenerRegistry.appendListeners( EventType.PRE_INSERT, listener );
listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
listener.initialize( configuration );
listener.initialize( activationContext.getConfiguration() );
}
// public static void activateBeanValidation( EventListenerRegistry listenerRegistry ) {
// final Properties properties = configuration.getProperties();
// ValidatorFactory factory = getValidatorFactory( properties );
// BeanValidationEventListener listener = new BeanValidationEventListener(
// factory, properties
// );
//
// listenerRegistry.addDuplicationStrategy( DuplicationStrategyImpl.INSTANCE );
//
// listenerRegistry.appendListeners( EventType.PRE_INSERT, listener );
// listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
// listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
//
// listener.initialize( configuration );
// }
@SuppressWarnings({"unchecked", "UnusedParameters"})
private static void applyRelationalConstraints(ValidatorFactory factory, ActivationContext activationContext) {
final Properties properties = activationContext.getConfiguration().getProperties();
if ( ! ConfigurationHelper.getBoolean( BeanValidationIntegrator.APPLY_CONSTRAINTS, properties, true ) ){
LOG.debug( "Skipping application of relational constraints from legacy Hibernate Validator" );
return;
}
final Set<ValidationMode> modes = activationContext.getValidationModes();
if ( ! ( modes.contains( ValidationMode.DDL ) || modes.contains( ValidationMode.AUTO ) ) ) {
return;
}
applyRelationalConstraints(
activationContext.getConfiguration().createMappings().getClasses().values(),
properties,
activationContext.getServiceRegistry().getService( JdbcServices.class ).getDialect()
);
}
@SuppressWarnings( {"UnusedDeclaration"})
public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties, Dialect dialect) {
public static void applyRelationalConstraints(Collection<PersistentClass> persistentClasses, Properties properties, Dialect dialect) {
ValidatorFactory factory = getValidatorFactory( properties );
Class<?>[] groupsArray = new GroupsPerOperation( properties ).get( GroupsPerOperation.Operation.DDL );
Set<Class<?>> groups = new HashSet<Class<?>>( Arrays.asList( groupsArray ) );
@ -143,26 +191,8 @@ class TypeSafeActivator {
}
}
// public static void applyDDL( Iterable<EntityBinding> bindings,
// Properties properties,
// ClassLoaderService classLoaderService ) {
// ValidatorFactory factory = getValidatorFactory(properties);
// Class<?>[] groupsArray = new GroupsPerOperation(properties).get(GroupsPerOperation.Operation.DDL);
// Set<Class<?>> groups = new HashSet<Class<?>>(Arrays.asList(groupsArray));
// for (EntityBinding binding : bindings) {
// final String className = binding.getEntity().getClassName();
// if (className == null || className.length() == 0) continue;
// try {
// applyDDL("", binding, classLoaderService.classForName(className), factory, groups, true);
// } catch (ClassLoadingException error) {
// throw new AssertionFailure("Entity class not found", error);
// } catch (Exception error) {
// LOG.unableToApplyConstraints(className, error);
// }
// }
// }
private static void applyDDL(String prefix,
private static void applyDDL(
String prefix,
PersistentClass persistentClass,
Class<?> clazz,
ValidatorFactory factory,
@ -200,30 +230,13 @@ class TypeSafeActivator {
}
}
// private static void applyDDL( String prefix,
// EntityBinding binding,
// Class<?> clazz,
// ValidatorFactory factory,
// Set<Class<?>> groups,
// boolean activateNotNull ) {
// final BeanDescriptor descriptor = factory.getValidator().getConstraintsForClass(clazz);
// //no bean level constraints can be applied, go to the properties
// for (PropertyDescriptor propertyDesc : descriptor.getConstrainedProperties()) {
// AttributeBinding attrBinding = findAttributeBindingByName(binding, prefix + propertyDesc.getPropertyName());
// if (attrBinding != null) {
// applyConstraints(propertyDesc.getConstraintDescriptors(), attrBinding, propertyDesc, groups, activateNotNull);
// // TODO: Handle composite attributes when possible
// }
// }
// }
private static boolean applyConstraints(Set<ConstraintDescriptor<?>> constraintDescriptors,
private static boolean applyConstraints(
Set<ConstraintDescriptor<?>> constraintDescriptors,
Property property,
PropertyDescriptor propertyDesc,
Set<Class<?>> groups,
boolean canApplyNotNull,
Dialect dialect
) {
Dialect dialect) {
boolean hasNotNull = false;
for ( ConstraintDescriptor<?> descriptor : constraintDescriptors ) {
if ( groups != null && Collections.disjoint( descriptor.getGroups(), groups ) ) {
@ -256,37 +269,6 @@ class TypeSafeActivator {
return hasNotNull;
}
// private static boolean applyConstraints( Set<ConstraintDescriptor<?>> constraintDescriptors,
// AttributeBinding attributeBinding,
// PropertyDescriptor propertyDesc,
// Set<Class<?>> groups,
// boolean canApplyNotNull ) {
// boolean hasNotNull = false;
// for ( ConstraintDescriptor<?> descriptor : constraintDescriptors ) {
// if (groups != null && Collections.disjoint(descriptor.getGroups(), groups)) continue;
// if (canApplyNotNull) hasNotNull = hasNotNull || applyNotNull(attributeBinding, descriptor);
//
// // apply bean validation specific constraints
// applyDigits( property, descriptor );
// applySize( property, descriptor, propertyDesc );
// applyMin( property, descriptor );
// applyMax( property, descriptor );
//
// // apply hibernate validator specific constraints - we cannot import any HV specific classes though!
// // no need to check explicitly for @Range. @Range is a composed constraint using @Min and @Max which
// // will be taken care later
// applyLength( property, descriptor, propertyDesc );
//
// // pass an empty set as composing constraints inherit the main constraint and thus are matching already
// hasNotNull = hasNotNull || applyConstraints(
// descriptor.getComposingConstraints(),
// property, propertyDesc, null,
// canApplyNotNull
// );
// }
// return hasNotNull;
// }
private static void applyMin(Property property, ConstraintDescriptor<?> descriptor, Dialect dialect) {
if ( Min.class.equals( descriptor.getAnnotation().annotationType() ) ) {
@SuppressWarnings("unchecked")
@ -339,26 +321,6 @@ class TypeSafeActivator {
return hasNotNull;
}
// private static boolean applyNotNull( AttributeBinding attributeBinding,
// ConstraintDescriptor<?> descriptor ) {
// boolean hasNotNull = false;
// if (NotNull.class.equals(descriptor.getAnnotation().annotationType())) {
// if ( !( attributeBinding.getPersistentClass() instanceof SingleTableSubclass ) ) {
// //single table should not be forced to null
// if ( !property.isComposite() ) { //composite should not add not-null on all columns
// @SuppressWarnings( "unchecked" )
// Iterator<Column> iter = property.getColumnIterator();
// while ( iter.hasNext() ) {
// iter.next().setNullable( false );
// hasNotNull = true;
// }
// }
// }
// hasNotNull = true;
// }
// return hasNotNull;
// }
private static void applyDigits(Property property, ConstraintDescriptor<?> descriptor) {
if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) {
@SuppressWarnings("unchecked")
@ -462,44 +424,6 @@ class TypeSafeActivator {
return property;
}
// /**
// * @param entityBinding
// * @param attrName
// * @return the attribute by path in a recursive way, including EntityIdentifier in the loop if attrName is
// * <code>null</code>. If attrName is <code>null</code> or empty, the EntityIdentifier is returned
// */
// private static AttributeBinding findAttributeBindingByName( EntityBinding entityBinding,
// String attrName ) {
// AttributeBinding attrBinding = null;
// EntityIdentifier identifier = entityBinding.getHierarchyDetails().getEntityIdentifier();
// BasicAttributeBinding idAttrBinding = identifier.getValueBinding();
// String idAttrName = idAttrBinding != null ? idAttrBinding.getAttribute().getName() : null;
// try {
// if (attrName == null || attrName.length() == 0 || attrName.equals(idAttrName)) attrBinding = idAttrBinding; // default to id
// else {
// if (attrName.indexOf(idAttrName + ".") == 0) {
// attrBinding = idAttrBinding;
// attrName = attrName.substring(idAttrName.length() + 1);
// }
// for (StringTokenizer st = new StringTokenizer(attrName, "."); st.hasMoreElements();) {
// String element = st.nextToken();
// if (attrBinding == null) attrBinding = entityBinding.locateAttributeBinding(element);
// else return null; // TODO: if (attrBinding.isComposite()) ...
// }
// }
// } catch (MappingException error) {
// try {
// //if we do not find it try to check the identifier mapper
// if (!identifier.isIdentifierMapper()) return null;
// // TODO: finish once composite/embedded/component IDs get worked out
// }
// catch ( MappingException ee ) {
// return null;
// }
// }
// return attrBinding;
// }
private static ValidatorFactory getValidatorFactory(Map<Object, Object> properties) {
ValidatorFactory factory = null;
if ( properties != null ) {
@ -509,7 +433,7 @@ class TypeSafeActivator {
factory = ValidatorFactory.class.cast( unsafeProperty );
}
catch ( ClassCastException e ) {
throw new HibernateException(
throw new IntegrationException(
"Property " + FACTORY_PROPERTY
+ " should contain an object of type " + ValidatorFactory.class.getName()
);
@ -521,7 +445,7 @@ class TypeSafeActivator {
factory = Validation.buildDefaultValidatorFactory();
}
catch ( Exception e ) {
throw new HibernateException( "Unable to build the default ValidatorFactory", e );
throw new IntegrationException( "Unable to build the default ValidatorFactory", e );
}
}
return factory;

View File

@ -0,0 +1,91 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cfg.beanvalidation;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.HibernateException;
/**
* Duplicates the javax.validation enum (because javax validation might not be on the runtime classpath)
*
* @author Steve Ebersole
*/
public enum ValidationMode {
AUTO( "auto" ),
CALLBACK( "callback" ),
NONE( "none" ),
DDL( "ddl" );
private final String externalForm;
private ValidationMode(String externalForm) {
this.externalForm = externalForm;
}
public static Set<ValidationMode> getModes(Object modeProperty) {
Set<ValidationMode> modes = new HashSet<ValidationMode>(3);
if (modeProperty == null) {
modes.add( ValidationMode.AUTO );
}
else {
final String[] modesInString = modeProperty.toString().split( "," );
for ( String modeInString : modesInString ) {
modes.add( getMode(modeInString) );
}
}
if ( modes.size() > 1 && ( modes.contains( ValidationMode.AUTO ) || modes.contains( ValidationMode.NONE ) ) ) {
throw new HibernateException( "Incompatible validation modes mixed: " + loggable( modes ) );
}
return modes;
}
private static ValidationMode getMode(String modeProperty) {
if (modeProperty == null || modeProperty.length() == 0) {
return AUTO;
}
else {
try {
return valueOf( modeProperty.trim().toUpperCase() );
}
catch ( IllegalArgumentException e ) {
throw new HibernateException( "Unknown validation mode in " + BeanValidationIntegrator.MODE_PROPERTY + ": " + modeProperty );
}
}
}
public static String loggable(Set<ValidationMode> modes) {
if ( modes == null || modes.isEmpty() ) {
return "[<empty>]";
}
StringBuilder buffer = new StringBuilder( "[" );
String sep = "";
for ( ValidationMode mode : modes ) {
buffer.append( sep ).append( mode.externalForm );
sep = ", ";
}
return buffer.append( "]" ).toString();
}
}

View File

@ -1609,4 +1609,7 @@ public interface CoreMessageLogger extends BasicLogger {
)
void embedXmlAttributesNoLongerSupported();
@LogMessage(level = INFO)
@Message( value = "'javax.persistence.validation.mode' named multiple values : %s", id = 447 )
void multipleValidationModes(String modes);
}