KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > accesslayer > ConnectionFactoryDBCPImpl


1 package org.apache.ojb.broker.accesslayer;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import org.apache.commons.dbcp.AbandonedConfig;
19 import org.apache.commons.dbcp.AbandonedObjectPool;
20 import org.apache.commons.dbcp.DriverManagerConnectionFactory;
21 import org.apache.commons.dbcp.PoolableConnectionFactory;
22 import org.apache.commons.dbcp.PoolingDataSource;
23 import org.apache.commons.pool.KeyedObjectPoolFactory;
24 import org.apache.commons.pool.ObjectPool;
25 import org.apache.commons.pool.impl.GenericKeyedObjectPool;
26 import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
27 import org.apache.commons.pool.impl.GenericObjectPool;
28 import org.apache.ojb.broker.PBKey;
29 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
30 import org.apache.ojb.broker.util.ClassHelper;
31 import org.apache.ojb.broker.util.logging.Logger;
32 import org.apache.ojb.broker.util.logging.LoggerFactory;
33 import org.apache.ojb.broker.util.logging.LoggerWrapperPrintWriter;
34
35 import javax.sql.DataSource JavaDoc;
36 import java.sql.Connection JavaDoc;
37 import java.sql.SQLException JavaDoc;
38 import java.util.Collection JavaDoc;
39 import java.util.Collections JavaDoc;
40 import java.util.HashMap JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.Map JavaDoc;
43 import java.util.Properties JavaDoc;
44
45 /**
46  * ConnectionFactory implementation using Commons DBCP and Commons Pool API
47  * to pool connections.
48  *
49  * Based on a proposal of Dirk Verbeek - Thanks.
50  *
51  * @author <a HREF="mailto:armin@codeAuLait.de">Armin Waibel</a>
52  * @version $Id: ConnectionFactoryDBCPImpl.java,v 1.10.2.5 2005/10/09 23:51:01 arminw Exp $
53  * @see <a HREF="http://jakarta.apache.org/commons/pool/">Commons Pool Website</a>
54  * @see <a HREF="http://jakarta.apache.org/commons/dbcp/">Commons DBCP Website</a>
55  */

