/*
* Copyright 2008, Unitils.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.unitils.orm.hibernate;
import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.unitils.util.PropertyUtils.getString;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.Set;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Dialect;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.unitils.core.Module;
import org.unitils.core.TestListener;
import org.unitils.core.UnitilsException;
import org.unitils.database.DataSourceWrapper;
import org.unitils.database.transaction.impl.UnitilsTransactionManagementConfiguration;
import org.unitils.database.util.Flushable;
import org.unitils.orm.common.OrmModule;
import org.unitils.orm.common.util.ConfiguredOrmPersistenceUnit;
import org.unitils.orm.common.util.OrmConfig;
import org.unitils.orm.common.util.OrmPersistenceUnitLoader;
import org.unitils.orm.hibernate.annotation.HibernateSessionFactory;
import org.unitils.orm.hibernate.util.HibernateAnnotationConfigLoader;
import org.unitils.orm.hibernate.util.HibernateAssert;
import org.unitils.orm.hibernate.util.HibernateSessionFactoryLoader;
import org.unitils.orm.jpa.annotation.JpaEntityManagerFactory;
import org.unitils.util.AnnotationUtils;
import org.unitils.util.ReflectionUtils;
/**
* Module providing support for unit tests for code that uses Hibernate. It offers an easy way of loading hibernate
* SessionFactories and having them injected them into the test. It also offers a test to check whether the hibernate
* mapping is consistent with the structure of the database.
* <p/>
* A Hibernate <code>SessionFactory</code> is created when requested and injected into all fields or methods of the test
* annotated with {@link HibernateSessionFactory}.
* <p/>
* It is highly recommended to write a unit test that invokes {@link HibernateUnitils#assertMappingWithDatabaseConsistent()},
* This is a very useful test that verifies whether the mapping of all your Hibernate mapped objects still corresponds
* with the actual structure of the database.
*
* @author Filip Neven
* @author Tim Ducheyne
*/
public class HibernateModule extends OrmModule<SessionFactory, Session, Configuration, HibernateSessionFactory, OrmConfig, HibernateAnnotationConfigLoader> implements Module, Flushable {
/* Property that defines the class name of the hibernate configuration */
public static final String PROPKEY_CONFIGURATION_CLASS_NAME = "HibernateModule.configuration.implClassName";
/* The logger instance for this class */
private static Log logger = LogFactory.getLog(HibernateModule.class);
/**
* Subclass of org.hibernate.cfg.Configuration that is used for configuring hibernate
*/
private Class<? extends Configuration> configurationObjectClass;
/**
* @param configuration The Unitils configuration, not null
*/
public void init(Properties configuration) {
super.init(configuration);
String configurationImplClassName = getString(PROPKEY_CONFIGURATION_CLASS_NAME, configuration);
configurationObjectClass = ReflectionUtils.getClassWithName(configurationImplClassName);
}
public void afterInit() {
super.afterInit();
}
public void registerTransactionManagementConfiguration() {
// Make sure that a spring HibernateTransactionManager is used for transaction management in the database module, if the
// current test object defines a hibernate SessionFactory
for (final DataSourceWrapper wrapper : wrappers) {
getDatabaseModule().registerTransactionManagementConfiguration(new UnitilsTransactionManagementConfiguration() {
public boolean isApplicableFor(Object testObject) {
return isPersistenceUnitConfiguredFor(testObject);
}
public PlatformTransactionManager getSpringPlatformTransactionManager(Object testObject) {
SessionFactory sessionFactory = getPersistenceUnit(testObject);
HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager(sessionFactory);
hibernateTransactionManager.setDataSource(getDataSource());
return hibernateTransactionManager;
}
public boolean isTransactionalResourceAvailable(Object testObject) {
return wrapper.isDataSourceLoaded();
}
public Integer getPreference() {
return 10;
}
});
}
}
@Override
protected HibernateAnnotationConfigLoader createOrmConfigLoader() {
return new HibernateAnnotationConfigLoader();
}
@Override
protected Class<HibernateSessionFactory> getPersistenceUnitConfigAnnotationClass() {
return HibernateSessionFactory.class;
}
@Override
protected Class<SessionFactory> getPersistenceUnitClass() {
return SessionFactory.class;
}
@Override
protected OrmPersistenceUnitLoader<SessionFactory, Configuration, OrmConfig> createOrmPersistenceUnitLoader() {
return new HibernateSessionFactoryLoader(databaseName);
}
@Override
protected String getOrmSpringSupportImplClassName() {
return "org.unitils.orm.hibernate.util.HibernateSpringSupport";
}
@Override
protected Session doGetPersistenceContext(Object testObject) {
return SessionFactoryUtils.getSession(getPersistenceUnit(testObject), true);
}
@Override
protected Session doGetActivePersistenceContext(Object testObject) {
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(getPersistenceUnit(testObject));
if (sessionHolder != null && sessionHolder.getSession() != null && sessionHolder.getSession().isOpen()) {
return sessionHolder.getSession();
}
return null;
}
@Override
protected void flushOrmPersistenceContext(Session activeSession) {
logger.info("Flushing session " + activeSession);
activeSession.flush();
}
/**
* Checks if the mapping of the Hibernate managed objects with the database is correct.
*
* @param testObject The test instance, not null
*/
public void assertMappingWithDatabaseConsistent(Object testObject) {
ConfiguredOrmPersistenceUnit<SessionFactory, Configuration> configuredPersistenceUnit = getConfiguredPersistenceUnit(testObject);
Configuration configuration = configuredPersistenceUnit.getOrmConfigurationObject();
Session session = getPersistenceContext(testObject);
Dialect databaseDialect = getDatabaseDialect(configuration);
HibernateAssert.assertMappingWithDatabaseConsistent(configuration, session, databaseDialect);
}
/**
* @return The subclass of <code>org.hibernate.cfg.Configuration</code> that is used for configuring hibernate
*/
public Class<? extends Configuration> getConfigurationObjectClass() {
return configurationObjectClass;
}
/**
* Gets the database dialect from the given Hibernate <code>Configuration</code.
*
* @param configuration The hibernate config, not null
* @return the database Dialect, not null
*/
protected Dialect getDatabaseDialect(Configuration configuration) {
String dialectClassName = configuration.getProperty("hibernate.dialect");
if (isEmpty(dialectClassName)) {
throw new UnitilsException("Property hibernate.dialect not specified");
}
try {
return (Dialect) Class.forName(dialectClassName).newInstance();
} catch (Exception e) {
throw new UnitilsException("Could not instantiate dialect class " + dialectClassName, e);
}
}
protected DataSource getDataSource() {
return getDatabaseModule().getWrapper(databaseName).getDataSourceAndActivateTransactionIfNeeded();
}
/**
* @return The TestListener associated with this module
*/
public TestListener getTestListener() {
return new HibernateTestListener();
}
/**
* The {@link TestListener} for this module
*/
protected class HibernateTestListener extends OrmTestListener {
/**
* @see org.unitils.core.TestListener#beforeTestMethod(java.lang.Object, java.lang.reflect.Method)
*/
@Override
public void beforeTestMethod(Object testObject, Method testMethod) {
//databaseName = getDatabaseName(testObject, testMethod);
registerTransactionManagementConfiguration();
super.beforeTestMethod(testObject, testMethod);
}
}
/**
* @see org.unitils.orm.common.OrmModule#getDatabaseName(java.lang.Object, java.lang.reflect.Method)
*/
@Override
protected void getDatabaseName(Object testObject, Method testMethod) {
//List<String> dataSources = new ArrayList<String>();
HibernateSessionFactory dataSource = AnnotationUtils.getMethodOrClassLevelAnnotation(HibernateSessionFactory.class, testMethod, testObject.getClass());
if (dataSource != null) {
wrappers.add(getDatabaseModule().getWrapper(dataSource.databaseName()));
//dataSources.add(dataSource.databaseName());
}
Set<HibernateSessionFactory> lstDataSources = AnnotationUtils.getFieldLevelAnnotations(testObject.getClass(), HibernateSessionFactory.class);
if (!lstDataSources.isEmpty()) {
for (HibernateSessionFactory testDataSource : lstDataSources) {
//ataSources.add(testDataSource.databaseName());
wrappers.add(getDatabaseModule().getWrapper(testDataSource.databaseName()));
}
}
//return dataSources;
}
}