org.hibernate.cfg.beanvalidation.BeanValidationIntegrator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.cfg.beanvalidation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class BeanValidationIntegrator implements Integrator {
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_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 {
// 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_SUPPLIED_FACTORY_METHOD_NAME, Object.class );
try {
validateMethod.invoke( null, object );
}
catch (InvocationTargetException e) {
if ( e.getTargetException() instanceof HibernateException ) {
throw (HibernateException) e.getTargetException();
}
throw new HibernateException( "Unable to check validity of passed ValidatorFactory", e );
}
catch (IllegalAccessException e) {
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 );
}
}
@Override
public void integrate(
final Metadata metadata,
final SessionFactoryImplementor sessionFactory,
final SessionFactoryServiceRegistry serviceRegistry) {
final ConfigurationService cfgService = serviceRegistry.getService( ConfigurationService.class );
// IMPL NOTE : see the comments on ActivationContext.getValidationModes() as to why this is multi-valued...
final Set modes = ValidationMode.getModes( cfgService.getSettings().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 );
// 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 Metadata getMetadata() {
return metadata;
}
@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 );
return true;
}
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 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 disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
// nothing to do here afaik
}
}