/******************************************************************************* * ALMA - Atacama Large Millimeter Array * Copyright (c) COSYLAB - Control System Laboratory, 2011 * (in the framework of the ALMA collaboration). * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *******************************************************************************/ package com.cosylab.cdb.jdal.hibernate; import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; /** * Basic Hibernate helper class, handles SessionFactory, Session and Transaction. * <p> * Holds Session and Transactions in thread local variables. All * exceptions are wrapped in an unchecked HibernateUtilException. */ @SuppressWarnings("unchecked") public class HibernateUtil { @SuppressWarnings("serial") public static class HibernateUtilException extends Exception { public HibernateUtilException(String message, Throwable cause) { super(message, cause); } public HibernateUtilException(Throwable cause) { super(cause); } } /** * Singleton accessor * @return */ public static synchronized HibernateUtil getInstance(Logger logger) { if (instance == null) { instance = new HibernateUtil(logger); } return instance; } /** * Nulls the instance field, so that subsequent calls to {@link #getInstance(Logger)} will create a new instance. * This probably only makes sense between junit tests that want to start out fresh. */ public static void clearInstance() { instance = null; } private static HibernateUtil instance; private final Logger logger; private Configuration configuration; private SessionFactory sessionFactory; private final ThreadLocal threadSession = new ThreadLocal(); private final ThreadLocal threadTransaction = new ThreadLocal(); private final ThreadLocal threadInterceptor = new ThreadLocal(); private static final String HIBERNATE_FILENAME_KEY = "cdb_rdb.hibernate.cfg.filename"; public static final String HIBERNATE_FILENAME_DEFAULT = "cdb_rdb-hibernate.cfg.xml"; private final String getConfigurationFileName() { String ret = System.getProperty(HIBERNATE_FILENAME_KEY, HIBERNATE_FILENAME_DEFAULT); logger.fine("Will try to load hibernate config file '" + ret + "' from the classpath."); return ret; } private HibernateUtil(Logger logger) { this.logger = logger; // Create the initial SessionFactory from the default configuration files try { configuration = new Configuration(); sessionFactory = configuration.configure(getConfigurationFileName()).buildSessionFactory(); // We could also let Hibernate bind it to JNDI: // configuration.configure().buildSessionFactory() } catch (Throwable ex) { // @TODO HSO: now that we moved this code from static {} initializer to the ctor, // should we throw or at least log the exception? // We have to catch Throwable, otherwise we will miss // NoClassDefFoundError and other subclasses of Error // commented out - will be provided dynamically for the tests... //log.error("Building SessionFactory failed.", ex); //throw new ExceptionInInitializerError(ex); } } /** * Use default configuration and add properties from Properties. * Build session factory from combined configuration. * @param config */ public void setConfiguration(Properties extraProperties) { try { Configuration config = new Configuration(); config.configure(getConfigurationFileName()); config.addProperties(extraProperties); sessionFactory = config.buildSessionFactory(); configuration = config; } catch (Throwable ex) { // We have to catch Throwable, otherwise we will miss // NoClassDefFoundError and other subclasses of Error logger.log(Level.SEVERE, "Building SessionFactory failed.", ex); throw new ExceptionInInitializerError(ex); } } /** * Set your own configuration and build session factory from it. * Used to tests. * @param config */ public void setConfiguration(Configuration config) { try { sessionFactory = config.buildSessionFactory(); configuration = config; } catch (Throwable ex) { // We have to catch Throwable, otherwise we will miss // NoClassDefFoundError and other subclasses of Error logger.log(Level.SEVERE, "Building SessionFactory failed.", ex); throw new ExceptionInInitializerError(ex); } } /** * Returns the SessionFactory used for this static class. * * @return SessionFactory */ public SessionFactory getSessionFactory() { /* Instead of a static variable, use JNDI: SessionFactory sessions = null; try { Context ctx = new InitialContext(); String jndiName = "java:hibernate/HibernateFactory"; sessions = (SessionFactory)ctx.lookup(jndiName); } catch (NamingException ex) { throw new HibernateUtilException(ex); } return sessions; */ return sessionFactory; } /** * Returns the original Hibernate configuration. * * @return Configuration */ public Configuration getConfiguration() { return configuration; } /** * Rebuild the SessionFactory with the static Configuration. * */ public void rebuildSessionFactory() throws HibernateUtilException { synchronized(sessionFactory) { try { sessionFactory = getConfiguration().buildSessionFactory(); } catch (Exception ex) { throw new HibernateUtilException(ex); } } } /** * Rebuild the SessionFactory with the given Hibernate Configuration. * * @param cfg */ public void rebuildSessionFactory(Configuration cfg) throws HibernateUtilException { synchronized(sessionFactory) { try { sessionFactory = cfg.buildSessionFactory(); configuration = cfg; } catch (Exception ex) { throw new HibernateUtilException(ex); } } } /** * Retrieves the current Session local to the thread. * <p/> * If no Session is open, opens a new Session for the running thread. * * @return Session */ public Session getSession() throws HibernateUtilException { Session s = (Session) threadSession.get(); try { if (s == null) { //log.debug("Opening new Session for this thread."); //System.err.println("Opening new Session for this thread."); if (getInterceptor() != null) { //System.err.println("Using interceptor: " + getInterceptor().getClass()); s = getSessionFactory().openSession(getInterceptor()); } else { //System.err.println("Without interceptor"); s = getSessionFactory().openSession(); } threadSession.set(s); } } catch (HibernateException ex) { throw new HibernateUtilException(ex); } return s; } /** * Closes the Session local to the thread. */ public void closeSession() throws HibernateUtilException { try { Session s = (Session) threadSession.get(); threadSession.set(null); // Added to correctly handle situations where persist() alone cause an exception and commitTransaction() or rollbackTransactions() doesn't occur // (for example where we have unique index constraints) if (threadTransaction.get() != null) rollbackTransaction(); if (s != null && s.isOpen()) { //System.err.println("Closing Session of this thread."); s.close(); } } catch (HibernateException ex) { throw new HibernateUtilException(ex); } } /** * Start a new database transaction. */ public void beginTransaction() throws HibernateUtilException { Transaction tx = (Transaction) threadTransaction.get(); try { if (tx == null) { //System.err.println("Starting new database transaction in this thread."); tx = getSession().beginTransaction(); threadTransaction.set(tx); } } catch (HibernateException ex) { throw new HibernateUtilException(ex); } } /** * Commit the database transaction. */ public void commitTransaction() throws HibernateUtilException { Transaction tx = (Transaction) threadTransaction.get(); try { if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ) { //System.err.println("Committing database transaction of this thread."); tx.commit(); } threadTransaction.set(null); } catch (HibernateException ex) { rollbackTransaction(); throw new HibernateUtilException(ex); } } /** * Commit the database transaction. */ public void rollbackTransaction() throws HibernateUtilException { Transaction tx = (Transaction) threadTransaction.get(); try { threadTransaction.set(null); if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ) { //System.err.println("Tyring to rollback database transaction of this thread."); tx.rollback(); } } catch (HibernateException ex) { throw new HibernateUtilException(ex); } } /** * Reconnects a Hibernate Session to the current Thread. * * @param session The Hibernate Session to be reconnected. */ @SuppressWarnings("deprecation") public void reconnect(Session session) throws HibernateUtilException { try { session.reconnect(); threadSession.set(session); } catch (HibernateException ex) { throw new HibernateUtilException(ex); } } /** * Disconnect and return Session from current Thread. * * @return Session the disconnected Session */ public Session disconnectSession() throws HibernateUtilException { Session session = getSession(); try { threadSession.set(null); if (session.isConnected() && session.isOpen()) session.disconnect(); } catch (HibernateException ex) { throw new HibernateUtilException(ex); } return session; } /** * Register a Hibernate interceptor with the current thread. * <p> * Every Session opened is opened with this interceptor after * registration. Has no effect if the current Session of the * thread is already open, effective on next close()/getSession(). */ public void registerInterceptor(Interceptor interceptor) { threadInterceptor.set(interceptor); } private Interceptor getInterceptor() { Interceptor interceptor = (Interceptor) threadInterceptor.get(); return interceptor; } public List getList(Class type) throws HibernateUtilException { List result = null; try{ beginTransaction(); Session session = getSession(); result = session.createCriteria(type).list(); commitTransaction(); } catch (Throwable thr) { throw new HibernateUtilException(thr); } finally { closeSession(); } return result; } }