/** * Copyright 2011 meltmedia * * 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.xchain.framework.hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.cfg.Environment; import org.hibernate.cfg.DefaultComponentSafeNamingStrategy; import org.xchain.annotations.Function; import org.xchain.framework.lifecycle.LifecycleClass; import org.xchain.framework.lifecycle.LifecycleContext; import org.xchain.framework.lifecycle.StartStep; import org.xchain.framework.lifecycle.StopStep; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.HashSet; import javax.xml.namespace.QName; /** * Manages Hibernate. * * @author Mike Moulton * @author Devon Tackett * @author Christian Trimble * @author John Trimble * @author Josh Kennedy */ @LifecycleClass(uri="http://www.xchain.org/hibernate/1.0") public class HibernateLifecycle { public static final QName DEFAULT_NAME = new QName("http://www.xchain.org/hibernate", "session-factory"); private static Logger log = LoggerFactory.getLogger(HibernateLifecycle.class); private static boolean started = false; private static Map<QName, SessionFactory> sessionFactoryMap = new HashMap<QName, SessionFactory>(); private static Map<QName, Configuration> configurationMap = new HashMap<QName, Configuration>(); private static Map<SessionFactory, QName> sessionFactoryToQName = new HashMap<SessionFactory, QName>(); private static Map<Configuration, QName> configurationToQName = new HashMap<Configuration, QName>(); private static boolean cleanUp = false; /** * Configures a hibernate using the default hibernate configuration file * located on the classpath. */ @StartStep(localName="hibernate", after="config") public static void startLifecycle() { if( log.isDebugEnabled() ) { log.debug( "Starting lifecycle step: hibernate" ); } synchronized( HibernateLifecycle.class ) { if (!started) { if ( log.isDebugEnabled() ) { log.debug("Starting HibernateLifecycle"); } try { for( Map.Entry<QName, Configuration> entry : configurationMap.entrySet() ) { if( entry.getValue() != null ) { //setXChainDefaults(entry.getKey(), entry.getValue()); mapSessionFactory(entry.getKey(), entry.getValue().buildSessionFactory()); } } started = true; } catch (RuntimeException ex) { if( log.isErrorEnabled() ) { log.error("Starting HibernateLifecycle failed", ex); } throw ex; } } } } /** * Shutdown the session factory to release all resources. */ @StopStep(localName="hibernate") public static void stopLifecycle() { if( log.isDebugEnabled() ) { log.debug( "Stopping lifecycle step: hibernate" ); } synchronized( HibernateLifecycle.class ) { if (started) { if ( log.isDebugEnabled() ) log.debug("Stopping HibernateLifecycle"); try { started = false; for( Map.Entry<QName, SessionFactory> entry : sessionFactoryMap.entrySet() ) { close(entry.getValue()); } } finally { sessionFactoryMap.clear(); sessionFactoryToQName.clear(); } } } } /** * Does Hibernate configuration. * * @param context */ @StartStep(localName="hibernate-configuration", before="hibernate") public static void startHibernateConfig( LifecycleContext context ) { if( installDefaultConfiguration() ) { if( log.isInfoEnabled() ) { log.info("Reading default Hibernate configuration from '/hibernate.cfg.xml' and '/hibernate.properties'."); } // Uses the hibernate annotation configuration, replace with Configuration() if you // don't use annotations or JDK 5.0 Configuration configuration = new AnnotationConfiguration(); ((AnnotationConfiguration)configuration).setNamingStrategy(new DefaultComponentSafeNamingStrategy()); // Read not only hibernate.properties, but also hibernate.cfg.xml configuration.configure(); // set the configuration. HibernateLifecycle.setConfiguration(configuration); // we will need to clean this up after the lifecycle has started. cleanUp = true; } } @StopStep(localName="hibernate-configuration") public static void stopHibernateConfig( LifecycleContext context ) { // clean up the configuration. if( cleanUp == true ) { HibernateLifecycle.setConfiguration(null); cleanUp = false; } } /** * Returns true if we need to create a default configuration. */ private static boolean installDefaultConfiguration() { return HibernateLifecycle.getConfiguration() == null && (hasResource("hibernate.cfg.xml") || hasResource("hibernate.properties")); } /** * Sets the default configuration. */ public static void setConfiguration( Configuration configuration ) { synchronized( HibernateLifecycle.class ) { setConfiguration( DEFAULT_NAME, configuration ); } } /** * Returns true if the resource path is defined in the context class loader. */ private static boolean hasResource(String resourcePath) { return Thread.currentThread().getContextClassLoader().getResource(resourcePath) != null; } /** * Sets a named configuration. */ public static void setConfiguration( QName name, Configuration configuration ) { synchronized( HibernateLifecycle.class ) { if( name == null ) { setConfiguration(configuration); } if( !HibernateLifecycle.started ) { HibernateLifecycle.configurationMap.put(name, configuration); HibernateLifecycle.configurationToQName.put(configuration, name); } else { throw new IllegalStateException("A named hibernate configuration cannot be set while the lifecycle is running."); } } } private static void setXChainDefaults( QName name, Configuration configuration ) { String dataSourceJndi = configuration.getProperty(Environment.DATASOURCE); String connectionProvider = configuration.getProperty(Environment.CONNECTION_PROVIDER); if( dataSourceJndi != null && connectionProvider == null ) { if( log.isInfoEnabled() ) { log.info("Adding the property '"+Environment.CONNECTION_PROVIDER+"' with value 'org.xchain.framework.hibernate.RebindingDataSourceConnectionProvider' to the hibernate configuration of datasource '"+dataSourceJndi+"'."); } configuration.setProperty(Environment.CONNECTION_PROVIDER, "org.xchain.framework.hibernate.RebindingDataSourceConnectionProvider"); } } private static void mapSessionFactory( QName name, SessionFactory sessionFactory ) { synchronized( HibernateLifecycle.class ) { HibernateLifecycle.sessionFactoryMap.put(name, sessionFactory); HibernateLifecycle.sessionFactoryToQName.put(sessionFactory, name); } } /** * Returns the Hibernate configuration. * * @return Configuration */ public static Configuration getConfiguration() { return getConfiguration(DEFAULT_NAME); } public static Configuration getConfiguration( QName name ) { synchronized( HibernateLifecycle.class ) { if( name == null ) { name = DEFAULT_NAME; } return HibernateLifecycle.configurationMap.get(name); } } /** * Returns the Hibernate Session Factory. * * @return SessionFactory * @throws SessionFactoryNotFoundException if there is not a session factory defined for the default QName. */ public static SessionFactory getSessionFactory() { return getSessionFactory(DEFAULT_NAME); } /** * Returns a named Hibernate Session Factory. * * @param name the name of the session factory. * * @return the SessionFactory with the specified name. * @throws SessionFactoryNotFoundException if there is not a session factory defined for the specified QName. */ public static SessionFactory getSessionFactory( QName name ) { SessionFactory sessionFactory = null; synchronized( HibernateLifecycle.class ) { if( name == null ) { name=DEFAULT_NAME; } sessionFactory = HibernateLifecycle.sessionFactoryMap.get(name); if( sessionFactory == null ) { throw new SessionFactoryNotFoundException(name); } return sessionFactory; } } public static Set<QName> getSessionFactoryNames() { synchronized( HibernateLifecycle.class ) { return new HashSet<QName>(sessionFactoryMap.keySet()); } } public static QName getQName( SessionFactory sessionFactory ) { synchronized( HibernateLifecycle.class ) { return sessionFactoryToQName.get(sessionFactory); } } public static QName getQName( Configuration configuration ) { synchronized( HibernateLifecycle.class ) { return configurationToQName.get(configuration); } } /** * Get the current Hibernate session. * * NOTE: This method is not compatible with sessions created with SessionFactory.openSession(). This includes the current <hibernate:session> command, since * it uses SessionFactory.openSession() to get its sessions. * * @return Session */ @Function(localName="current-session") public static Session getCurrentSession() { return getSessionFactory(DEFAULT_NAME).getCurrentSession(); } /** * Gets the current Hibernate session for the named session factory. If the name is null, * then getCurrentSession() is returned. * * @return the current Session for the named SessionFactory. */ @Function(localName="current-session") public static Session getCurrentSession( QName name ) { synchronized( HibernateLifecycle.class ) { if( name == null ) { name = DEFAULT_NAME; } return getSessionFactory(name).getCurrentSession(); } } private static void close( SessionFactory sessionFactory ) { if( sessionFactory != null ) { try { sessionFactory.close(); } catch( Throwable e ) { if( log.isWarnEnabled() ) { log.warn("A hibernate session factory could not be closed.", e); } } } } }