sascha@168: /*
sascha@168:  * Copyright 2004 The Apache Software Foundation.
sascha@168:  *
sascha@168:  * Licensed under the Apache License, Version 2.0 (the "License");
sascha@168:  * you may not use this file except in compliance with the License.
sascha@168:  * You may obtain a copy of the License at
sascha@168:  *
sascha@168:  *      http://www.apache.org/licenses/LICENSE-2.0
sascha@168:  *
sascha@168:  * Unless required by applicable law or agreed to in writing, software
sascha@168:  * distributed under the License is distributed on an "AS IS" BASIS,
sascha@168:  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
sascha@168:  * See the License for the specific language governing permissions and
sascha@168:  * limitations under the License.
sascha@168:  */
ingo@3336: package de.intevation.flys.utils;
sascha@168: 
sascha@168: import java.sql.Connection;
sascha@168: import java.sql.SQLException;
sascha@168: 
sascha@168: import java.util.Iterator;
sascha@168: import java.util.Properties;
sascha@168: import java.util.Map;
sascha@168: 
sascha@168: import org.apache.commons.dbcp.BasicDataSource;
sascha@168: import org.apache.commons.dbcp.BasicDataSourceFactory;
sascha@168: 
sascha@168: import org.apache.log4j.Logger;
sascha@168: 
sascha@168: import org.hibernate.HibernateException;
sascha@168: 
sascha@168: import org.hibernate.connection.ConnectionProviderFactory;
sascha@168: import org.hibernate.connection.ConnectionProvider;
sascha@168: 
sascha@168: import org.hibernate.cfg.Environment;
sascha@168: 
sascha@168: /**
sascha@168:  * <p>A connection provider that uses an Apache commons DBCP connection pool.</p>
sascha@168:  *
sascha@168:  * <p>To use this connection provider set:<br>
sascha@168:  * <code>hibernate.connection.provider_class&nbsp;org.hibernate.connection.DBCPConnectionProvider</code></p>
sascha@168:  *
sascha@168:  * <pre>Supported Hibernate properties:
sascha@168:  *   hibernate.connection.driver_class
sascha@168:  *   hibernate.connection.url
sascha@168:  *   hibernate.connection.username
sascha@168:  *   hibernate.connection.password
sascha@168:  *   hibernate.connection.isolation
sascha@168:  *   hibernate.connection.autocommit
sascha@168:  *   hibernate.connection.pool_size
sascha@168:  *   hibernate.connection (JDBC driver properties)</pre>
sascha@168:  * <br>
sascha@168:  * All DBCP properties are also supported by using the hibernate.dbcp prefix.
sascha@168:  * A complete list can be found on the DBCP configuration page:
sascha@168:  * <a href="http://jakarta.apache.org/commons/dbcp/configuration.html">http://jakarta.apache.org/commons/dbcp/configuration.html</a>.
sascha@168:  * <br>
sascha@168:  * <pre>Example:
sascha@168:  *   hibernate.connection.provider_class org.hibernate.connection.DBCPConnectionProvider
sascha@168:  *   hibernate.connection.driver_class org.hsqldb.jdbcDriver
sascha@168:  *   hibernate.connection.username sa
sascha@168:  *   hibernate.connection.password
sascha@168:  *   hibernate.connection.url jdbc:hsqldb:test
sascha@168:  *   hibernate.connection.pool_size 20
sascha@168:  *   hibernate.dbcp.initialSize 10
sascha@168:  *   hibernate.dbcp.maxWait 3000
sascha@168:  *   hibernate.dbcp.validationQuery select 1 from dual</pre>
sascha@168:  *
sascha@168:  * <p>More information about configuring/using DBCP can be found on the
sascha@168:  * <a href="http://jakarta.apache.org/commons/dbcp/">DBCP website</a>.
sascha@168:  * There you will also find the DBCP wiki, mailing lists, issue tracking
sascha@168:  * and other support facilities</p>
sascha@168:  *
sascha@168:  * @see org.hibernate.connection.ConnectionProvider
sascha@168:  * @author Dirk Verbeeck
sascha@168:  */
sascha@168: public class DBCPConnectionProvider
sascha@168: implements   ConnectionProvider
sascha@168: {
sascha@168:     private static Logger log = Logger.getLogger(DBCPConnectionProvider.class);
sascha@168: 
sascha@168:     private static final String PREFIX = "hibernate.dbcp.";
sascha@168: 
sascha@168:     private BasicDataSource ds;
sascha@168: 
sascha@464:     // Old Environment property for backward-compatibility
sascha@168:     // (property removed in Hibernate3)
sascha@168:     private static final String DBCP_PS_MAXACTIVE =
sascha@168:         "hibernate.dbcp.ps.maxActive";
sascha@168: 
sascha@168:     // Property doesn't exists in Hibernate2
sascha@168:     private static final String AUTOCOMMIT =
sascha@168:         "hibernate.connection.autocommit";
sascha@168: 
sascha@168:     public void configure(Properties props) throws HibernateException {
sascha@168:         try {
sascha@168:             log.debug("Configure DBCPConnectionProvider");
sascha@168: 
sascha@168:             // DBCP properties used to create the BasicDataSource
sascha@168:             Properties dbcpProperties = new Properties();
sascha@168: 
sascha@168:             // DriverClass & url
sascha@168:             String jdbcDriverClass = props.getProperty(Environment.DRIVER);
sascha@168:             String jdbcUrl = props.getProperty(Environment.URL);
sascha@168:             dbcpProperties.put("driverClassName", jdbcDriverClass);
sascha@168:             dbcpProperties.put("url", jdbcUrl);
sascha@168: 
sascha@168:             // Username / password
sascha@168:             String username = props.getProperty(Environment.USER);
sascha@168:             String password = props.getProperty(Environment.PASS);
sascha@168:             dbcpProperties.put("username", username);
sascha@168:             dbcpProperties.put("password", password);
sascha@168: 
sascha@168:             // Isolation level
sascha@168:             String isolationLevel = props.getProperty(Environment.ISOLATION);
sascha@168:             if (isolationLevel != null
sascha@168:             && (isolationLevel = isolationLevel.trim()).length() > 0) {
sascha@168:                 dbcpProperties.put("defaultTransactionIsolation", isolationLevel);
sascha@168:             }
sascha@168: 
sascha@168:             // Turn off autocommit (unless autocommit property is set)
sascha@168:             String autocommit = props.getProperty(AUTOCOMMIT);
sascha@168:             if (autocommit != null
sascha@168:             && (autocommit = autocommit.trim()).length() > 0) {
sascha@168:                 dbcpProperties.put("defaultAutoCommit", autocommit);
sascha@168:             } else {
sascha@168:                 dbcpProperties.put("defaultAutoCommit", String.valueOf(Boolean.FALSE));
sascha@168:             }
sascha@168: 
sascha@168:             // Pool size
sascha@168:             String poolSize = props.getProperty(Environment.POOL_SIZE);
sascha@168:             if (poolSize != null
sascha@168:             && (poolSize = poolSize.trim()).length() > 0
sascha@168:             && Integer.parseInt(poolSize) > 0)  {
sascha@168:                 dbcpProperties.put("maxActive", poolSize);
sascha@168:             }
sascha@168: 
sascha@168:             // Copy all "driver" properties into "connectionProperties"
sascha@168:             Properties driverProps =
sascha@168:                 ConnectionProviderFactory.getConnectionProperties(props);
sascha@168: 
sascha@168:             if (driverProps.size() > 0) {
sascha@168:                 StringBuilder connectionProperties = new StringBuilder();
sascha@464:                 for (Iterator iter = driverProps.entrySet().iterator();
sascha@168:                     iter.hasNext();
sascha@168:                 ) {
sascha@168:                     Map.Entry entry = (Map.Entry)iter.next();
sascha@168:                     String    key   = (String)entry.getKey();
sascha@168:                     String    value = (String)entry.getValue();
sascha@168:                     connectionProperties
sascha@168:                         .append(key)
sascha@168:                         .append('=')
sascha@168:                         .append(value);
sascha@168:                     if (iter.hasNext()) {
sascha@168:                         connectionProperties.append(';');
sascha@168:                     }
sascha@168:                 }
sascha@168:                 dbcpProperties.put(
sascha@168:                     "connectionProperties", connectionProperties.toString());
sascha@168:             }
sascha@168: 
sascha@168:             // Copy all DBCP properties removing the prefix
sascha@168:             for (Iterator iter = props.entrySet().iterator() ; iter.hasNext() ;) {
sascha@168:                 Map.Entry entry = (Map.Entry)iter.next();
sascha@168:                 String    key   = (String)entry.getKey();
sascha@168:                 if (key.startsWith(PREFIX)) {
sascha@168:                     String property = key.substring(PREFIX.length());
sascha@168:                     String value    = (String)entry.getValue();
sascha@168:                     dbcpProperties.put(property, value);
sascha@168:                 }
sascha@168:             }
sascha@168: 
sascha@168:             // Backward-compatibility
sascha@168:             if (props.getProperty(DBCP_PS_MAXACTIVE) != null) {
sascha@168:                 dbcpProperties.put(
sascha@168:                     "poolPreparedStatements",
sascha@168:                     String.valueOf(Boolean.TRUE));
sascha@168:                 dbcpProperties.put(
sascha@168:                     "maxOpenPreparedStatements",
sascha@168:                     props.getProperty(DBCP_PS_MAXACTIVE));
sascha@168:             }
sascha@168: 
sascha@168:             // Some debug info
sascha@181:             /* // commented out, because it leaks the password
sascha@168:             if (log.isDebugEnabled()) {
sascha@168:                 log.debug("Creating a DBCP BasicDataSource" +
sascha@168:                           " with the following DBCP factory properties:");
sascha@168:                 StringWriter sw = new StringWriter();
sascha@168:                 dbcpProperties.list(new PrintWriter(sw, true));
sascha@168:                 log.debug(sw.toString());
sascha@168:             }
sascha@181:             */
sascha@168: 
sascha@168:             // Let the factory create the pool
sascha@168:             ds = (BasicDataSource)BasicDataSourceFactory
sascha@168:                 .createDataSource(dbcpProperties);
sascha@168: 
sascha@168:             // The BasicDataSource has lazy initialization
sascha@168:             // borrowing a connection will start the DataSource
sascha@168:             // and make sure it is configured correctly.
sascha@168: 
sascha@168:             // Connection conn = ds.getConnection();
sascha@168:             // conn.close();
sascha@168:         }
sascha@168:         catch (Exception e) {
sascha@168:             String message = "Could not create a DBCP pool";
sascha@168:             log.fatal(message, e);
sascha@168:             if (ds != null) {
sascha@168:                 BasicDataSource x = ds; ds = null;
sascha@168:                 try {
sascha@168:                     x.close();
sascha@168:                 }
sascha@168:                 catch (SQLException sqle) {
sascha@168:                 }
sascha@168:             }
sascha@168:             throw new HibernateException(message, e);
sascha@168:         }
sascha@168:         log.debug("Configure DBCPConnectionProvider complete");
sascha@168:     }
sascha@168: 
sascha@168:     public Connection getConnection() throws SQLException {
sascha@168:         return ds.getConnection();
sascha@168:     }
sascha@168: 
sascha@168:     public void closeConnection(Connection conn) throws SQLException {
sascha@168:         conn.close();
sascha@168:     }
sascha@168: 
sascha@168:     public void close() throws HibernateException {
sascha@168:         try {
sascha@168:             if (ds != null) {
sascha@168:                 BasicDataSource x = ds; ds = null;
sascha@168:                 x.close();
sascha@168:             }
sascha@168:         }
sascha@168:         catch (SQLException sqle) {
sascha@168:             throw new HibernateException("Could not close DBCP pool", sqle);
sascha@168:         }
sascha@168:     }
sascha@168: 
sascha@168:     public boolean supportsAggressiveRelease() {
sascha@168:         return false;
sascha@168:     }
sascha@168: }
sascha@168: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :