001: /*
002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/db/CmsDbPool.java,v $
003: * Date : $Date: 2008-02-27 12:05:42 $
004: * Version: $Revision: 1.49 $
005: *
006: * This library is part of OpenCms -
007: * the Open Source Content Management System
008: *
009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * For further information about Alkacon Software GmbH, please see the
022: * company website: http://www.alkacon.com
023: *
024: * For further information about OpenCms, please see the
025: * project website: http://www.opencms.org
026: *
027: * You should have received a copy of the GNU Lesser General Public
028: * License along with this library; if not, write to the Free Software
029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
030: */
031:
032: package org.opencms.db;
033:
034: import org.opencms.main.CmsLog;
035: import org.opencms.util.CmsStringUtil;
036:
037: import java.sql.Connection;
038: import java.util.ArrayList;
039: import java.util.List;
040: import java.util.Map;
041:
042: import org.apache.commons.collections.ExtendedProperties;
043: import org.apache.commons.dbcp.ConnectionFactory;
044: import org.apache.commons.dbcp.DriverManagerConnectionFactory;
045: import org.apache.commons.dbcp.PoolableConnectionFactory;
046: import org.apache.commons.dbcp.PoolingDriver;
047: import org.apache.commons.pool.impl.GenericKeyedObjectPool;
048: import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
049: import org.apache.commons.pool.impl.GenericObjectPool;
050:
051: /**
052: * Various methods to create DBCP pools.<p>
053: *
054: * Only JDBC Driver based pools are supported currently. JNDI DataSource
055: * based pools might be added probably later.<p>
056: *
057: * <b>Please note:</b> This class is subject to change in later versions.
058: * To obtain information about the connections, please use the
059: * {@link org.opencms.db.CmsSqlManager}.<p>
060: *
061: * @author Thomas Weckert
062: *
063: * @version $Revision: 1.49 $
064: *
065: * @since 6.0.0
066: */
067: public final class CmsDbPool {
068:
069: /** This prefix is required to make the JDBC DriverManager return pooled DBCP connections. */
070: public static final String DBCP_JDBC_URL_PREFIX = "jdbc:apache:commons:dbcp:";
071:
072: /** Prefix for database keys. */
073: public static final String KEY_DATABASE = "db.";
074:
075: /** Key for the database name. */
076: public static final String KEY_DATABASE_NAME = KEY_DATABASE
077: + "name";
078:
079: /** Key for the pool id. */
080: public static final String KEY_DATABASE_POOL = KEY_DATABASE
081: + "pool";
082:
083: /** Key for statement pooling. */
084: public static final String KEY_DATABASE_STATEMENTS = KEY_DATABASE
085: + "statements";
086:
087: /** Key for jdbc driver. */
088: public static final String KEY_JDBC_DRIVER = "jdbcDriver";
089:
090: /** Key for jdbc url. */
091: public static final String KEY_JDBC_URL = "jdbcUrl";
092:
093: /** Key for jdbc url params. */
094: public static final String KEY_JDBC_URL_PARAMS = KEY_JDBC_URL
095: + ".params";
096:
097: /** Key for maximum active connections. */
098: public static final String KEY_MAX_ACTIVE = "maxActive";
099:
100: /** Key for maximum idle connections. */
101: public static final String KEY_MAX_IDLE = "maxIdle";
102:
103: /** Key for maximum wait time. */
104: public static final String KEY_MAX_WAIT = "maxWait";
105:
106: /** Key for minimum idle time before a connection is subject to an eviction test. */
107: public static final String KEY_MIN_EVICTABLE_IDLE_TIME = "minEvictableIdleTime";
108:
109: /** Key for minimum number of connections kept open. */
110: public static final String KEY_MIN_IDLE = "minIdle";
111:
112: /** Key for number of tested connections per run. */
113: public static final String KEY_NUM_TESTS_PER_EVICTION_RUN = "numTestsPerEvictionRun";
114:
115: /** Key for database password. */
116: public static final String KEY_PASSWORD = "password";
117:
118: /** Key for default. */
119: public static final String KEY_POOL_DEFAULT = "default";
120:
121: /** Key for pool url. */
122: public static final String KEY_POOL_URL = "poolUrl";
123:
124: /** Key for pool user. */
125: public static final String KEY_POOL_USER = "user";
126:
127: /** Key for vfs pool. */
128: public static final String KEY_POOL_VFS = "vfs";
129:
130: /** Key for pooling flag. */
131: public static final String KEY_POOLING = "pooling";
132:
133: /** Key for test on borrow flag. */
134: public static final String KEY_TEST_ON_BORROW = "testOnBorrow";
135:
136: /** Key for test query. */
137: public static final String KEY_TEST_QUERY = "testQuery";
138:
139: /** Key for test while idle flag. */
140: public static final String KEY_TEST_WHILE_IDLE = "testWhileIdle";
141:
142: /** Key for time between two eviction runs. */
143: public static final String KEY_TIME_BETWEEN_EVICTION_RUNS = "timeBetweenEvictionRuns";
144:
145: /** Key for user name. */
146: public static final String KEY_USERNAME = "user";
147:
148: /** Key for "when pool exhausted" action. */
149: public static final String KEY_WHEN_EXHAUSTED_ACTION = "whenExhaustedAction";
150:
151: /** The name of the opencms default pool. */
152: public static final String OPENCMS_DEFAULT_POOL_NAME = "default";
153:
154: /** The default OpenCms JDBC pool URL. */
155: public static final String OPENCMS_DEFAULT_POOL_URL = "opencms:default";
156:
157: /** The prefix used for opencms JDBC pools. */
158: public static final String OPENCMS_URL_PREFIX = "opencms:";
159:
160: /**
161: * Default constructor.<p>
162: *
163: * Nobody is allowed to create an instance of this class!
164: */
165: private CmsDbPool() {
166:
167: super ();
168: }
169:
170: /**
171: * Creates a JDBC DriverManager based DBCP connection pool.<p>
172: *
173: * @param configuration the configuration (opencms.properties)
174: * @param key the key of the database pool in the configuration
175: * @return String the URL to access the created DBCP pool
176: * @throws Exception if the pool could not be initialized
177: */
178: public static PoolingDriver createDriverManagerConnectionPool(
179: Map configuration, String key) throws Exception {
180:
181: ExtendedProperties config;
182: if (configuration instanceof ExtendedProperties) {
183: config = (ExtendedProperties) configuration;
184: } else {
185: config = new ExtendedProperties();
186: config.putAll(configuration);
187: }
188:
189: // read the values of the pool configuration specified by the given key
190: String jdbcDriver = config.getString(KEY_DATABASE_POOL + '.'
191: + key + '.' + KEY_JDBC_DRIVER);
192: String jdbcUrl = config.getString(KEY_DATABASE_POOL + '.' + key
193: + '.' + KEY_JDBC_URL);
194: String jdbcUrlParams = config.getString(KEY_DATABASE_POOL + '.'
195: + key + '.' + KEY_JDBC_URL_PARAMS);
196: int maxActive = config.getInteger(KEY_DATABASE_POOL + '.' + key
197: + '.' + KEY_MAX_ACTIVE, 10);
198: int maxWait = config.getInteger(KEY_DATABASE_POOL + '.' + key
199: + '.' + KEY_MAX_WAIT, 2000);
200: int maxIdle = config.getInteger(KEY_DATABASE_POOL + '.' + key
201: + '.' + KEY_MAX_IDLE, 5);
202: int minEvictableIdleTime = config.getInteger(KEY_DATABASE_POOL
203: + '.' + key + '.' + KEY_MIN_EVICTABLE_IDLE_TIME,
204: 1800000);
205: int minIdle = config.getInteger(KEY_DATABASE_POOL + '.' + key
206: + '.' + KEY_MIN_IDLE, 0);
207: int numTestsPerEvictionRun = config.getInteger(
208: KEY_DATABASE_POOL + '.' + key + '.'
209: + KEY_NUM_TESTS_PER_EVICTION_RUN, 3);
210: int timeBetweenEvictionRuns = config.getInteger(
211: KEY_DATABASE_POOL + '.' + key + '.'
212: + KEY_TIME_BETWEEN_EVICTION_RUNS, 3600000);
213: String testQuery = config.getString(KEY_DATABASE_POOL + '.'
214: + key + '.' + KEY_TEST_QUERY);
215: String username = config.getString(KEY_DATABASE_POOL + '.'
216: + key + '.' + KEY_USERNAME);
217: String password = config.getString(KEY_DATABASE_POOL + '.'
218: + key + '.' + KEY_PASSWORD);
219: String poolUrl = config.getString(KEY_DATABASE_POOL + '.' + key
220: + '.' + KEY_POOL_URL);
221: String whenExhaustedActionValue = config.getString(
222: KEY_DATABASE_POOL + '.' + key + '.'
223: + KEY_WHEN_EXHAUSTED_ACTION).trim();
224: byte whenExhaustedAction = 0;
225: boolean testOnBorrow = Boolean.valueOf(
226: config.getString(
227: KEY_DATABASE_POOL + '.' + key + '.'
228: + KEY_TEST_ON_BORROW, "false").trim())
229: .booleanValue();
230: boolean testWhileIdle = Boolean.valueOf(
231: config.getString(
232: KEY_DATABASE_POOL + '.' + key + '.'
233: + KEY_TEST_WHILE_IDLE, "false").trim())
234: .booleanValue();
235:
236: if ("block".equalsIgnoreCase(whenExhaustedActionValue)) {
237: whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
238: } else if ("fail".equalsIgnoreCase(whenExhaustedActionValue)) {
239: whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
240: } else if ("grow".equalsIgnoreCase(whenExhaustedActionValue)) {
241: whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
242: } else {
243: whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
244: }
245:
246: if ("".equals(testQuery)) {
247: testQuery = null;
248: }
249:
250: if (username == null) {
251: username = "";
252: }
253:
254: if (password == null) {
255: password = "";
256: }
257:
258: // read the values of the statement pool configuration specified by the given key
259: boolean poolingStmts = Boolean.valueOf(
260: config.getString(
261: KEY_DATABASE_STATEMENTS + '.' + key + '.'
262: + KEY_POOLING, CmsStringUtil.TRUE)
263: .trim()).booleanValue();
264: int maxActiveStmts = config.getInteger(KEY_DATABASE_STATEMENTS
265: + '.' + key + '.' + KEY_MAX_ACTIVE, 25);
266: int maxWaitStmts = config.getInteger(KEY_DATABASE_STATEMENTS
267: + '.' + key + '.' + KEY_MAX_WAIT, 250);
268: int maxIdleStmts = config.getInteger(KEY_DATABASE_STATEMENTS
269: + '.' + key + '.' + KEY_MAX_IDLE, 15);
270: String whenStmtsExhaustedActionValue = config
271: .getString(KEY_DATABASE_STATEMENTS + '.' + key + '.'
272: + KEY_WHEN_EXHAUSTED_ACTION);
273: byte whenStmtsExhaustedAction = GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW;
274: if (whenStmtsExhaustedActionValue != null) {
275: whenStmtsExhaustedActionValue = whenStmtsExhaustedActionValue
276: .trim();
277: whenStmtsExhaustedAction = ("block"
278: .equalsIgnoreCase(whenStmtsExhaustedActionValue)) ? GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK
279: : ("fail"
280: .equalsIgnoreCase(whenStmtsExhaustedActionValue)) ? GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL
281: : GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW;
282: }
283:
284: // create an instance of the JDBC driver
285: Class.forName(jdbcDriver).newInstance();
286:
287: // initialize a keyed object pool to store connections
288: GenericObjectPool connectionPool = new GenericObjectPool(null);
289:
290: /* Abandoned pool configuration:
291: *
292: * In case the systems encounters "pool exhaustion" (runs out of connections),
293: * comment the above line with "new GenericObjectPool(null)" and uncomment the
294: * 5 lines below. This will generate an "abandoned pool" configuration that logs
295: * abandoned connections to the System.out. Unfortunatly this code is deprecated,
296: * so to avoid code warnings it's also disabled here.
297: * Tested with commons-pool v 1.2.
298: */
299:
300: // AbandonedConfig abandonedConfig = new AbandonedConfig();
301: // abandonedConfig.setLogAbandoned(true);
302: // abandonedConfig.setRemoveAbandoned(true);
303: // abandonedConfig.setRemoveAbandonedTimeout(5);
304: // GenericObjectPool connectionPool = new AbandonedObjectPool(null, abandonedConfig);
305: // initialize an object pool to store connections
306: connectionPool.setMaxActive(maxActive);
307: connectionPool.setMaxIdle(maxIdle);
308: connectionPool.setMinIdle(minIdle);
309: connectionPool.setMaxWait(maxWait);
310: connectionPool.setWhenExhaustedAction(whenExhaustedAction);
311:
312: if (testQuery != null) {
313: connectionPool.setTestOnBorrow(testOnBorrow);
314: connectionPool.setTestWhileIdle(testWhileIdle);
315: connectionPool
316: .setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRuns);
317: connectionPool
318: .setNumTestsPerEvictionRun(numTestsPerEvictionRun);
319: connectionPool
320: .setMinEvictableIdleTimeMillis(minEvictableIdleTime);
321: }
322:
323: // initialize a connection factory to make the DriverManager taking connections from the pool
324: if (jdbcUrlParams != null) {
325: jdbcUrl += jdbcUrlParams;
326: }
327:
328: ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
329: jdbcUrl, username, password);
330:
331: // Set up statement pool, if desired
332: GenericKeyedObjectPoolFactory statementFactory = null;
333: if (poolingStmts) {
334: statementFactory = new GenericKeyedObjectPoolFactory(null,
335: maxActiveStmts, whenStmtsExhaustedAction,
336: maxWaitStmts, maxIdleStmts);
337: }
338:
339: // initialize a factory to obtain pooled connections and prepared statements
340: new PoolableConnectionFactory(connectionFactory,
341: connectionPool, statementFactory, testQuery, false,
342: true);
343:
344: // initialize a new pooling driver using the pool
345: PoolingDriver driver = new PoolingDriver();
346: driver.registerPool(poolUrl, connectionPool);
347:
348: // try to connect once to the database to ensure it can be connected to at all
349: Connection con = connectionFactory.createConnection();
350: con.close();
351:
352: if (CmsLog.INIT.isInfoEnabled()) {
353: CmsLog.INIT.info(Messages.get().getBundle().key(
354: Messages.INIT_JDBC_POOL_2, poolUrl, jdbcUrl));
355: }
356: return driver;
357: }
358:
359: /**
360: * Returns the database pool name for a given configuration key.<p>
361: *
362: * @param configuration the configuration
363: * @param key a db pool configuration key
364: * @return the database pool name
365: */
366: public static String getDbPoolName(Map configuration, String key) {
367:
368: return configuration.get(
369: KEY_DATABASE_POOL + '.' + key + '.' + KEY_POOL_URL)
370: .toString();
371: }
372:
373: /**
374: * Returns a list of available database pool names.<p>
375: *
376: * @param configuration the configuration to read the pool names from
377: *
378: * @return a list of database pool names
379: */
380: public static List getDbPoolUrls(ExtendedProperties configuration) {
381:
382: List dbPoolNames = new ArrayList();
383: String[] driverPoolNames = configuration
384: .getStringArray(CmsDriverManager.CONFIGURATION_DB
385: + ".pools");
386:
387: for (int i = 0; i < driverPoolNames.length; i++) {
388: dbPoolNames.add(getDbPoolName(configuration,
389: driverPoolNames[i]));
390: }
391: return dbPoolNames;
392: }
393:
394: /**
395: * Returns the name of the default database connection pool.<p>
396: *
397: * @return the name of the default database connection pool
398: */
399: public static String getDefaultDbPoolName() {
400:
401: return OPENCMS_DEFAULT_POOL_NAME;
402: }
403: }
|