Coverage Report - org.apache.commons.dbcp.datasources.InstanceKeyDataSource
 
Classes in this File Line Coverage Branch Coverage Complexity
InstanceKeyDataSource
48%
108/222
42%
27/64
2.333
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  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  
 package org.apache.commons.dbcp.datasources;
 19  
 
 20  
 import java.io.Serializable;
 21  
 import java.io.PrintWriter;
 22  
 import java.sql.Connection;
 23  
 import java.sql.SQLException;
 24  
 import java.util.NoSuchElementException;
 25  
 import java.util.Properties;
 26  
 
 27  
 import javax.naming.Context;
 28  
 import javax.naming.InitialContext;
 29  
 import javax.naming.NamingException;
 30  
 import javax.naming.Reference;
 31  
 import javax.naming.StringRefAddr;
 32  
 import javax.naming.Referenceable;
 33  
 import javax.sql.ConnectionPoolDataSource;
 34  
 import javax.sql.DataSource;
 35  
 import javax.sql.PooledConnection;
 36  
 
 37  
 import org.apache.commons.dbcp.SQLNestedException;
 38  
 import org.apache.commons.pool.impl.GenericObjectPool;
 39  
 
 40  
 /**
 41  
  * <p>The base class for <code>SharedPoolDataSource</code> and 
 42  
  * <code>PerUserPoolDataSource</code>.  Many of the configuration properties
 43  
  * are shared and defined here.  This class is declared public in order
 44  
  * to allow particular usage with commons-beanutils; do not make direct
 45  
  * use of it outside of commons-dbcp.
 46  
  * </p>
 47  
  *
 48  
  * <p>
 49  
  * A J2EE container will normally provide some method of initializing the
 50  
  * <code>DataSource</code> whose attributes are presented
 51  
  * as bean getters/setters and then deploying it via JNDI.  It is then
 52  
  * available to an application as a source of pooled logical connections to 
 53  
  * the database.  The pool needs a source of physical connections.  This
 54  
  * source is in the form of a <code>ConnectionPoolDataSource</code> that
 55  
  * can be specified via the {@link #setDataSourceName(String)} used to
 56  
  * lookup the source via JNDI.
 57  
  * </p>
 58  
  *
 59  
  * <p>
 60  
  * Although normally used within a JNDI environment, A DataSource
 61  
  * can be instantiated and initialized as any bean.  In this case the 
 62  
  * <code>ConnectionPoolDataSource</code> will likely be instantiated in
 63  
  * a similar manner.  This class allows the physical source of connections
 64  
  * to be attached directly to this pool using the 
 65  
  * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
 66  
  * </p>
 67  
  *
 68  
  * <p>
 69  
  * The dbcp package contains an adapter, 
 70  
  * {@link org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS},
 71  
  * that can be used to allow the use of <code>DataSource</code>'s based on this
 72  
  * class with jdbc driver implementations that do not supply a 
 73  
  * <code>ConnectionPoolDataSource</code>, but still
 74  
  * provide a {@link java.sql.Driver} implementation.
 75  
  * </p>
 76  
  *
 77  
  * <p>
 78  
  * The <a href="package-summary.html">package documentation</a> contains an 
 79  
  * example using Apache Tomcat and JNDI and it also contains a non-JNDI example. 
 80  
  * </p>
 81  
  *
 82  
  * @author John D. McNally
 83  
  * @version $Revision: 1023401 $ $Date: 2010-10-16 21:54:24 -0400 (Sat, 16 Oct 2010) $
 84  
  */
 85  
 public abstract class InstanceKeyDataSource
 86  
         implements DataSource, Referenceable, Serializable {
 87  
     private static final long serialVersionUID = -4243533936955098795L;
 88  
     private static final String GET_CONNECTION_CALLED 
 89  
             = "A Connection was already requested from this source, " 
 90  
             + "further initialization is not allowed.";
 91  
     private static final String BAD_TRANSACTION_ISOLATION
 92  
         = "The requested TransactionIsolation level is invalid.";
 93  
     /**
 94  
     * Internal constant to indicate the level is not set. 
 95  
     */
 96  
     protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
 97  
     
 98  
     /** Guards property setters - once true, setters throw IllegalStateException */
 99  166
     private volatile boolean getConnectionCalled = false;
 100  
 
 101  
     /** Underlying source of PooledConnections */
 102  166
     private ConnectionPoolDataSource dataSource = null;
 103  
     
 104  
     /** DataSource Name used to find the ConnectionPoolDataSource */
 105  166
     private String dataSourceName = null;
 106  
     
 107  
     // Default connection properties
 108  166
     private boolean defaultAutoCommit = false;
 109  166
     private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
 110  166
     private boolean defaultReadOnly = false;
 111  
     
 112  
     /** Description */
 113  166
     private String description = null;
 114  
     
 115  
     /** Environment that may be used to set up a jndi initial context. */
 116  166
     Properties jndiEnvironment = null;
 117  
     
 118  
     /** Login TimeOut in seconds */
 119  166
     private int loginTimeout = 0;
 120  
     
 121  
     /** Log stream */
 122  166
     private PrintWriter logWriter = null;
 123  
     
 124  
     // Pool properties
 125  166
     private boolean _testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
 126  166
     private boolean _testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
 127  166
     private int _timeBetweenEvictionRunsMillis = (int)
 128  
         Math.min(Integer.MAX_VALUE,
 129  
                  GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
 130  166
     private int _numTestsPerEvictionRun = 
 131  
         GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
 132  166
     private int _minEvictableIdleTimeMillis = (int)
 133  
     Math.min(Integer.MAX_VALUE,
 134  
              GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
 135  166
     private boolean _testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
 136  166
     private String validationQuery = null;
 137  166
     private boolean rollbackAfterValidation = false;
 138  
     
 139  
     /** true iff one of the setters for testOnBorrow, testOnReturn, testWhileIdle has been called. */
 140  166
     private boolean testPositionSet = false;
 141  
 
 142  
     /** Instance key */
 143  166
     protected String instanceKey = null;
 144  
 
 145  
     /**
 146  
      * Default no-arg constructor for Serialization
 147  
      */
 148  166
     public InstanceKeyDataSource() {
 149  166
         defaultAutoCommit = true;
 150  166
     }
 151  
 
 152  
     /**
 153  
      * Throws an IllegalStateException, if a PooledConnection has already
 154  
      * been requested.
 155  
      */
 156  
     protected void assertInitializationAllowed()
 157  
         throws IllegalStateException {
 158  780
         if (getConnectionCalled) {
 159  0
             throw new IllegalStateException(GET_CONNECTION_CALLED);
 160  
         }
 161  780
     }
 162  
 
 163  
     /**
 164  
      * Close the connection pool being maintained by this datasource.
 165  
      */
 166  
     public abstract void close() throws Exception;
 167  
     
 168  
     protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey);
 169  
 
 170  
     /* JDBC_4_ANT_KEY_BEGIN */
 171  
     public boolean isWrapperFor(Class<?> iface) throws SQLException {
 172  0
         return false;
 173  
     }
 174  
 
 175  
     public <T> T unwrap(Class<T> iface) throws SQLException {
 176  0
         throw new SQLException("InstanceKeyDataSource is not a wrapper.");
 177  
     }
 178  
     /* JDBC_4_ANT_KEY_END */
 179  
 
 180  
     // -------------------------------------------------------------------
 181  
     // Properties
 182  
 
 183  
     /**
 184  
      * Get the value of connectionPoolDataSource.  This method will return
 185  
      * null, if the backing datasource is being accessed via jndi.
 186  
      *
 187  
      * @return value of connectionPoolDataSource.
 188  
      */
 189  
     public ConnectionPoolDataSource getConnectionPoolDataSource() {
 190  0
         return dataSource;
 191  
     }
 192  
     
 193  
     /**
 194  
      * Set the backend ConnectionPoolDataSource.  This property should not be
 195  
      * set if using jndi to access the datasource.
 196  
      *
 197  
      * @param v  Value to assign to connectionPoolDataSource.
 198  
      */
 199  
     public void setConnectionPoolDataSource(ConnectionPoolDataSource v) {
 200  154
         assertInitializationAllowed();
 201  154
         if (dataSourceName != null) {
 202  0
             throw new IllegalStateException(
 203  
                 "Cannot set the DataSource, if JNDI is used.");
 204  
         }
 205  154
         if (dataSource != null) 
 206  
         {
 207  0
             throw new IllegalStateException(
 208  
                 "The CPDS has already been set. It cannot be altered.");
 209  
         }
 210  154
         dataSource = v;
 211  154
         instanceKey = InstanceKeyObjectFactory.registerNewInstance(this);
 212  154
     }
 213  
 
 214  
     /**
 215  
      * Get the name of the ConnectionPoolDataSource which backs this pool.
 216  
      * This name is used to look up the datasource from a jndi service 
 217  
      * provider.
 218  
      *
 219  
      * @return value of dataSourceName.
 220  
      */
 221  
     public String getDataSourceName() {
 222  4569
         return dataSourceName;
 223  
     }
 224  
     
 225  
     /**
 226  
      * Set the name of the ConnectionPoolDataSource which backs this pool.
 227  
      * This name is used to look up the datasource from a jndi service 
 228  
      * provider.
 229  
      *
 230  
      * @param v  Value to assign to dataSourceName.
 231  
      */
 232  
     public void setDataSourceName(String v) {
 233  4
         assertInitializationAllowed();
 234  4
         if (dataSource != null) {
 235  0
             throw new IllegalStateException(
 236  
                 "Cannot set the JNDI name for the DataSource, if already " +
 237  
                 "set using setConnectionPoolDataSource.");
 238  
         }
 239  4
         if (dataSourceName != null) 
 240  
         {
 241  0
             throw new IllegalStateException(
 242  
                 "The DataSourceName has already been set. " + 
 243  
                 "It cannot be altered.");
 244  
         }
 245  4
         this.dataSourceName = v;
 246  4
         instanceKey = InstanceKeyObjectFactory.registerNewInstance(this);
 247  4
     }
 248  
 
 249  
     /** 
 250  
      * Get the value of defaultAutoCommit, which defines the state of 
 251  
      * connections handed out from this pool.  The value can be changed
 252  
      * on the Connection using Connection.setAutoCommit(boolean).
 253  
      * The default is true.
 254  
      *
 255  
      * @return value of defaultAutoCommit.
 256  
      */
 257  
     public boolean isDefaultAutoCommit() {
 258  7653
         return defaultAutoCommit;
 259  
     }
 260  
     
 261  
     /**
 262  
      * Set the value of defaultAutoCommit, which defines the state of 
 263  
      * connections handed out from this pool.  The value can be changed
 264  
      * on the Connection using Connection.setAutoCommit(boolean).
 265  
      * The default is true.
 266  
      *
 267  
      * @param v  Value to assign to defaultAutoCommit.
 268  
      */
 269  
     public void setDefaultAutoCommit(boolean v) {
 270  0
         assertInitializationAllowed();
 271  0
         this.defaultAutoCommit = v;
 272  0
     }
 273  
 
 274  
     /**
 275  
      * Get the value of defaultReadOnly, which defines the state of 
 276  
      * connections handed out from this pool.  The value can be changed
 277  
      * on the Connection using Connection.setReadOnly(boolean).
 278  
      * The default is false.
 279  
      *
 280  
      * @return value of defaultReadOnly.
 281  
      */
 282  
     public boolean isDefaultReadOnly() {
 283  7653
         return defaultReadOnly;
 284  
     }
 285  
     
 286  
     /**
 287  
      * Set the value of defaultReadOnly, which defines the state of 
 288  
      * connections handed out from this pool.  The value can be changed
 289  
      * on the Connection using Connection.setReadOnly(boolean).
 290  
      * The default is false.
 291  
      *
 292  
      * @param v  Value to assign to defaultReadOnly.
 293  
      */
 294  
     public void setDefaultReadOnly(boolean v) {
 295  0
         assertInitializationAllowed();
 296  0
         this.defaultReadOnly = v;
 297  0
     }
 298  
 
 299  
     /**
 300  
      * Get the value of defaultTransactionIsolation, which defines the state of
 301  
      * connections handed out from this pool.  The value can be changed
 302  
      * on the Connection using Connection.setTransactionIsolation(int).
 303  
      * If this method returns -1, the default is JDBC driver dependent.
 304  
      * 
 305  
      * @return value of defaultTransactionIsolation.
 306  
      */
 307  
     public int getDefaultTransactionIsolation() {
 308  7653
             return defaultTransactionIsolation;
 309  
     }
 310  
 
 311  
     /**
 312  
      * Set the value of defaultTransactionIsolation, which defines the state of
 313  
      * connections handed out from this pool.  The value can be changed
 314  
      * on the Connection using Connection.setTransactionIsolation(int).
 315  
      * The default is JDBC driver dependent.
 316  
      * 
 317  
      * @param v  Value to assign to defaultTransactionIsolation
 318  
      */
 319  
     public void setDefaultTransactionIsolation(int v) {
 320  148
         assertInitializationAllowed();
 321  148
         switch (v) {
 322  
         case Connection.TRANSACTION_NONE:
 323  
         case Connection.TRANSACTION_READ_COMMITTED:
 324  
         case Connection.TRANSACTION_READ_UNCOMMITTED:
 325  
         case Connection.TRANSACTION_REPEATABLE_READ:
 326  
         case Connection.TRANSACTION_SERIALIZABLE:
 327  148
             break;
 328  
         default:
 329  0
             throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
 330  
         }
 331  148
         this.defaultTransactionIsolation = v;
 332  148
     }
 333  
     
 334  
     /**
 335  
      * Get the description.  This property is defined by jdbc as for use with
 336  
      * GUI (or other) tools that might deploy the datasource.  It serves no
 337  
      * internal purpose.
 338  
      *
 339  
      * @return value of description.
 340  
      */
 341  
     public String getDescription() {
 342  0
         return description;
 343  
     }
 344  
     
 345  
     /**
 346  
      * Set the description.  This property is defined by jdbc as for use with
 347  
      * GUI (or other) tools that might deploy the datasource.  It serves no
 348  
      * internal purpose.
 349  
      * 
 350  
      * @param v  Value to assign to description.
 351  
      */
 352  
     public void setDescription(String v) {
 353  0
         this.description = v;
 354  0
     }
 355  
         
 356  
     /**
 357  
      * Get the value of jndiEnvironment which is used when instantiating
 358  
      * a jndi InitialContext.  This InitialContext is used to locate the
 359  
      * backend ConnectionPoolDataSource.
 360  
      *
 361  
      * @return value of jndiEnvironment.
 362  
      */
 363  
     public String getJndiEnvironment(String key) {
 364  0
         String value = null;
 365  0
         if (jndiEnvironment != null) {
 366  0
             value = jndiEnvironment.getProperty(key);
 367  
         }
 368  0
         return value;
 369  
     }
 370  
     
 371  
     /**
 372  
      * Sets the value of the given JNDI environment property to be used when
 373  
      * instantiating a JNDI InitialContext. This InitialContext is used to
 374  
      * locate the backend ConnectionPoolDataSource.
 375  
      * 
 376  
      * @param key the JNDI environment property to set.
 377  
      * @param value the value assigned to specified JNDI environment property.
 378  
      */
 379  
     public void setJndiEnvironment(String key, String value) {
 380  0
         if (jndiEnvironment == null) {
 381  0
             jndiEnvironment = new Properties();
 382  
         }
 383  0
         jndiEnvironment.setProperty(key, value);
 384  0
     }
 385  
     
 386  
     /**
 387  
      * Get the value of loginTimeout.
 388  
      * @return value of loginTimeout.
 389  
      */
 390  
     public int getLoginTimeout() {
 391  0
         return loginTimeout;
 392  
     }
 393  
     
 394  
     /**
 395  
      * Set the value of loginTimeout.
 396  
      * @param v  Value to assign to loginTimeout.
 397  
      */
 398  
     public void setLoginTimeout(int v) {
 399  0
         this.loginTimeout = v;
 400  0
     }
 401  
         
 402  
     /**
 403  
      * Get the value of logWriter.
 404  
      * @return value of logWriter.
 405  
      */
 406  
     public PrintWriter getLogWriter() {
 407  0
         if (logWriter == null) {
 408  0
             logWriter = new PrintWriter(System.out);
 409  
         }        
 410  0
         return logWriter;
 411  
     }
 412  
     
 413  
     /**
 414  
      * Set the value of logWriter.
 415  
      * @param v  Value to assign to logWriter.
 416  
      */
 417  
     public void setLogWriter(PrintWriter v) {
 418  0
         this.logWriter = v;
 419  0
     }
 420  
     
 421  
     /**
 422  
      * @see #getTestOnBorrow
 423  
      */
 424  
     public final boolean isTestOnBorrow() {
 425  0
         return getTestOnBorrow();
 426  
     }
 427  
     
 428  
     /**
 429  
      * When <tt>true</tt>, objects will be
 430  
      * {*link PoolableObjectFactory#validateObject validated}
 431  
      * before being returned by the {*link #borrowObject}
 432  
      * method.  If the object fails to validate,
 433  
      * it will be dropped from the pool, and we will attempt
 434  
      * to borrow another.
 435  
      *
 436  
      * @see #setTestOnBorrow
 437  
      */
 438  
     public boolean getTestOnBorrow() {
 439  164
         return _testOnBorrow;
 440  
     }
 441  
 
 442  
     /**
 443  
      * When <tt>true</tt>, objects will be
 444  
      * {*link PoolableObjectFactory#validateObject validated}
 445  
      * before being returned by the {*link #borrowObject}
 446  
      * method.  If the object fails to validate,
 447  
      * it will be dropped from the pool, and we will attempt
 448  
      * to borrow another. For a <code>true</code> value to have any effect,
 449  
      * the <code>validationQuery</code> property must be set to a non-null
 450  
      * string.
 451  
      *
 452  
      * @see #getTestOnBorrow
 453  
      */
 454  
     public void setTestOnBorrow(boolean testOnBorrow) {
 455  0
         assertInitializationAllowed();
 456  0
         _testOnBorrow = testOnBorrow;
 457  0
         testPositionSet = true;
 458  0
     }
 459  
 
 460  
     /**
 461  
      * @see #getTestOnReturn
 462  
      */
 463  
     public final boolean isTestOnReturn() {
 464  0
         return getTestOnReturn();
 465  
     }
 466  
     
 467  
     /**
 468  
      * When <tt>true</tt>, objects will be
 469  
      * {*link PoolableObjectFactory#validateObject validated}
 470  
      * before being returned to the pool within the
 471  
      * {*link #returnObject}.
 472  
      *
 473  
      * @see #setTestOnReturn
 474  
      */
 475  
     public boolean getTestOnReturn() {
 476  164
         return _testOnReturn;
 477  
     }
 478  
 
 479  
     /**
 480  
      * When <tt>true</tt>, objects will be
 481  
      * {*link PoolableObjectFactory#validateObject validated}
 482  
      * before being returned to the pool within the
 483  
      * {*link #returnObject}. For a <code>true</code> value to have any effect,
 484  
      * the <code>validationQuery</code> property must be set to a non-null
 485  
      * string.
 486  
      *
 487  
      * @see #getTestOnReturn
 488  
      */
 489  
     public void setTestOnReturn(boolean testOnReturn) {
 490  0
         assertInitializationAllowed();
 491  0
         _testOnReturn = testOnReturn;
 492  0
         testPositionSet = true;
 493  0
     }
 494  
 
 495  
     /**
 496  
      * Returns the number of milliseconds to sleep between runs of the
 497  
      * idle object evictor thread.
 498  
      * When non-positive, no idle object evictor thread will be
 499  
      * run.
 500  
      *
 501  
      * @see #setTimeBetweenEvictionRunsMillis
 502  
      */
 503  
     public int getTimeBetweenEvictionRunsMillis() {
 504  164
         return _timeBetweenEvictionRunsMillis;
 505  
     }
 506  
 
 507  
     /**
 508  
      * Sets the number of milliseconds to sleep between runs of the
 509  
      * idle object evictor thread.
 510  
      * When non-positive, no idle object evictor thread will be
 511  
      * run.
 512  
      *
 513  
      * @see #getTimeBetweenEvictionRunsMillis
 514  
      */
 515  
     public void 
 516  
         setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
 517  0
         assertInitializationAllowed();
 518  0
             _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
 519  0
     }
 520  
 
 521  
     /**
 522  
      * Returns the number of objects to examine during each run of the
 523  
      * idle object evictor thread (if any).
 524  
      *
 525  
      * @see #setNumTestsPerEvictionRun
 526  
      * @see #setTimeBetweenEvictionRunsMillis
 527  
      */
 528  
     public int getNumTestsPerEvictionRun() {
 529  164
         return _numTestsPerEvictionRun;
 530  
     }
 531  
 
 532  
     /**
 533  
      * Sets the number of objects to examine during each run of the
 534  
      * idle object evictor thread (if any).
 535  
      * <p>
 536  
      * When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt>
 537  
      * tests will be run.  I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
 538  
      * idle objects will be tested per run.
 539  
      *
 540  
      * @see #getNumTestsPerEvictionRun
 541  
      * @see #setTimeBetweenEvictionRunsMillis
 542  
      */
 543  
     public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
 544  0
         assertInitializationAllowed();
 545  0
         _numTestsPerEvictionRun = numTestsPerEvictionRun;
 546  0
     }
 547  
 
 548  
     /**
 549  
      * Returns the minimum amount of time an object may sit idle in the pool
 550  
      * before it is eligable for eviction by the idle object evictor
 551  
      * (if any).
 552  
      *
 553  
      * @see #setMinEvictableIdleTimeMillis
 554  
      * @see #setTimeBetweenEvictionRunsMillis
 555  
      */
 556  
     public int getMinEvictableIdleTimeMillis() {
 557  164
         return _minEvictableIdleTimeMillis;
 558  
     }
 559  
 
 560  
     /**
 561  
      * Sets the minimum amount of time an object may sit idle in the pool
 562  
      * before it is eligable for eviction by the idle object evictor
 563  
      * (if any).
 564  
      * When non-positive, no objects will be evicted from the pool
 565  
      * due to idle time alone.
 566  
      *
 567  
      * @see #getMinEvictableIdleTimeMillis
 568  
      * @see #setTimeBetweenEvictionRunsMillis
 569  
      */
 570  
     public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
 571  0
         assertInitializationAllowed();
 572  0
         _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
 573  0
     }
 574  
 
 575  
     /**
 576  
      * @see #getTestWhileIdle
 577  
      */
 578  
     public final boolean isTestWhileIdle() {
 579  0
         return getTestWhileIdle();
 580  
     }
 581  
     
 582  
     /**
 583  
      * When <tt>true</tt>, objects will be
 584  
      * {*link PoolableObjectFactory#validateObject validated}
 585  
      * by the idle object evictor (if any).  If an object
 586  
      * fails to validate, it will be dropped from the pool.
 587  
      *
 588  
      * @see #setTestWhileIdle
 589  
      * @see #setTimeBetweenEvictionRunsMillis
 590  
      */
 591  
     public boolean getTestWhileIdle() {
 592  164
         return _testWhileIdle;
 593  
     }
 594  
 
 595  
     /**
 596  
      * When <tt>true</tt>, objects will be
 597  
      * {*link PoolableObjectFactory#validateObject validated}
 598  
      * by the idle object evictor (if any).  If an object
 599  
      * fails to validate, it will be dropped from the pool. For a
 600  
      * <code>true</code> value to have any effect,
 601  
      * the <code>validationQuery</code> property must be set to a non-null
 602  
      * string.
 603  
      *
 604  
      * @see #getTestWhileIdle
 605  
      * @see #setTimeBetweenEvictionRunsMillis
 606  
      */
 607  
     public void setTestWhileIdle(boolean testWhileIdle) {
 608  0
         assertInitializationAllowed();
 609  0
         _testWhileIdle = testWhileIdle;
 610  0
         testPositionSet = true;
 611  0
     }
 612  
 
 613  
     /**
 614  
      * The SQL query that will be used to validate connections from this pool
 615  
      * before returning them to the caller.  If specified, this query
 616  
      * <strong>MUST</strong> be an SQL SELECT statement that returns at least
 617  
      * one row.
 618  
      */
 619  
     public String getValidationQuery() {
 620  164
         return (this.validationQuery);
 621  
     }
 622  
 
 623  
     /**
 624  
      * The SQL query that will be used to validate connections from this pool
 625  
      * before returning them to the caller.  If specified, this query
 626  
      * <strong>MUST</strong> be an SQL SELECT statement that returns at least
 627  
      * one row. If none of the properties, testOnBorow, testOnReturn, testWhileIdle
 628  
      * have been previously set, calling this method sets testOnBorrow to true.
 629  
      */
 630  
     public void setValidationQuery(String validationQuery) {
 631  0
         assertInitializationAllowed();
 632  0
         this.validationQuery = validationQuery;
 633  0
         if (!testPositionSet) {
 634  0
             setTestOnBorrow(true);
 635  
         }
 636  0
     }
 637  
 
 638  
     /**
 639  
      * Whether a rollback will be issued after executing the SQL query 
 640  
      * that will be used to validate connections from this pool
 641  
      * before returning them to the caller.
 642  
      * 
 643  
      * @return true if a rollback will be issued after executing the
 644  
      * validation query
 645  
      * @since 1.2.2
 646  
      */
 647  
     public boolean isRollbackAfterValidation() {
 648  164
         return (this.rollbackAfterValidation);
 649  
     }
 650  
 
 651  
     /**
 652  
      * Whether a rollback will be issued after executing the SQL query 
 653  
      * that will be used to validate connections from this pool
 654  
      * before returning them to the caller. Default behavior is NOT
 655  
      * to issue a rollback. The setting will only have an effect
 656  
      * if a validation query is set
 657  
      * 
 658  
      * @param rollbackAfterValidation new property value
 659  
      * @since 1.2.2
 660  
      */
 661  
     public void setRollbackAfterValidation(boolean rollbackAfterValidation) {
 662  0
         assertInitializationAllowed();
 663  0
         this.rollbackAfterValidation = rollbackAfterValidation;
 664  0
     }
 665  
 
 666  
     // ----------------------------------------------------------------------
 667  
     // Instrumentation Methods
 668  
 
 669  
     // ----------------------------------------------------------------------
 670  
     // DataSource implementation 
 671  
 
 672  
     /**
 673  
      * Attempt to establish a database connection.
 674  
      */
 675  
     public Connection getConnection() throws SQLException {
 676  172
         return getConnection(null, null);
 677  
     }
 678  
 
 679  
     /**
 680  
      * Attempt to retrieve a database connection using {@link #getPooledConnectionAndInfo(String, String)}
 681  
      * with the provided username and password.  The password on the {@link PooledConnectionAndInfo}
 682  
      * instance returned by <code>getPooledConnectionAndInfo</code> is compared to the <code>password</code>
 683  
      * parameter.  If the comparison fails, a database connection using the supplied username and password
 684  
      * is attempted.  If the connection attempt fails, an SQLException is thrown, indicating that the given password
 685  
      * did not match the password used to create the pooled connection.  If the connection attempt succeeds, this
 686  
      * means that the database password has been changed.  In this case, the <code>PooledConnectionAndInfo</code>
 687  
      * instance retrieved with the old password is destroyed and the <code>getPooledConnectionAndInfo</code> is
 688  
      * repeatedly invoked until a <code>PooledConnectionAndInfo</code> instance with the new password is returned. 
 689  
      * 
 690  
      */
 691  
     public Connection getConnection(String username, String password)
 692  
             throws SQLException {        
 693  7765
         if (instanceKey == null) {
 694  0
             throw new SQLException("Must set the ConnectionPoolDataSource " 
 695  
                     + "through setDataSourceName or setConnectionPoolDataSource"
 696  
                     + " before calling getConnection.");
 697  
         }
 698  7765
         getConnectionCalled = true;
 699  7765
         PooledConnectionAndInfo info = null;
 700  
         try {
 701  7765
             info = getPooledConnectionAndInfo(username, password);
 702  0
         } catch (NoSuchElementException e) {
 703  0
             closeDueToException(info);
 704  0
             throw new SQLNestedException("Cannot borrow connection from pool", e);
 705  0
         } catch (RuntimeException e) {
 706  0
             closeDueToException(info);
 707  0
             throw e;
 708  98
         } catch (SQLException e) {            
 709  98
             closeDueToException(info);
 710  98
             throw e;
 711  0
         } catch (Exception e) {
 712  0
             closeDueToException(info);
 713  0
             throw new SQLNestedException("Cannot borrow connection from pool", e);
 714  7667
         }
 715  
         
 716  7667
         if (!(null == password ? null == info.getPassword() 
 717  
                 : password.equals(info.getPassword()))) {  // Password on PooledConnectionAndInfo does not match
 718  
             try { // See if password has changed by attempting connection
 719  18
                 testCPDS(username, password);
 720  12
             } catch (SQLException ex) {
 721  
                 // Password has not changed, so refuse client, but return connection to the pool
 722  12
                 closeDueToException(info);
 723  12
                 throw new SQLException("Given password did not match password used"
 724  
                                        + " to create the PooledConnection.");
 725  0
             } catch (javax.naming.NamingException ne) {
 726  0
                 throw (SQLException) new SQLException(
 727  
                         "NamingException encountered connecting to database").initCause(ne);
 728  6
             }
 729  
             /*
 730  
              * Password must have changed -> destroy connection and keep retrying until we get a new, good one,
 731  
              * destroying any idle connections with the old passowrd as we pull them from the pool.
 732  
              */
 733  6
             final UserPassKey upkey = info.getUserPassKey();
 734  6
             final PooledConnectionManager manager = getConnectionManager(upkey);
 735  6
             manager.invalidate(info.getPooledConnection()); // Destroy and remove from pool
 736  6
             manager.setPassword(upkey.getPassword()); // Reset the password on the factory if using CPDSConnectionFactory
 737  6
             info = null;
 738  6
             for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return 
 739  
                 try {
 740  6
                     info = getPooledConnectionAndInfo(username, password);
 741  0
                 } catch (NoSuchElementException e) {
 742  0
                     closeDueToException(info);
 743  0
                     throw new SQLNestedException("Cannot borrow connection from pool", e);
 744  0
                 } catch (RuntimeException e) {
 745  0
                     closeDueToException(info);
 746  0
                     throw e;
 747  0
                 } catch (SQLException e) {            
 748  0
                     closeDueToException(info);
 749  0
                     throw e;
 750  0
                 } catch (Exception e) {
 751  0
                     closeDueToException(info);
 752  0
                     throw new SQLNestedException("Cannot borrow connection from pool", e);
 753  6
                 }
 754  6
                 if (info != null && password.equals(info.getPassword())) {
 755  6
                     break;
 756  
                 } else {
 757  0
                     if (info != null) {
 758  0
                         manager.invalidate(info.getPooledConnection());
 759  
                     }
 760  0
                     info = null;
 761  
                 }
 762  
             }  
 763  6
             if (info == null) {
 764  0
                 throw new SQLException("Cannot borrow connection from pool - password change failure.");
 765  
             }
 766  
         }
 767  
 
 768  7655
         Connection con = info.getPooledConnection().getConnection();
 769  
         try { 
 770  7655
             setupDefaults(con, username);
 771  7653
             con.clearWarnings();
 772  7653
             return con;
 773  2
         } catch (SQLException ex) {  
 774  
             try {
 775  2
                 con.close();
 776  0
             } catch (Exception exc) { 
 777  0
                 getLogWriter().println(
 778  
                      "ignoring exception during close: " + exc);
 779  2
             }
 780  2
             throw ex;
 781  
         }
 782  
     }
 783  
 
 784  
     protected abstract PooledConnectionAndInfo 
 785  
         getPooledConnectionAndInfo(String username, String password)
 786  
         throws SQLException;
 787  
 
 788  
     protected abstract void setupDefaults(Connection con, String username) 
 789  
         throws SQLException;
 790  
 
 791  
         
 792  
     private void closeDueToException(PooledConnectionAndInfo info) {
 793  110
         if (info != null) {
 794  
             try {
 795  12
                 info.getPooledConnection().getConnection().close();
 796  0
             } catch (Exception e) {
 797  
                 // do not throw this exception because we are in the middle
 798  
                 // of handling another exception.  But record it because
 799  
                 // it potentially leaks connections from the pool.
 800  0
                 getLogWriter().println("[ERROR] Could not return connection to "
 801  
                     + "pool during exception handling. " + e.getMessage());   
 802  12
             }
 803  
         }
 804  110
     }
 805  
 
 806  
     protected ConnectionPoolDataSource 
 807  
         testCPDS(String username, String password)
 808  
         throws javax.naming.NamingException, SQLException {
 809  
         // The source of physical db connections
 810  192
         ConnectionPoolDataSource cpds = this.dataSource;
 811  192
         if (cpds == null) {            
 812  0
             Context ctx = null;
 813  0
             if (jndiEnvironment == null) {
 814  0
                 ctx = new InitialContext();                
 815  
             } else {
 816  0
                 ctx = new InitialContext(jndiEnvironment);
 817  
             }
 818  0
             Object ds = ctx.lookup(dataSourceName);
 819  0
             if (ds instanceof ConnectionPoolDataSource) {
 820  0
                 cpds = (ConnectionPoolDataSource) ds;
 821  
             } else {
 822  0
                 throw new SQLException("Illegal configuration: "
 823  
                     + "DataSource " + dataSourceName
 824  
                     + " (" + ds.getClass().getName() + ")"
 825  
                     + " doesn't implement javax.sql.ConnectionPoolDataSource");
 826  
             }
 827  
         }
 828  
         
 829  
         // try to get a connection with the supplied username/password
 830  192
         PooledConnection conn = null;
 831  
         try {
 832  192
             if (username != null) {
 833  150
                 conn = cpds.getPooledConnection(username, password);
 834  
             }
 835  
             else {
 836  42
                 conn = cpds.getPooledConnection();
 837  
             }
 838  172
             if (conn == null) {
 839  0
                 throw new SQLException(
 840  
                     "Cannot connect using the supplied username/password");
 841  
             }
 842  
         }
 843  
         finally {
 844  192
             if (conn != null) {
 845  
                 try {
 846  172
                     conn.close();
 847  
                 }
 848  0
                 catch (SQLException e) {
 849  
                     // at least we could connect
 850  192
                 }
 851  
             }
 852  
         }
 853  172
         return cpds;
 854  
     }
 855  
 
 856  
     protected byte whenExhaustedAction(int maxActive, int maxWait) {
 857  164
         byte whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
 858  164
         if (maxActive <= 0) {
 859  0
             whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_GROW;
 860  164
         } else if (maxWait == 0) {
 861  2
             whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
 862  
         }
 863  164
         return whenExhausted;
 864  
     }    
 865  
 
 866  
     // ----------------------------------------------------------------------
 867  
     // Referenceable implementation 
 868  
 
 869  
     /**
 870  
      * Retrieves the Reference of this object.
 871  
      * <strong>Note:</strong> <code>InstanceKeyDataSource</code> subclasses
 872  
      * should override this method. The implementaion included below
 873  
      * is not robust and will be removed at the next major version DBCP
 874  
      * release.
 875  
      *
 876  
      * @return The non-null Reference of this object.
 877  
      * @exception NamingException If a naming exception was encountered
 878  
      *      while retrieving the reference.
 879  
      */
 880  
     // TODO: Remove the implementation of this method at next major
 881  
     // version release.
 882  
     
 883  
     public Reference getReference() throws NamingException {
 884  0
         final String className = getClass().getName();
 885  0
         final String factoryName = className + "Factory"; // XXX: not robust 
 886  0
         Reference ref = new Reference(className, factoryName, null);
 887  0
         ref.add(new StringRefAddr("instanceKey", instanceKey));
 888  0
         return ref;
 889  
     }
 890  
 }