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: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $ 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 }