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

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

/*
 * 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.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;

/**
 * @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 = "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";

	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 );
				}
				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(
			Configuration configuration,
			SessionFactoryImplementor sessionFactory,
			SessionFactoryServiceRegistry serviceRegistry) {
		// determine requested validation modes.
		final Set 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
		boolean isBeanValidationAvailable;
		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)
	 */
	@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,
			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 );
				}
			}
		}
	}

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy