HHH-7470 Re-enabling bean validation integration for new metamodel

This commit is contained in:
Hardy Ferentschik 2012-07-26 12:38:59 +02:00
parent 0f77803b9c
commit 1d53de8262
3 changed files with 233 additions and 247 deletions

View File

@ -22,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cfg.beanvalidation;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
@ -36,7 +37,6 @@ import javax.validation.ValidatorFactory;
import org.jboss.logging.Logger;
import org.hibernate.EntityMode;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.spi.PreDeleteEvent;
import org.hibernate.event.spi.PreDeleteEventListener;
@ -53,12 +53,13 @@ import org.hibernate.persister.entity.EntityPersister;
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
//FIXME review exception model
public class BeanValidationEventListener
implements PreInsertEventListener, PreUpdateEventListener, PreDeleteEventListener {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
BeanValidationEventListener.class.getName());
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
BeanValidationEventListener.class.getName()
);
private ValidatorFactory factory;
private ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister =
@ -76,17 +77,16 @@ public class BeanValidationEventListener
* Constructor used in an environment where validator factory is injected (JPA2).
*
* @param factory The {@code ValidatorFactory} to use to create {@code Validator} instance(s)
* @param properties Configued properties
* @param properties Configured properties
*/
public BeanValidationEventListener(ValidatorFactory factory, Properties properties) {
init( factory, properties );
}
public void initialize(Configuration cfg) {
public void initialize(Properties properties) {
if ( !initialized ) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Properties props = cfg.getProperties();
init( factory, props );
init( factory, properties );
}
}
@ -135,6 +135,12 @@ public class BeanValidationEventListener
if ( groups.length > 0 ) {
final Set<ConstraintViolation<T>> constraintViolations = validator.validate( object, groups );
if ( constraintViolations.size() > 0 ) {
throw createConstraintViolationException( operation, groups, constraintViolations );
}
}
}
private <T> ConstraintViolationException createConstraintViolationException(GroupsPerOperation.Operation operation, Class<?>[] groups, Set<ConstraintViolation<T>> constraintViolations) {
Set<ConstraintViolation<?>> propagatedViolations =
new HashSet<ConstraintViolation<?>>( constraintViolations.size() );
Set<String> classNames = new HashSet<String>();
@ -156,12 +162,10 @@ public class BeanValidationEventListener
}
builder.append( "]" );
throw new ConstraintViolationException(
return new ConstraintViolationException(
builder.toString(), propagatedViolations
);
}
}
}
private String toString(Class<?>[] groups) {
StringBuilder toString = new StringBuilder( "[" );

View File

@ -50,12 +50,13 @@ 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";
public static final String BV_CHECK_CLASS = "javax.validation.Validation";
public static final String MODE_PROPERTY = "javax.persistence.validation.mode";
private static final String ACTIVATOR_CLASS = "org.hibernate.cfg.beanvalidation.TypeSafeActivator";
@ -63,14 +64,89 @@ public class BeanValidationIntegrator implements Integrator {
private static final String ACTIVATE_METHOD = "activateBeanValidation";
private static final String VALIDATE_METHOD = "validateFactory";
public static void validateFactory(Object object) {
try {
final Class activatorClass = BeanValidationIntegrator.class.getClassLoader().loadClass( ACTIVATOR_CLASS );
try {
final Method validateMethod = activatorClass.getMethod( VALIDATE_METHOD, Object.class );
if ( ! validateMethod.isAccessible() ) {
validateMethod.setAccessible( true );
@Override
// TODO Can be removed once the switch to the new metamodel is complete. See also HHH-7470 (HF)
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
final Set<ValidationMode> modes = ValidationMode.getModes( configuration.getProperties().get( MODE_PROPERTY ) );
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
final Dialect dialect = serviceRegistry.getService( JdbcServices.class ).getDialect();
final boolean isBeanValidationAvailable = isBeanValidationOnClasspath( classLoaderService );
final Class typeSafeActivatorClass = loadTypeSafeActivatorClass( serviceRegistry );
applyRelationalConstraints(
modes,
isBeanValidationAvailable,
typeSafeActivatorClass,
configuration,
dialect
);
applyHibernateListeners(
modes,
isBeanValidationAvailable,
typeSafeActivatorClass,
sessionFactory,
serviceRegistry
);
}
@Override
public void integrate(MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
final Set<ValidationMode> modes = ValidationMode.getModes(
sessionFactory.getProperties()
.get( MODE_PROPERTY )
);
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
final boolean isBeanValidationAvailable = isBeanValidationOnClasspath( classLoaderService );
final Class typeSafeActivatorClass = loadTypeSafeActivatorClass( serviceRegistry );
// TODO apply relational constraints. Depends on HHH-7472 (HF)
// applyRelationalConstraints( modes, isBeanValidationAvailable, typeSafeActivatorClass, props, metadata );
applyHibernateListeners(
modes,
isBeanValidationAvailable,
typeSafeActivatorClass,
sessionFactory,
serviceRegistry
);
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
// nothing to do here afaik
}
public static void validateFactory(Object object) {
Class activatorClass;
try {
activatorClass = BeanValidationIntegrator.class.getClassLoader().loadClass( ACTIVATOR_CLASS );
}
catch ( HibernateException e ) {
throw e;
}
catch ( Exception e ) {
throw new HibernateException( "Could not locate TypeSafeActivator class", e );
}
try {
final Method validateMethod = getValidateMethod( activatorClass );
invokeValidateMethod( object, validateMethod );
}
catch ( HibernateException e ) {
throw e;
}
catch ( Exception e ) {
throw new HibernateException( "Could not locate method needed for ValidatorFactory validation", e );
}
}
private static void invokeValidateMethod(Object object, Method validateMethod) {
try {
validateMethod.invoke( null, object );
}
@ -84,88 +160,33 @@ public class BeanValidationIntegrator implements Integrator {
throw new HibernateException( "Unable to check validity of passed ValidatorFactory", e );
}
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new HibernateException( "Could not locate method needed for ValidatorFactory validation", e );
}
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new HibernateException( "Could not locate TypeSafeActivator class", e );
private static Method getValidateMethod(Class activatorClass) throws NoSuchMethodException {
final Method validateMethod = activatorClass.getMethod( VALIDATE_METHOD, Object.class );
if ( !validateMethod.isAccessible() ) {
validateMethod.setAccessible( true );
}
return validateMethod;
}
@Override
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
// determine requested validation modes.
final Set<ValidationMode> modes = ValidationMode.getModes( configuration.getProperties().get( MODE_PROPERTY ) );
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
*
* @param classLoaderService the class loader service
*
* @return {@code true} if the Bean Validation classes are on the classpath, {@code false otherwise}
*/
private boolean isBeanValidationOnClasspath(ClassLoaderService classLoaderService) {
// 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 e ) {
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,
configuration,
dialect
);
applyHibernateListeners(
modes,
isBeanValidationAvailable,
typeSafeActivatorClass,
configuration,
sessionFactory,
serviceRegistry
);
}
/**
* {@inheritDoc}
*
* @see org.hibernate.integrator.spi.Integrator#integrate(org.hibernate.metamodel.spi.source.MetadataImplementor, org.hibernate.engine.spi.SessionFactoryImplementor, org.hibernate.service.spi.SessionFactoryServiceRegistry)
*/
@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);
return isBeanValidationAvailable;
}
private Class loadTypeSafeActivatorClass(SessionFactoryServiceRegistry serviceRegistry) {
@ -203,7 +224,12 @@ public class BeanValidationIntegrator implements Integrator {
}
try {
Method applyDDLMethod = typeSafeActivatorClass.getMethod( DDL_METHOD, Collection.class, Properties.class, Dialect.class );
Method applyDDLMethod = typeSafeActivatorClass.getMethod(
DDL_METHOD,
Collection.class,
Properties.class,
Dialect.class
);
try {
applyDDLMethod.invoke(
null,
@ -227,72 +253,86 @@ public class BeanValidationIntegrator implements Integrator {
}
}
// 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,
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 ) {
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 );
}
else if (modes.contains( ValidationMode.AUTO ) ) {
//nothing to activate
return;
if ( modes.contains( ValidationMode.AUTO ) ) {
return; //nothing to activate
}
}
try {
Method activateMethod = typeSafeActivatorClass.getMethod( ACTIVATE_METHOD, EventListenerRegistry.class, Configuration.class );
Method activateMethod = typeSafeActivatorClass.getMethod(
ACTIVATE_METHOD,
EventListenerRegistry.class,
Properties.class
);
try {
activateMethod.invoke(
null,
serviceRegistry.getService( EventListenerRegistry.class ),
configuration
sessionFactory.getProperties()
);
}
catch ( HibernateException e ) {
@ -310,41 +350,6 @@ public class BeanValidationIntegrator implements Integrator {
}
}
// 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,
@ -387,9 +392,4 @@ public class BeanValidationIntegrator implements Integrator {
}
}
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
// nothing to do here afaik
}
}

View File

@ -48,7 +48,6 @@ 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.dialect.Dialect;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
@ -82,8 +81,7 @@ class TypeSafeActivator {
}
@SuppressWarnings( {"UnusedDeclaration"})
public static void activateBeanValidation(EventListenerRegistry listenerRegistry, Configuration configuration) {
final Properties properties = configuration.getProperties();
public static void activateBeanValidation(EventListenerRegistry listenerRegistry, Properties properties) {
ValidatorFactory factory = getValidatorFactory( properties );
BeanValidationEventListener listener = new BeanValidationEventListener(
factory, properties
@ -95,25 +93,9 @@ class TypeSafeActivator {
listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
listener.initialize( configuration );
listener.initialize( properties );
}
// 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( {"UnusedDeclaration"})
public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties, Dialect dialect) {
ValidatorFactory factory = getValidatorFactory( properties );