org.transitime.db.hibernate.HibernateUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.transitime.db.hibernate.HibernateUtils.java

Source

/* 
 * This file is part of Transitime.org
 * 
 * Transitime.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * Transitime.org 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Transitime.org .  If not, see <http://www.gnu.org/licenses/>.
 */
package org.transitime.db.hibernate;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.transitime.configData.DbSetupConfig;

/**
 * Utilities for dealing with Hibernate issues such as sessions.
 * 
 * @author SkiBu Smith
 *
 */
public class HibernateUtils {

    // Should be set to what is used in hibernate.cfg.xml where the
    // batch_size is set, e.g. <property name="hibernate.jdbc.batch_size">25</property>
    public static final int BATCH_SIZE = 25;

    // When Using @Column for route, stop, etc IDs don't need the default of
    // 255 characters. Therefore can use shorter fields. 
    public static final int DEFAULT_ID_SIZE = 60;

    // Cache. Keyed on database name
    private static HashMap<String, SessionFactory> sessionFactoryCache = new HashMap<String, SessionFactory>();

    public static final Logger logger = LoggerFactory.getLogger(HibernateUtils.class);

    /********************** Member Functions **************************/

    /**
     * Creates a new session factory. This is to be cached and only access
     * internally since creating one is expensive.
     * 
     * @param dbName
     * @return
     */
    private static SessionFactory createSessionFactory(String dbName) throws HibernateException {
        logger.debug("Creating new Hibernate SessionFactory for dbName={}", dbName);

        // Create a Hibernate configuration based on customized config file
        Configuration config = new Configuration();

        // Want to be able to specify a configuration file for now
        // since developing in Eclipse and want all config files
        // to be in same place. But the Config.configure(String) 
        // method can't seem to work with a Windows directory name such
        // as C:/users/Mike/software/hibernate.cfg.xml . Therefore create
        // a File object for that file name and pass in the File object
        // to configure().
        String fileName = DbSetupConfig.getHibernateConfigFileName();
        logger.info("Configuring Hibernate for dbName={} using config file={}", dbName, fileName);
        File f = new File(fileName);
        if (!f.exists()) {
            logger.info(
                    "The Hibernate file {} doesn't exist as a regular file " + "so seeing if it is in classpath.",
                    fileName);

            // Couldn't find file directly so look in classpath for it
            ClassLoader classLoader = HibernateUtils.class.getClassLoader();
            URL url = classLoader.getResource(fileName);
            if (url != null)
                f = new File(url.getFile());
        }
        if (f.exists())
            config.configure(f);
        else {
            logger.error("Could not load in hibernate config file {}", fileName);
        }

        // Add the annotated classes so that they can be used
        AnnotatedClassesList.addAnnotatedClasses(config);

        // Set the db info for the URL, user name, and password. Use values 
        // from CoreConfig if set. If they are not set then the values will be 
        // obtained from the hibernate.cfg.xml 
        // config file.
        String dbUrl = null;
        if (DbSetupConfig.getDbHost() != null) {
            dbUrl = "jdbc:" + DbSetupConfig.getDbType() + "://" + DbSetupConfig.getDbHost() + "/" + dbName;
            config.setProperty("hibernate.connection.url", dbUrl);
        } else {
            dbUrl = config.getProperty("hibernate.connection.url");
        }

        String dbUserName = DbSetupConfig.getDbUserName();
        if (dbUserName != null) {
            config.setProperty("hibernate.connection.username", dbUserName);
        } else {
            dbUserName = config.getProperty("hibernate.connection.username");
        }

        if (DbSetupConfig.getDbPassword() != null)
            config.setProperty("hibernate.connection.password", DbSetupConfig.getDbPassword());

        // Log info, but don't log password. This can just be debug logging
        // even though it is important because the C3P0 connector logs the info.
        logger.debug(
                "For Hibernate factory project dbName={} " + "using url={} username={}, and configured password",
                dbName, dbUrl, dbUserName);

        // Get the session factory for persistence
        Properties properties = config.getProperties();
        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(properties).build();
        SessionFactory sessionFactory = config.buildSessionFactory(serviceRegistry);

        // Return the factory
        return sessionFactory;
    }

    /**
     * Returns a cached Hibernate SessionFactory. Returns null if there is a
     * problem.
     * 
     * @param agencyId
     *            Used as the database name if the property
     *            transitime.core.dbName is not set
     * @return
     */
    public static SessionFactory getSessionFactory(String agencyId) throws HibernateException {
        // Determine the database name to use. Will usually use the
        // projectId since each project has a database. But this might
        // be overridden by the transitime.core.dbName property.
        String dbName = DbSetupConfig.getDbName();
        if (dbName == null)
            dbName = agencyId;

        SessionFactory factory;

        synchronized (sessionFactoryCache) {
            factory = sessionFactoryCache.get(dbName);
            // If factory not yet created for this projectId then create it
            if (factory == null) {
                try {
                    factory = createSessionFactory(dbName);
                    sessionFactoryCache.put(dbName, factory);
                } catch (Exception e) {
                    logger.error("Could not create SessionFactory for " + "dbName={}", dbName, e);
                    throw e;
                }
            }
        }

        return factory;
    }

    /**
     * Clears out the session factory so that a new one will be created for the
     * dbName. This way new db connections are made. This is useful for dealing
     * with timezones and postgres. For that situation want to be able to read
     * in timezone from db so can set default timezone. Problem with postgres is
     * that once a factory is used to generate sessions the database will
     * continue to use the default timezone that was configured at that time.
     * This means that future calls to the db will use the wrong timezone!
     * Through this function one can read in timezone from database, set the
     * default timezone, clear the factory so that future db connections will
     * use the newly configured timezone, and then successfully process dates.
     */
    public static void clearSessionFactory() {
        sessionFactoryCache.clear();
    }

    /**
     * Returns session for the specified agencyId.
     * <p>
     * NOTE: Make sure you close the session after the query!! Use a try/catch
     * around the query and close the session in a finally block to make sure it
     * happens. The system only gets a limited number of sessions!!
     * 
     * @param agencyId
     *            Used as the database name if the property
     *            transitime.core.dbName is not set
     * @return The Session. Make sure you close it when done because system only
     *         gets limited number of open sessions.
     * @throws HibernateException
     */
    public static Session getSession(String agencyId) throws HibernateException {
        SessionFactory sessionFactory = HibernateUtils.getSessionFactory(agencyId);
        Session session = sessionFactory.openSession();
        return session;
    }

    /**
     * Returns the session for the database name specified by the
     * transitime.db.dbName Java property.
     * <p>
     * NOTE: Make sure you close the session after the query!! Use a try/catch
     * around the query and close the session in a finally block to make sure it
     * happens. The system only gets a limited number of sessions!!
     * 
     * @return The Session. Make sure you close it when done because system only
     *         gets limited number of open sessions.
     */
    public static Session getSession() {
        SessionFactory sessionFactory = HibernateUtils.getSessionFactory(DbSetupConfig.getDbName());
        Session session = sessionFactory.openSession();
        return session;
    }

    /**
     * Determines the size of a serializable object by serializing it in memory
     * and then measuring the resulting size in bytes.
     * 
     * @param obj
     *            Object to be serialized
     * @return Size of serialized object in bytes or -1 if object cannot be
     *         serialized
     */
    public static int sizeof(Object obj) {

        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);

            objectOutputStream.writeObject(obj);
            objectOutputStream.flush();
            objectOutputStream.close();
        } catch (IOException e) {
            return -1;
        }

        return byteOutputStream.toByteArray().length;
    }
}