56 public class ConnectionFactoryDBCPImpl extends ConnectionFactoryAbstractImpl
57 {
58
59     public static final String JavaDoc PARAM_NAME_UNWRAP_ALLOWED = "accessToUnderlyingConnectionAllowed";
60     public static final String JavaDoc PARAM_NAME_POOL_STATEMENTS = "poolPreparedStatements";
61     public static final String JavaDoc PARAM_NAME_STATEMENT_POOL_MAX_TOTAL = "maxOpenPreparedStatements";
62
63     private Logger log = LoggerFactory.getLogger(ConnectionFactoryDBCPImpl.class);
64
65     /** Key=PBKey, value=ObjectPool. */
66     private Map JavaDoc poolMap = Collections.synchronizedMap(new HashMap JavaDoc());
67     /** Key=PBKey, value=PoolingDataSource. */
68     private Map JavaDoc dsMap = Collections.synchronizedMap(new HashMap JavaDoc());
69     /** Synchronize object for operations not synchronized on Map only. */
70     private final Object JavaDoc poolSynch = new Object JavaDoc();
71
72     public Connection JavaDoc checkOutJdbcConnection(JdbcConnectionDescriptor jcd) throws LookupException
73     {
74         final DataSource ds = getDataSource(jcd);
75
76         // Returned DS is never null, exception are logged by getDataSource and gets
77
// re-thrown here since we don't catch them
78

79         Connection JavaDoc conn;
80         try
81         {
82             conn = ds.getConnection();
83         }
84         catch (SQLException JavaDoc e)
85         {
86             throw new LookupException("Could not get connection from DBCP DataSource", e);
87         }
88         return conn;
89     }
90
91     public void releaseJdbcConnection(JdbcConnectionDescriptor jcd, Connection JavaDoc con)
92             throws LookupException
93     {
94         try
95         {
96             // We are using datasources, thus close returns connection to pool
97
con.close();
98         }
99         catch (SQLException JavaDoc e)
100         {
101             log.warn("Connection close failed", e);
102         }
103     }
104
105     /**
106      * Closes all managed pools.
107      */

108     public void releaseAllResources()
109     {
110         super.releaseAllResources();
111         synchronized (poolSynch)
112         {
113             if (!poolMap.isEmpty())
114             {
115                 Collection JavaDoc pools = poolMap.values();
116                 Iterator JavaDoc iterator = pools.iterator();
117                 ObjectPool op = null;
118                 while (iterator.hasNext())
119                 {
120                     try
121                     {
122                         op = (ObjectPool) iterator.next();
123                         op.close();
124                     }
125                     catch (Exception JavaDoc e)
126                     {
127                         log.error("Exception occured while closing ObjectPool " + op, e);
128                     }
129                 }
130                 poolMap.clear();
131             }
132             dsMap.clear();
133         }
134     }
135
136     /**
137      * Returns the DBCP DataSource for the specified connection descriptor,
138      * after creating a new DataSource if needed.
139      * @param jcd the descriptor for which to return a DataSource
140      * @return a DataSource, after creating a new pool if needed.
141      * Guaranteed to never be null.
142      * @throws LookupException if pool is not in cache and cannot be created
143      */

144     protected DataSource getDataSource(JdbcConnectionDescriptor jcd)
145             throws LookupException
146     {
147         final PBKey key = jcd.getPBKey();
148         DataSource ds = (DataSource) dsMap.get(key);
149         if (ds == null)
150         {
151             // Found no pool for PBKey
152
try
153             {
154                 synchronized (poolSynch)
155                 {
156                     // Setup new object pool
157
ObjectPool pool = setupPool(jcd);
158                     poolMap.put(key, pool);
159                     // Wrap the underlying object pool as DataSource
160
ds = wrapAsDataSource(jcd, pool);
161                     dsMap.put(key, ds);
162                 }
163             }
164             catch (Exception JavaDoc e)
165             {
166                 log.error("Could not setup DBCP DataSource for " + jcd, e);
167                 throw new LookupException(e);
168             }
169         }
170         return ds;
171     }
172
173     /**
174      * Returns a new ObjectPool for the specified connection descriptor.
175      * Override this method to setup your own pool.
176      * @param jcd the connection descriptor for which to set up the pool
177      * @return a newly created object pool
178      */

179     protected ObjectPool setupPool(JdbcConnectionDescriptor jcd)
180     {
181         log.info("Create new ObjectPool for DBCP connections:" + jcd);
182
183         try
184         {
185             ClassHelper.newInstance(jcd.getDriver());
186         }
187         catch (InstantiationException JavaDoc e)
188         {
189             log.fatal("Unable to instantiate the driver class: " + jcd.getDriver() + " in ConnectionFactoryDBCImpl!" , e);
190         }
191         catch (IllegalAccessException JavaDoc e)
192         {
193             log.fatal("IllegalAccessException while instantiating the driver class: " + jcd.getDriver() + " in ConnectionFactoryDBCImpl!" , e);
194         }
195         catch (ClassNotFoundException JavaDoc e)
196         {
197             log.fatal("Could not find the driver class : " + jcd.getDriver() + " in ConnectionFactoryDBCImpl!" , e);
198         }
199
200         // Get the configuration for the connection pool
201
GenericObjectPool.Config conf = jcd.getConnectionPoolDescriptor().getObjectPoolConfig();
202
203         // Get the additional abandoned configuration
204
AbandonedConfig ac = jcd.getConnectionPoolDescriptor().getAbandonedConfig();
205
206         // Create the ObjectPool that serves as the actual pool of connections.
207
final ObjectPool connectionPool = createConnectionPool(conf, ac);
208
209         // Create a DriverManager-based ConnectionFactory that
210
// the connectionPool will use to create Connection instances
211
final org.apache.commons.dbcp.ConnectionFactory connectionFactory;
212         connectionFactory = createConnectionFactory(jcd);
213
214         // Create PreparedStatement object pool (if any)
215
KeyedObjectPoolFactory statementPoolFactory = createStatementPoolFactory(jcd);
216
217         // Set validation query and auto-commit mode
218
final String JavaDoc validationQuery;
219         final boolean defaultAutoCommit;
220         final boolean defaultReadOnly = false;
221         validationQuery = jcd.getConnectionPoolDescriptor().getValidationQuery();
222         defaultAutoCommit = (jcd.getUseAutoCommit() != JdbcConnectionDescriptor.AUTO_COMMIT_SET_FALSE);
223
224         //
225
// Now we'll create the PoolableConnectionFactory, which wraps
226
// the "real" Connections created by the ConnectionFactory with
227
// the classes that implement the pooling functionality.
228
//
229
final PoolableConnectionFactory poolableConnectionFactory;
230         poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,
231                 connectionPool,
232                 statementPoolFactory,
233                 validationQuery,
234                 defaultReadOnly,
235                 defaultAutoCommit,
236                 ac);
237         return poolableConnectionFactory.getPool();
238     }
239
240     protected ObjectPool createConnectionPool(GenericObjectPool.Config config,
241                                               AbandonedConfig ac)
242     {
243         final GenericObjectPool connectionPool;
244         final boolean doRemoveAbandoned = ac != null && ac.getRemoveAbandoned();
245
246         if (doRemoveAbandoned) {
247             connectionPool = new AbandonedObjectPool(null, ac);
248         } else {
249             connectionPool = new GenericObjectPool();
250         }
251         connectionPool.setMaxActive(config.maxActive);
252         connectionPool.setMaxIdle(config.maxIdle);
253         connectionPool.setMinIdle(config.minIdle);
254         connectionPool.setMaxWait(config.maxWait);
255         connectionPool.setTestOnBorrow(config.testOnBorrow);
256         connectionPool.setTestOnReturn(config.testOnReturn);
257         connectionPool.setTimeBetweenEvictionRunsMillis(config.timeBetweenEvictionRunsMillis);
258         connectionPool.setNumTestsPerEvictionRun(config.numTestsPerEvictionRun);
259         connectionPool.setMinEvictableIdleTimeMillis(config.minEvictableIdleTimeMillis);
260         connectionPool.setTestWhileIdle(config.testWhileIdle);
261         return connectionPool;
262     }
263
264     protected KeyedObjectPoolFactory createStatementPoolFactory(JdbcConnectionDescriptor jcd)
265     {
266         final String JavaDoc platform = jcd.getDbms();
267         if (platform.startsWith("Oracle9i"))
268         {
269             // mkalen: let the platform set Oracle-specific statement pooling
270
return null;
271         }
272
273         // Set up statement pool, if desired
274
GenericKeyedObjectPoolFactory statementPoolFactory = null;
275         final Properties JavaDoc properties = jcd.getConnectionPoolDescriptor().getDbcpProperties();
276         final String JavaDoc poolStmtParam = properties.getProperty(PARAM_NAME_POOL_STATEMENTS);
277         if (poolStmtParam != null && Boolean.valueOf(poolStmtParam).booleanValue())
278         {
279             int maxOpenPreparedStatements = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
280             final String JavaDoc maxOpenPrepStmtString = properties.getProperty(PARAM_NAME_STATEMENT_POOL_MAX_TOTAL);
281             if (maxOpenPrepStmtString != null)
282             {
283                 maxOpenPreparedStatements = Integer.parseInt(maxOpenPrepStmtString);
284             }
285             // Use the same values as Commons DBCP BasicDataSource
286
statementPoolFactory = new GenericKeyedObjectPoolFactory(null,
287                         -1, // unlimited maxActive (per key)
288
GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL,
289                         0, // maxWait
290
1, // maxIdle (per key)
291
maxOpenPreparedStatements);
292         }
293         return statementPoolFactory;
294     }
295
296     /**
297      * Wraps the specified object pool for connections as a DataSource.
298      *
299      * @param jcd the OJB connection descriptor for the pool to be wrapped
300      * @param connectionPool the connection pool to be wrapped
301      * @return a DataSource attached to the connection pool.
302      * Connections will be wrapped using DBCP PoolGuard, that will not allow
303      * unwrapping unless the "accessToUnderlyingConnectionAllowed=true" configuration
304      * is specified.
305      */

