001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: */
013: /*
014: * Copyright 2004 The Apache Software Foundation.
015: *
016: * Licensed under the Apache License, Version 2.0 (the "License");
017: * you may not use this file except in compliance with the License.
018: * You may obtain a copy of the License at
019: *
020: * http://www.apache.org/licenses/LICENSE-2.0
021: *
022: * Unless required by applicable law or agreed to in writing, software
023: * distributed under the License is distributed on an "AS IS" BASIS,
024: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
025: * See the License for the specific language governing permissions and
026: * limitations under the License.
027: */
028: package org.hibernate.connection;
029:
030: import java.io.PrintWriter;
031: import java.io.StringWriter;
032: import java.sql.Connection;
033: import java.sql.SQLException;
034: import java.util.Iterator;
035: import java.util.Properties;
036: import org.apache.commons.dbcp.BasicDataSource;
037: import org.apache.commons.dbcp.BasicDataSourceFactory;
038: import org.apache.commons.logging.Log;
039: import org.apache.commons.logging.LogFactory;
040: import org.hibernate.HibernateException;
041: import org.hibernate.cfg.Environment;
042: import org.pentaho.messages.Messages;
043:
044: /**
045: * <p>
046: * A connection provider that uses an Apache commons DBCP connection pool.
047: * </p>
048: *
049: * <p>
050: * To use this connection provider set:<br>
051: * <code>hibernate.connection.provider_class org.hibernate.connection.DBCPConnectionProvider</code>
052: * </p>
053: *
054: * <pre>
055: * Supported Hibernate properties:
056: * hibernate.connection.driver_class
057: * hibernate.connection.url
058: * hibernate.connection.username
059: * hibernate.connection.password
060: * hibernate.connection.isolation
061: * hibernate.connection.autocommit
062: * hibernate.connection.pool_size
063: * hibernate.connection (JDBC driver properties)
064: * </pre>
065: *
066: * <br>
067: * All DBCP properties are also supported by using the hibernate.dbcp prefix. A
068: * complete list can be found on the DBCP configuration page: <a
069: * href="http://jakarta.apache.org/commons/dbcp/configuration.html">http://jakarta.apache.org/commons/dbcp/configuration.html</a>.
070: * <br>
071: *
072: * <pre>
073: * Example:
074: * hibernate.connection.provider_class org.hibernate.connection.DBCPConnectionProvider
075: * hibernate.connection.driver_class org.hsqldb.jdbcDriver
076: * hibernate.connection.username sa
077: * hibernate.connection.password
078: * hibernate.connection.url jdbc:hsqldb:test
079: * hibernate.connection.pool_size 20
080: * hibernate.dbcp.initialSize 10
081: * hibernate.dbcp.maxWait 3000
082: * hibernate.dbcp.validationQuery select 1 from dual
083: * </pre>
084: *
085: * <p>
086: * More information about configuring/using DBCP can be found on the <a
087: * href="http://jakarta.apache.org/commons/dbcp/">DBCP website</a>. There you
088: * will also find the DBCP wiki, mailing lists, issue tracking and other support
089: * facilities
090: * </p>
091: *
092: * @see org.hibernate.connection.ConnectionProvider
093: * @author Dirk Verbeeck
094: */
095: public class DBCPConnectionProvider implements ConnectionProvider {
096:
097: private static final Log log = LogFactory
098: .getLog(DBCPConnectionProvider.class);
099:
100: private static final String PREFIX = "hibernate.dbcp."; //$NON-NLS-1$
101:
102: private BasicDataSource ds;
103:
104: // Old Environment property for backward-compatibility (property removed in
105: // Hibernate3)
106: private static final String DBCP_PS_MAXACTIVE = "hibernate.dbcp.ps.maxActive"; //$NON-NLS-1$
107:
108: // Property doesn't exists in Hibernate2
109: private static final String AUTOCOMMIT = "hibernate.connection.autocommit"; //$NON-NLS-1$
110:
111: public void configure(Properties props) throws HibernateException {
112: try {
113: log
114: .debug(Messages
115: .getString("DBCPConnectionProvider.USER_DEBUG_CONFIG_DBCP")); //$NON-NLS-1$
116:
117: // DBCP properties used to create the BasicDataSource
118: Properties dbcpProperties = new Properties();
119:
120: // DriverClass & url
121: String jdbcDriverClass = props
122: .getProperty(Environment.DRIVER);
123: String jdbcUrl = props.getProperty(Environment.URL);
124: dbcpProperties.put("driverClassName", jdbcDriverClass); //$NON-NLS-1$
125: dbcpProperties.put("url", jdbcUrl); //$NON-NLS-1$
126:
127: // Username / password
128: String username = props.getProperty(Environment.USER);
129: String password = props.getProperty(Environment.PASS);
130: dbcpProperties.put("username", username); //$NON-NLS-1$
131: dbcpProperties.put("password", password); //$NON-NLS-1$
132:
133: // Isolation level
134: String isolationLevel = props
135: .getProperty(Environment.ISOLATION);
136: if ((isolationLevel != null)
137: && (isolationLevel.trim().length() > 0)) {
138: dbcpProperties.put(
139: "defaultTransactionIsolation", isolationLevel); //$NON-NLS-1$
140: }
141:
142: // Turn off autocommit (unless autocommit property is set)
143: String autocommit = props.getProperty(AUTOCOMMIT);
144: if ((autocommit != null)
145: && (autocommit.trim().length() > 0)) {
146: dbcpProperties.put("defaultAutoCommit", autocommit); //$NON-NLS-1$
147: } else {
148: dbcpProperties
149: .put(
150: "defaultAutoCommit", String.valueOf(Boolean.FALSE)); //$NON-NLS-1$
151: }
152:
153: // Pool size
154: String poolSize = props.getProperty(Environment.POOL_SIZE);
155: if ((poolSize != null) && (poolSize.trim().length() > 0)
156: && (Integer.parseInt(poolSize) > 0)) {
157: dbcpProperties.put("maxActive", poolSize); //$NON-NLS-1$
158: }
159:
160: // Copy all "driver" properties into "connectionProperties"
161: Properties driverProps = ConnectionProviderFactory
162: .getConnectionProperties(props);
163: if (driverProps.size() > 0) {
164: StringBuffer connectionProperties = new StringBuffer();
165: for (Iterator iter = driverProps.keySet().iterator(); iter
166: .hasNext();) {
167: String key = (String) iter.next();
168: String value = driverProps.getProperty(key);
169: connectionProperties.append(key).append('=')
170: .append(value);
171: if (iter.hasNext()) {
172: connectionProperties.append(';');
173: }
174: }
175: dbcpProperties
176: .put(
177: "connectionProperties", connectionProperties.toString()); //$NON-NLS-1$
178: }
179:
180: // Copy all DBCP properties removing the prefix
181: for (Iterator iter = props.keySet().iterator(); iter
182: .hasNext();) {
183: String key = String.valueOf(iter.next());
184: if (key.startsWith(PREFIX)) {
185: String property = key.substring(PREFIX.length());
186: String value = props.getProperty(key);
187: dbcpProperties.put(property, value);
188: }
189: }
190:
191: // Backward-compatibility
192: if (props.getProperty(DBCP_PS_MAXACTIVE) != null) {
193: dbcpProperties
194: .put(
195: "poolPreparedStatements", String.valueOf(Boolean.TRUE)); //$NON-NLS-1$
196: dbcpProperties
197: .put(
198: "maxOpenPreparedStatements", props.getProperty(DBCP_PS_MAXACTIVE)); //$NON-NLS-1$
199: }
200:
201: // Some debug info
202: if (log.isDebugEnabled()) {
203: log
204: .debug(Messages
205: .getString("DBCPConnectionProvider.USER_DEBUG_CREATING_BASIC")); //$NON-NLS-1$
206: StringWriter sw = new StringWriter();
207: dbcpProperties.list(new PrintWriter(sw, true));
208: log.debug(sw.toString());
209: }
210:
211: // Let the factory create the pool
212: ds = (BasicDataSource) BasicDataSourceFactory
213: .createDataSource(dbcpProperties);
214:
215: // The BasicDataSource has lazy initialization
216: // borrowing a connection will start the DataSource
217: // and make sure it is configured correctly.
218: Connection conn = ds.getConnection();
219: conn.close();
220:
221: // Log pool statistics before continuing.
222: logStatistics();
223: } catch (Exception e) {
224: String message = Messages
225: .getString("DBCPConnectionProvider.ERROR_0001_COULD_NOT_CREATE"); //$NON-NLS-1$
226: log.fatal(message, e);
227: if (ds != null) {
228: try {
229: ds.close();
230: } catch (SQLException e2) {
231: // ignore
232: log.error(e2.getLocalizedMessage());
233: }
234: ds = null;
235: }
236: throw new HibernateException(message, e);
237: }
238: log
239: .debug(Messages
240: .getString("DBCPConnectionProvider.USER_DEBUG_DBCP_CONFIG_COMPLETE")); //$NON-NLS-1$
241: }
242:
243: public Connection getConnection() throws SQLException {
244: Connection conn = null;
245: try {
246: conn = ds.getConnection();
247: } finally {
248: logStatistics();
249: }
250: return conn;
251: }
252:
253: public void closeConnection(Connection conn) throws SQLException {
254: try {
255: conn.close();
256: } finally {
257: logStatistics();
258: }
259: }
260:
261: public boolean supportsAggressiveRelease() {
262: // To my knowledge, it doesn't.
263: // MB
264: return false;
265: }
266:
267: public void close() throws HibernateException {
268: log
269: .debug(Messages
270: .getString("DBCPConnectionProvider.USER_DEBUG_CLOSE_DBCP")); //$NON-NLS-1$
271: logStatistics();
272: try {
273: if (ds != null) {
274: ds.close();
275: ds = null;
276: } else {
277: log
278: .warn(Messages
279: .getString("DBCPConnectionProvider.USER_WARN_CANNOT_CLOSE_POOL")); //$NON-NLS-1$
280: }
281: } catch (Exception e) {
282: throw new HibernateException(
283: Messages
284: .getString("DBCPConnectionProvider.ERROR_0002_COULD_NOT_CLOSE_POOL"), e); //$NON-NLS-1$
285: }
286: log
287: .debug(Messages
288: .getString("DBCPConnectionProvider.USER_DEBUG_CLOSE_DBCP_COMPLETE")); //$NON-NLS-1$
289: }
290:
291: protected void logStatistics() {
292: if (log.isInfoEnabled()) {
293: log.info(Messages.getString(
294: "DBCPConnectionProvider.USER_STATS_MSG", //$NON-NLS-1$
295: Integer.toString(ds.getNumActive()), Integer
296: .toString(ds.getMaxActive()), Integer
297: .toString(ds.getNumIdle()), Integer
298: .toString(ds.getMaxIdle())));
299: }
300: }
301: }
|