diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/ActivationContext.java b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/ActivationContext.java
new file mode 100644
index 0000000000..b80054e95f
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/ActivationContext.java
@@ -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).
+ *
+ * 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 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();
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationIntegrator.java b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationIntegrator.java
index 927e4d6f8f..7aa074000d 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationIntegrator.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationIntegrator.java
@@ -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 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 {
+ final Class typeSafeActivatorClass = loadTypeSafeActivatorClass( classLoaderService );
+ @SuppressWarnings("unchecked")
+ final Method activateMethod = typeSafeActivatorClass.getMethod( ACTIVATE_METHOD_NAME, ActivationContext.class );
+ final ActivationContext activationContext = new ActivationContext() {
+ @Override
+ public Set getValidationModes() {
+ return modes;
+ }
+
+ @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 {
classLoaderService.classForName( BV_CHECK_CLASS );
- isBeanValidationAvailable = true;
+ return true;
}
- catch ( Exception e ) {
- isBeanValidationAvailable = false;
+ catch (Exception e) {
+ return 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}
+ * Used to validate the case when the Bean Validation API is not available.
*
- * @see org.hibernate.integrator.spi.Integrator#integrate(org.hibernate.metamodel.source.MetadataImplementor, org.hibernate.engine.spi.SessionFactoryImplementor, org.hibernate.service.spi.SessionFactoryServiceRegistry)
+ * @param modes The requested validation modes.
*/
+ private void validateMissingBeanValidationApi(Set 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 ) {
-// Properties props = sessionFactory.getProperties();
-// final Set 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 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 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 modes,
- boolean beanValidationAvailable,
- Class typeSafeActivatorClass,
- Configuration configuration,
+ public void integrate(
+ MetadataImplementor metadata,
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 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 getModes(Object modeProperty) {
- Set modes = new HashSet(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 ) );
- }
- 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 " + MODE_PROPERTY + ": " + modeProperty );
- }
- }
- }
+ SessionFactoryServiceRegistry serviceRegistry ) {
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/IntegrationException.java b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/IntegrationException.java
new file mode 100644
index 0000000000..782fae2e1c
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/IntegrationException.java
@@ -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 );
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java
index d4bba1cec4..fb5733adc8 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java
@@ -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 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 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 persistentClasses, Properties properties, Dialect dialect) {
+ public static void applyRelationalConstraints(Collection persistentClasses, Properties properties, Dialect dialect) {
ValidatorFactory factory = getValidatorFactory( properties );
Class>[] groupsArray = new GroupsPerOperation( properties ).get( GroupsPerOperation.Operation.DDL );
Set> groups = new HashSet>( Arrays.asList( groupsArray ) );
@@ -143,32 +191,14 @@ class TypeSafeActivator {
}
}
-// public static void applyDDL( Iterable bindings,
-// Properties properties,
-// ClassLoaderService classLoaderService ) {
-// ValidatorFactory factory = getValidatorFactory(properties);
-// Class>[] groupsArray = new GroupsPerOperation(properties).get(GroupsPerOperation.Operation.DDL);
-// Set> groups = new HashSet>(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,
- PersistentClass persistentClass,
- Class> clazz,
- ValidatorFactory factory,
- Set> groups,
- boolean activateNotNull,
- Dialect dialect) {
+ private static void applyDDL(
+ String prefix,
+ PersistentClass persistentClass,
+ Class> clazz,
+ ValidatorFactory factory,
+ Set> groups,
+ boolean activateNotNull,
+ Dialect dialect) {
final BeanDescriptor descriptor = factory.getValidator().getConstraintsForClass( clazz );
//no bean level constraints can be applied, go to the properties
@@ -200,30 +230,13 @@ class TypeSafeActivator {
}
}
-// private static void applyDDL( String prefix,
-// EntityBinding binding,
-// Class> clazz,
-// ValidatorFactory factory,
-// Set> 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> constraintDescriptors,
- Property property,
- PropertyDescriptor propertyDesc,
- Set> groups,
- boolean canApplyNotNull,
- Dialect dialect
- ) {
+ private static boolean applyConstraints(
+ Set> constraintDescriptors,
+ Property property,
+ PropertyDescriptor propertyDesc,
+ Set> groups,
+ boolean canApplyNotNull,
+ 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> constraintDescriptors,
-// AttributeBinding attributeBinding,
-// PropertyDescriptor propertyDesc,
-// Set> 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 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
-// * null. If attrName is null 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