/** * Copyright 2010 Wallace Wadge * * 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 com.jolbox.bonecp.provider; import java.sql.Connection; import java.sql.SQLException; import java.util.Map; import java.util.Properties; import org.hibernate.HibernateException; import org.hibernate.cfg.AvailableSettings; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.service.UnknownUnwrapTypeException; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.service.spi.Configurable; import org.hibernate.service.spi.Stoppable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jolbox.bonecp.BoneCP; import com.jolbox.bonecp.BoneCPConfig; import com.jolbox.bonecp.hooks.ConnectionHook; /** * Hibernate Connection Provider. * * @author wallacew */ public class BoneCPConnectionProvider implements ConnectionProvider,Configurable,Stoppable { /** * uid */ private static final long serialVersionUID = -5236029951415598543L; /** Config key. */ protected static final String CONFIG_CONNECTION_DRIVER_CLASS = "hibernate.connection.driver_class"; /** Config key. */ protected static final String CONFIG_CONNECTION_PASSWORD = "hibernate.connection.password"; /** Config key. */ protected static final String CONFIG_CONNECTION_USERNAME = "hibernate.connection.username"; /** Config key. */ protected static final String CONFIG_CONNECTION_URL = "hibernate.connection.url"; /** Config key. */ protected static final String CONFIG_CONNECTION_DRIVER_CLASS_ALTERNATE = "javax.persistence.jdbc.driver"; /** Config key. */ protected static final String CONFIG_CONNECTION_PASSWORD_ALTERNATE = "javax.persistence.jdbc.password"; /** Config key. */ protected static final String CONFIG_CONNECTION_USERNAME_ALTERNATE = "javax.persistence.jdbc.user"; /** Config key. */ protected static final String CONFIG_CONNECTION_URL_ALTERNATE = "javax.persistence.jdbc.url"; /** Connection pool handle. */ private BoneCP pool; /** Isolation level. */ private Integer isolation; /** Autocommit option. */ private boolean autocommit; /** Classloader to use to load the jdbc driver. */ private ClassLoader classLoader; /** Configuration handle. */ private BoneCPConfig config; /** Class logger. */ private static final Logger logger = LoggerFactory.getLogger(BoneCPConnectionProvider.class); /** * {@inheritDoc} * * @see org.hibernate.engine.jdbc.connections.spi.ConnectionProvider#closeConnection(java.sql.Connection) */ public void closeConnection(Connection conn) throws SQLException { conn.close(); } /** * Pool configuration. * @param props * @throws HibernateException */ public void configure(Properties props) throws HibernateException { try{ this.config = new BoneCPConfig(props); // old hibernate config String url = props.getProperty(CONFIG_CONNECTION_URL); String username = props.getProperty(CONFIG_CONNECTION_USERNAME); String password = props.getProperty(CONFIG_CONNECTION_PASSWORD); String driver = props.getProperty(CONFIG_CONNECTION_DRIVER_CLASS); if (url == null){ url = props.getProperty(CONFIG_CONNECTION_URL_ALTERNATE); } if (username == null){ username = props.getProperty(CONFIG_CONNECTION_USERNAME_ALTERNATE); } if (password == null){ password = props.getProperty(CONFIG_CONNECTION_PASSWORD_ALTERNATE); } if (driver == null){ driver = props.getProperty(CONFIG_CONNECTION_DRIVER_CLASS_ALTERNATE); } if (url != null){ this.config.setJdbcUrl(url); } if (username != null){ this.config.setUsername(username); } if (password != null){ this.config.setPassword(password); } // Remember Isolation level this.isolation = ConfigurationHelper.getInteger(AvailableSettings.ISOLATION, props); this.autocommit = ConfigurationHelper.getBoolean(AvailableSettings.AUTOCOMMIT, props); logger.debug(this.config.toString()); if (driver != null && !driver.trim().equals("")){ loadClass(driver); } if (this.config.getConnectionHookClassName() != null){ Object hookClass = loadClass(this.config.getConnectionHookClassName()).newInstance(); this.config.setConnectionHook((ConnectionHook) hookClass); } // create the connection pool this.pool = createPool(this.config); } catch (Exception e) { throw new HibernateException(e); } } /** Loads the given class, respecting the given classloader. * @param clazz class to load * @return Loaded class * @throws ClassNotFoundException */ protected Class<?> loadClass(String clazz) throws ClassNotFoundException { if (this.classLoader == null){ return Class.forName(clazz); } return Class.forName(clazz, true, this.classLoader); } /** Creates the given connection pool with the given configuration. Extracted here to make unit mocking easier. * @param config configuration object. * @return BoneCP connection pool handle. */ protected BoneCP createPool(BoneCPConfig config) { try{ return new BoneCP(config); } catch (SQLException e) { throw new HibernateException(e); } } /** * {@inheritDoc} * * @see org.hibernate.engine.jdbc.connections.spi.ConnectionProvider#getConnection() */ public Connection getConnection() throws SQLException { Connection connection = this.pool.getConnection(); // set the Transaction Isolation if defined try { // set the Transaction Isolation if defined if ((this.isolation != null) && (connection.getTransactionIsolation() != this.isolation.intValue())) { connection.setTransactionIsolation (this.isolation.intValue()); } // toggle autoCommit to false if set if ( connection.getAutoCommit() != this.autocommit ){ connection.setAutoCommit(this.autocommit); } return connection; } catch (SQLException e){ try { connection.close(); } catch (Exception e2) { logger.warn("Setting connection properties failed and closing this connection failed again", e); } throw e; } } /** * {@inheritDoc} * * @see org.hibernate.engine.jdbc.connections.spi.ConnectionProvider#supportsAggressiveRelease() */ public boolean supportsAggressiveRelease() { return false; } /** Returns the configuration object being used. * @return configuration object */ protected BoneCPConfig getConfig() { return this.config; } /** Returns the classloader to use when attempting to load the jdbc driver (if a value is given). * @return the classLoader currently set. */ public ClassLoader getClassLoader() { return this.classLoader; } /** Specifies the classloader to use when attempting to load the jdbc driver (if a value is given). Set to null to use the default * loader. * @param classLoader the classLoader to set */ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override @SuppressWarnings("rawtypes") public boolean isUnwrappableAs(Class unwrapType) { return ConnectionProvider.class.equals( unwrapType ) || BoneCPConnectionProvider.class.isAssignableFrom( unwrapType ); } @SuppressWarnings("unchecked") @Override public <T> T unwrap(Class<T> unwrapType) { if ( ConnectionProvider.class.equals( unwrapType ) || BoneCPConnectionProvider.class.isAssignableFrom( unwrapType ) ) { return (T) this; } throw new UnknownUnwrapTypeException( unwrapType ); } /** * Legacy conversion. * @param map * @return Properties */ private Properties mapToProperties(Map<String, String> map) { Properties p = new Properties(); for (Map.Entry<String,String> entry : map.entrySet()) { p.put(entry.getKey(), entry.getValue()); } return p; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public void configure(Map configurationValues) { configure(mapToProperties(configurationValues)); } @Override public void stop() { close(); } /** * alias for stop. */ public void close(){ this.pool.shutdown(); } }