306     protected DataSource wrapAsDataSource(JdbcConnectionDescriptor jcd,
307                                           ObjectPool connectionPool)
308     {
309         final boolean allowConnectionUnwrap;
310         if (jcd == null)
311         {
312             allowConnectionUnwrap = false;
313         }
314         else
315         {
316             final Properties JavaDoc properties = jcd.getConnectionPoolDescriptor().getDbcpProperties();
317             final String JavaDoc allowConnectionUnwrapParam;
318             allowConnectionUnwrapParam = properties.getProperty(PARAM_NAME_UNWRAP_ALLOWED);
319             allowConnectionUnwrap = allowConnectionUnwrapParam != null &&
320                     Boolean.valueOf(allowConnectionUnwrapParam).booleanValue();
321         }
322         final PoolingDataSource dataSource;
323         dataSource = new PoolingDataSource(connectionPool);
324         dataSource.setAccessToUnderlyingConnectionAllowed(allowConnectionUnwrap);
325
326         if(jcd != null)
327         {
328             final AbandonedConfig ac = jcd.getConnectionPoolDescriptor().getAbandonedConfig();
329             if (ac.getRemoveAbandoned() && ac.getLogAbandoned()) {
330                 final LoggerWrapperPrintWriter loggerPiggyBack;
331                 loggerPiggyBack = new LoggerWrapperPrintWriter(log, Logger.ERROR);
332                 dataSource.setLogWriter(loggerPiggyBack);
333             }
334         }
335         return dataSource;
336     }
337
338     /**
339      * Creates a DriverManager-based ConnectionFactory for creating the Connection
340      * instances to feed into the object pool of the specified jcd-alias.
341      * <p>
342      * <b>NB!</b> If you override this method to specify your own ConnectionFactory
343      * you <em>must</em> make sure that you follow OJB's lifecycle contract defined in the
344      * {@link org.apache.ojb.broker.platforms.Platform} API - ie that you call
345      * initializeJdbcConnection when a new Connection is created. For convenience, use
346      * {@link ConnectionFactoryAbstractImpl#initializeJdbcConnection} instead of Platform call.
347      * <p>
348      * The above is automatically true if you re-use the inner class {@link ConPoolFactory}
349      * below and just override this method for additional user-defined "tweaks".
350      *
351      * @param jcd the jdbc-connection-alias for which we are creating a ConnectionFactory
352      * @return a DriverManager-based ConnectionFactory that creates Connection instances
353      * using DriverManager, and that follows the lifecycle contract defined in OJB
354      * {@link org.apache.ojb.broker.platforms.Platform} API.
355      */

356     protected org.apache.commons.dbcp.ConnectionFactory createConnectionFactory(JdbcConnectionDescriptor jcd)
357     {
358         final ConPoolFactory result;
359         final Properties JavaDoc properties = getJdbcProperties(jcd);
360         result = new ConPoolFactory(jcd, properties);
361         return result;
362     }
363
364     // ----- deprecated methods, to be removed -----
365

366     /**
367      * mkalen: Left for binary API-compatibility with OJB 1.0.3 (don't break users' factories)
368      * @deprecated since OJB 1.0.4,
369      * please use {@link #createConnectionPool(org.apache.commons.pool.impl.GenericObjectPool.Config, org.apache.commons.dbcp.AbandonedConfig)}
370      */

371     protected ObjectPool createObjectPool(GenericObjectPool.Config config)
372     {
373         return createConnectionPool(config, null);
374     }
375
376     /**
377      * mkalen: Left for binary API-compatibility with OJB 1.0.3 (don't break users' factories)
378      * @deprecated since OJB 1.0.4,
379      * please use {@link #wrapAsDataSource(org.apache.ojb.broker.metadata.JdbcConnectionDescriptor, org.apache.commons.pool.ObjectPool)}
380      */

381     protected PoolingDataSource createPoolingDataSource(ObjectPool connectionPool)
382     {
383         // mkalen: not a nice cast but we do not want to break signature and it is safe
384
// since any new implementations will not be based on this method and the wrapper-
385
// call here goes to code we control (where we know it's PoolingDataSource)
386
return (PoolingDataSource) wrapAsDataSource(null, connectionPool);
387     }
388
389     // ----- end deprecated methods -----
390

391     //**************************************************************************************
392
// Inner classes
393
//************************************************************************************
394

395     /**
396      * Inner class used as factory for DBCP connection pooling.
397      * Adhers to OJB platform specification by calling platform-specific init methods
398      * on newly created connections.
399      * @see org.apache.ojb.broker.platforms.Platform#initializeJdbcConnection
400      */

401     class ConPoolFactory extends DriverManagerConnectionFactory
402     {
403
404         private final JdbcConnectionDescriptor jcd;
405
406         public ConPoolFactory(JdbcConnectionDescriptor jcd, Properties JavaDoc properties)
407         {
408             super(getDbURL(jcd), properties);
409             this.jcd = jcd;
410         }
411
412         public Connection JavaDoc createConnection() throws SQLException JavaDoc
413         {
414             final Connection JavaDoc conn = super.createConnection();
415             if (conn != null)
416             {
417                 try
418                 {
419                     initializeJdbcConnection(conn, jcd);
420                 }
421                 catch (LookupException e)
422                 {
423                     log.error("Platform dependent initialization of connection failed", e);
424                 }
425             }
426             return conn;
427         }
428
429     }
430
431 }
432
Popular Tags