001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.dbcp;
019
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.sql.CallableStatement;
023 import java.sql.Connection;
024 import java.sql.DatabaseMetaData;
025 import java.sql.Driver;
026 import java.sql.DriverManager;
027 import java.sql.DriverPropertyInfo;
028 import java.sql.PreparedStatement;
029 import java.sql.SQLException;
030 import java.sql.SQLWarning;
031 import java.sql.Statement;
032 import java.util.HashMap;
033 import java.util.Map;
034 import java.util.NoSuchElementException;
035 import java.util.Properties;
036 import java.util.Set;
037
038 import org.apache.commons.jocl.JOCLContentHandler;
039 import org.apache.commons.pool.ObjectPool;
040 import org.xml.sax.SAXException;
041
042
043 /**
044 * A {@link Driver} implementation that obtains
045 * {@link Connection}s from a registered
046 * {@link ObjectPool}.
047 *
048 * @author Rodney Waldhoff
049 * @author Dirk Verbeeck
050 * @version $Revision: 902692 $ $Date: 2010-01-24 22:28:54 -0500 (Sun, 24 Jan 2010) $
051 */
052 public class PoolingDriver implements Driver {
053 /** Register myself with the {@link DriverManager}. */
054 static {
055 try {
056 DriverManager.registerDriver(new PoolingDriver());
057 } catch(Exception e) {
058 }
059 }
060
061 /** The map of registered pools. */
062 protected static final HashMap _pools = new HashMap();
063
064 /** Controls access to the underlying connection */
065 private static boolean accessToUnderlyingConnectionAllowed = false;
066
067 public PoolingDriver() {
068 }
069
070 /**
071 * Returns the value of the accessToUnderlyingConnectionAllowed property.
072 *
073 * @return true if access to the underlying is allowed, false otherwise.
074 */
075 public static synchronized boolean isAccessToUnderlyingConnectionAllowed() {
076 return accessToUnderlyingConnectionAllowed;
077 }
078
079 /**
080 * Sets the value of the accessToUnderlyingConnectionAllowed property.
081 * It controls if the PoolGuard allows access to the underlying connection.
082 * (Default: false)
083 *
084 * @param allow Access to the underlying connection is granted when true.
085 */
086 public static synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
087 accessToUnderlyingConnectionAllowed = allow;
088 }
089
090 /**
091 * WARNING: This method throws DbcpExceptions (RuntimeExceptions)
092 * and will be replaced by the protected getConnectionPool method.
093 *
094 * @deprecated This will be removed in a future version of DBCP.
095 */
096 public synchronized ObjectPool getPool(String name) {
097 try {
098 return getConnectionPool(name);
099 }
100 catch (Exception e) {
101 throw new DbcpException(e);
102 }
103 }
104
105 public synchronized ObjectPool getConnectionPool(String name) throws SQLException {
106 ObjectPool pool = (ObjectPool)(_pools.get(name));
107 if(null == pool) {
108 InputStream in = this.getClass().getResourceAsStream(String.valueOf(name) + ".jocl");
109 if (in == null) {
110 in = Thread.currentThread().getContextClassLoader(
111 ).getResourceAsStream(String.valueOf(name) + ".jocl");
112 }
113 if(null != in) {
114 JOCLContentHandler jocl = null;
115 try {
116 jocl = JOCLContentHandler.parse(in);
117 }
118 catch (SAXException e) {
119 throw (SQLException) new SQLException("Could not parse configuration file").initCause(e);
120 }
121 catch (IOException e) {
122 throw (SQLException) new SQLException("Could not load configuration file").initCause(e);
123 }
124 if(jocl.getType(0).equals(String.class)) {
125 pool = getPool((String)(jocl.getValue(0)));
126 if(null != pool) {
127 registerPool(name,pool);
128 }
129 } else {
130 pool = ((PoolableConnectionFactory)(jocl.getValue(0))).getPool();
131 if(null != pool) {
132 registerPool(name,pool);
133 }
134 }
135 }
136 else {
137 throw new SQLException("Configuration file not found");
138 }
139 }
140 return pool;
141 }
142
143 public synchronized void registerPool(String name, ObjectPool pool) {
144 _pools.put(name,pool);
145 }
146
147 public synchronized void closePool(String name) throws SQLException {
148 ObjectPool pool = (ObjectPool) _pools.get(name);
149 if (pool != null) {
150 _pools.remove(name);
151 try {
152 pool.close();
153 }
154 catch (Exception e) {
155 throw (SQLException) new SQLException("Error closing pool " + name).initCause(e);
156 }
157 }
158 }
159
160 public synchronized String[] getPoolNames(){
161 Set names = _pools.keySet();
162 return (String[]) names.toArray(new String[names.size()]);
163 }
164
165 public boolean acceptsURL(String url) throws SQLException {
166 try {
167 return url.startsWith(URL_PREFIX);
168 } catch(NullPointerException e) {
169 return false;
170 }
171 }
172
173 public Connection connect(String url, Properties info) throws SQLException {
174 if(acceptsURL(url)) {
175 ObjectPool pool = getConnectionPool(url.substring(URL_PREFIX_LEN));
176 if(null == pool) {
177 throw new SQLException("No pool found for " + url + ".");
178 } else {
179 try {
180 Connection conn = (Connection)(pool.borrowObject());
181 if (conn != null) {
182 conn = new PoolGuardConnectionWrapper(pool, conn);
183 }
184 return conn;
185 } catch(SQLException e) {
186 throw e;
187 } catch(NoSuchElementException e) {
188 throw (SQLException) new SQLException("Cannot get a connection, pool error: " + e.getMessage()).initCause(e);
189 } catch(RuntimeException e) {
190 throw e;
191 } catch(Exception e) {
192 throw (SQLException) new SQLException("Cannot get a connection, general error: " + e.getMessage()).initCause(e);
193 }
194 }
195 } else {
196 return null;
197 }
198 }
199
200 /**
201 * Invalidates the given connection.
202 *
203 * @param conn connection to invalidate
204 * @throws SQLException if the connection is not a
205 * <code>PoolGuardConnectionWrapper</code> or an error occurs invalidating
206 * the connection
207 * @since 1.2.2
208 */
209 public void invalidateConnection(Connection conn) throws SQLException {
210 if (conn instanceof PoolGuardConnectionWrapper) { // normal case
211 PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
212 ObjectPool pool = pgconn.pool;
213 Connection delegate = pgconn.delegate;
214 try {
215 pool.invalidateObject(delegate);
216 }
217 catch (Exception e) {
218 }
219 pgconn.delegate = null;
220 }
221 else {
222 throw new SQLException("Invalid connection class");
223 }
224 }
225
226 public int getMajorVersion() {
227 return MAJOR_VERSION;
228 }
229
230 public int getMinorVersion() {
231 return MINOR_VERSION;
232 }
233
234 public boolean jdbcCompliant() {
235 return true;
236 }
237
238 public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
239 return new DriverPropertyInfo[0];
240 }
241
242 /** My URL prefix */
243 protected static final String URL_PREFIX = "jdbc:apache:commons:dbcp:";
244 protected static final int URL_PREFIX_LEN = URL_PREFIX.length();
245
246 // version numbers
247 protected static final int MAJOR_VERSION = 1;
248 protected static final int MINOR_VERSION = 0;
249
250 /**
251 * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a
252 * closed connection cannot be used anymore.
253 */
254 static private class PoolGuardConnectionWrapper extends DelegatingConnection {
255
256 private final ObjectPool pool;
257 private Connection delegate;
258
259 PoolGuardConnectionWrapper(ObjectPool pool, Connection delegate) {
260 super(delegate);
261 this.pool = pool;
262 this.delegate = delegate;
263 }
264
265 protected void checkOpen() throws SQLException {
266 if(delegate == null) {
267 throw new SQLException("Connection is closed.");
268 }
269 }
270
271 public void close() throws SQLException {
272 if (delegate != null) {
273 this.delegate.close();
274 this.delegate = null;
275 super.setDelegate(null);
276 }
277 }
278
279 public boolean isClosed() throws SQLException {
280 if (delegate == null) {
281 return true;
282 }
283 return delegate.isClosed();
284 }
285
286 public void clearWarnings() throws SQLException {
287 checkOpen();
288 delegate.clearWarnings();
289 }
290
291 public void commit() throws SQLException {
292 checkOpen();
293 delegate.commit();
294 }
295
296 public Statement createStatement() throws SQLException {
297 checkOpen();
298 return new DelegatingStatement(this, delegate.createStatement());
299 }
300
301 public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
302 checkOpen();
303 return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency));
304 }
305
306 public boolean equals(Object obj) {
307 if (delegate == null){
308 return false;
309 }
310 return delegate.equals(obj);
311 }
312
313 public boolean getAutoCommit() throws SQLException {
314 checkOpen();
315 return delegate.getAutoCommit();
316 }
317
318 public String getCatalog() throws SQLException {
319 checkOpen();
320 return delegate.getCatalog();
321 }
322
323 public DatabaseMetaData getMetaData() throws SQLException {
324 checkOpen();
325 return delegate.getMetaData();
326 }
327
328 public int getTransactionIsolation() throws SQLException {
329 checkOpen();
330 return delegate.getTransactionIsolation();
331 }
332
333 public Map getTypeMap() throws SQLException {
334 checkOpen();
335 return delegate.getTypeMap();
336 }
337
338 public SQLWarning getWarnings() throws SQLException {
339 checkOpen();
340 return delegate.getWarnings();
341 }
342
343 public int hashCode() {
344 if (delegate == null){
345 return 0;
346 }
347 return delegate.hashCode();
348 }
349
350 public boolean isReadOnly() throws SQLException {
351 checkOpen();
352 return delegate.isReadOnly();
353 }
354
355 public String nativeSQL(String sql) throws SQLException {
356 checkOpen();
357 return delegate.nativeSQL(sql);
358 }
359
360 public CallableStatement prepareCall(String sql) throws SQLException {
361 checkOpen();
362 return new DelegatingCallableStatement(this, delegate.prepareCall(sql));
363 }
364
365 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
366 checkOpen();
367 return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency));
368 }
369
370 public PreparedStatement prepareStatement(String sql) throws SQLException {
371 checkOpen();
372 return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql));
373 }
374
375 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
376 checkOpen();
377 return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency));
378 }
379
380 public void rollback() throws SQLException {
381 checkOpen();
382 delegate.rollback();
383 }
384
385 public void setAutoCommit(boolean autoCommit) throws SQLException {
386 checkOpen();
387 delegate.setAutoCommit(autoCommit);
388 }
389
390 public void setCatalog(String catalog) throws SQLException {
391 checkOpen();
392 delegate.setCatalog(catalog);
393 }
394
395 public void setReadOnly(boolean readOnly) throws SQLException {
396 checkOpen();
397 delegate.setReadOnly(readOnly);
398 }
399
400 public void setTransactionIsolation(int level) throws SQLException {
401 checkOpen();
402 delegate.setTransactionIsolation(level);
403 }
404
405 public void setTypeMap(Map map) throws SQLException {
406 checkOpen();
407 delegate.setTypeMap(map);
408 }
409
410 public String toString() {
411 if (delegate == null){
412 return "NULL";
413 }
414 return delegate.toString();
415 }
416
417 public int getHoldability() throws SQLException {
418 checkOpen();
419 return delegate.getHoldability();
420 }
421
422 public void setHoldability(int holdability) throws SQLException {
423 checkOpen();
424 delegate.setHoldability(holdability);
425 }
426
427 public java.sql.Savepoint setSavepoint() throws SQLException {
428 checkOpen();
429 return delegate.setSavepoint();
430 }
431
432 public java.sql.Savepoint setSavepoint(String name) throws SQLException {
433 checkOpen();
434 return delegate.setSavepoint(name);
435 }
436
437 public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException {
438 checkOpen();
439 delegate.releaseSavepoint(savepoint);
440 }
441
442 public void rollback(java.sql.Savepoint savepoint) throws SQLException {
443 checkOpen();
444 delegate.rollback(savepoint);
445 }
446
447 public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
448 checkOpen();
449 return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability));
450 }
451
452 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
453 checkOpen();
454 return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
455 }
456
457 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
458 checkOpen();
459 return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys));
460 }
461
462 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
463 checkOpen();
464 return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
465 }
466
467 public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
468 checkOpen();
469 return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes));
470 }
471
472 public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
473 checkOpen();
474 return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
475 }
476
477 /**
478 * @see org.apache.commons.dbcp.DelegatingConnection#getDelegate()
479 */
480 public Connection getDelegate() {
481 if (isAccessToUnderlyingConnectionAllowed()) {
482 return super.getDelegate();
483 } else {
484 return null;
485 }
486 }
487
488 /**
489 * @see org.apache.commons.dbcp.DelegatingConnection#getInnermostDelegate()
490 */
491 public Connection getInnermostDelegate() {
492 if (isAccessToUnderlyingConnectionAllowed()) {
493 return super.getInnermostDelegate();
494 } else {
495 return null;
496 }
497 }
498 }
499 }