All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.cfg.beanvalidation.BeanValidationIntegrator Maven / Gradle / Ivy

There is a newer version: 6.5.0.CR2
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2011, 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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;

import org.jboss.logging.Logger;

import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.classloading.spi.ClassLoaderService;
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()
	);

	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 );
				if ( ! validateMethod.isAccessible() ) {
					validateMethod.setAccessible( true );
				}
				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 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 );

		// 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 );
			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 integrate(
			MetadataImplementor metadata,
			SessionFactoryImplementor sessionFactory,
			SessionFactoryServiceRegistry serviceRegistry ) {
	}

	@Override
	public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
		// nothing to do here afaik
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy