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.datasources;
019    
020    import java.io.Serializable;
021    import java.io.PrintWriter;
022    import java.sql.Connection;
023    import java.sql.SQLException;
024    import java.util.NoSuchElementException;
025    import java.util.Properties;
026    
027    import javax.naming.Context;
028    import javax.naming.InitialContext;
029    import javax.naming.NamingException;
030    import javax.naming.Reference;
031    import javax.naming.StringRefAddr;
032    import javax.naming.Referenceable;
033    import javax.sql.ConnectionPoolDataSource;
034    import javax.sql.DataSource;
035    import javax.sql.PooledConnection;
036    
037    import org.apache.commons.dbcp.SQLNestedException;
038    import org.apache.commons.pool.impl.GenericObjectPool;
039    
040    /**
041     * <p>The base class for <code>SharedPoolDataSource</code> and 
042     * <code>PerUserPoolDataSource</code>.  Many of the configuration properties
043     * are shared and defined here.  This class is declared public in order
044     * to allow particular usage with commons-beanutils; do not make direct
045     * use of it outside of commons-dbcp.
046     * </p>
047     *
048     * <p>
049     * A J2EE container will normally provide some method of initializing the
050     * <code>DataSource</code> whose attributes are presented
051     * as bean getters/setters and then deploying it via JNDI.  It is then
052     * available to an application as a source of pooled logical connections to 
053     * the database.  The pool needs a source of physical connections.  This
054     * source is in the form of a <code>ConnectionPoolDataSource</code> that
055     * can be specified via the {@link #setDataSourceName(String)} used to
056     * lookup the source via JNDI.
057     * </p>
058     *
059     * <p>
060     * Although normally used within a JNDI environment, A DataSource
061     * can be instantiated and initialized as any bean.  In this case the 
062     * <code>ConnectionPoolDataSource</code> will likely be instantiated in
063     * a similar manner.  This class allows the physical source of connections
064     * to be attached directly to this pool using the 
065     * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
066     * </p>
067     *
068     * <p>
069     * The dbcp package contains an adapter, 
070     * {@link org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS},
071     * that can be used to allow the use of <code>DataSource</code>'s based on this
072     * class with jdbc driver implementations that do not supply a 
073     * <code>ConnectionPoolDataSource</code>, but still
074     * provide a {@link java.sql.Driver} implementation.
075     * </p>
076     *
077     * <p>
078     * The <a href="package-summary.html">package documentation</a> contains an 
079     * example using Apache Tomcat and JNDI and it also contains a non-JNDI example. 
080     * </p>
081     *
082     * @author John D. McNally
083     * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $
084     */
085    public abstract class InstanceKeyDataSource
086            implements DataSource, Referenceable, Serializable {
087        private static final long serialVersionUID = -4243533936955098795L;
088        private static final String GET_CONNECTION_CALLED 
089                = "A Connection was already requested from this source, " 
090                + "further initialization is not allowed.";
091        private static final String BAD_TRANSACTION_ISOLATION
092            = "The requested TransactionIsolation level is invalid.";
093        /**
094        * Internal constant to indicate the level is not set. 
095        */
096        protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
097        
098        /** Guards property setters - once true, setters throw IllegalStateException */
099        private volatile boolean getConnectionCalled = false;
100    
101        /** Underlying source of PooledConnections */
102        private ConnectionPoolDataSource dataSource = null;
103        
104        /** DataSource Name used to find the ConnectionPoolDataSource */
105        private String dataSourceName = null;
106        
107        // Default connection properties
108        private boolean defaultAutoCommit = false;
109        private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
110        private boolean defaultReadOnly = false;
111        
112        /** Description */
113        private String description = null;
114        
115        /** Environment that may be used to set up a jndi initial context. */
116        Properties jndiEnvironment = null;
117        
118        /** Login TimeOut in seconds */
119        private int loginTimeout = 0;
120        
121        /** Log stream */
122        private PrintWriter logWriter = null;
123        
124        // Pool properties
125        private boolean _testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
126        private boolean _testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
127        private int _timeBetweenEvictionRunsMillis = (int)
128            Math.min(Integer.MAX_VALUE,
129                     GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
130        private int _numTestsPerEvictionRun = 
131            GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
132        private int _minEvictableIdleTimeMillis = (int)
133        Math.min(Integer.MAX_VALUE,
134                 GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
135        private boolean _testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
136        private String validationQuery = null;
137        private boolean rollbackAfterValidation = false;
138        
139        /** true iff one of the setters for testOnBorrow, testOnReturn, testWhileIdle has been called. */
140        private boolean testPositionSet = false;
141    
142        /** Instance key */
143        protected String instanceKey = null;
144    
145        /**
146         * Default no-arg constructor for Serialization
147         */
148        public InstanceKeyDataSource() {
149            defaultAutoCommit = true;
150        }
151    
152        /**
153         * Throws an IllegalStateException, if a PooledConnection has already
154         * been requested.
155         */
156        protected void assertInitializationAllowed()
157            throws IllegalStateException {
158            if (getConnectionCalled) {
159                throw new IllegalStateException(GET_CONNECTION_CALLED);
160            }
161        }
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        /*
171        public boolean isWrapperFor(Class<?> iface) throws SQLException {
172            return false;
173        }
174    
175        public <T> T unwrap(Class<T> iface) throws SQLException {
176            throw new SQLException("InstanceKeyDataSource is not a wrapper.");
177        }
178        */
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            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            assertInitializationAllowed();
201            if (dataSourceName != null) {
202                throw new IllegalStateException(
203                    "Cannot set the DataSource, if JNDI is used.");
204            }
205            if (dataSource != null) 
206            {
207                throw new IllegalStateException(
208                    "The CPDS has already been set. It cannot be altered.");
209            }
210            dataSource = v;
211            instanceKey = InstanceKeyObjectFactory.registerNewInstance(this);
212        }
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            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            assertInitializationAllowed();
234            if (dataSource != null) {
235                throw new IllegalStateException(
236                    "Cannot set the JNDI name for the DataSource, if already " +
237                    "set using setConnectionPoolDataSource.");
238            }
239            if (dataSourceName != null) 
240            {
241                throw new IllegalStateException(
242                    "The DataSourceName has already been set. " + 
243                    "It cannot be altered.");
244            }
245            this.dataSourceName = v;
246            instanceKey = InstanceKeyObjectFactory.registerNewInstance(this);
247        }
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            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            assertInitializationAllowed();
271            this.defaultAutoCommit = v;
272        }
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            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            assertInitializationAllowed();
296            this.defaultReadOnly = v;
297        }
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                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            assertInitializationAllowed();
321            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                break;
328            default:
329                throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
330            }
331            this.defaultTransactionIsolation = v;
332        }
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            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            this.description = v;
354        }
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            String value = null;
365            if (jndiEnvironment != null) {
366                value = jndiEnvironment.getProperty(key);
367            }
368            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            if (jndiEnvironment == null) {
381                jndiEnvironment = new Properties();
382            }
383            jndiEnvironment.setProperty(key, value);
384        }
385        
386        /**
387         * Get the value of loginTimeout.
388         * @return value of loginTimeout.
389         */
390        public int getLoginTimeout() {
391            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            this.loginTimeout = v;
400        }
401            
402        /**
403         * Get the value of logWriter.
404         * @return value of logWriter.
405         */
406        public PrintWriter getLogWriter() {
407            if (logWriter == null) {
408                logWriter = new PrintWriter(System.out);
409            }        
410            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            this.logWriter = v;
419        }
420        
421        /**
422         * @see #getTestOnBorrow
423         */
424        public final boolean isTestOnBorrow() {
425            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            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            assertInitializationAllowed();
456            _testOnBorrow = testOnBorrow;
457            testPositionSet = true;
458        }
459    
460        /**
461         * @see #getTestOnReturn
462         */
463        public final boolean isTestOnReturn() {
464            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            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            assertInitializationAllowed();
491            _testOnReturn = testOnReturn;
492            testPositionSet = true;
493        }
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            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            assertInitializationAllowed();
518                _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
519        }
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            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            assertInitializationAllowed();
545            _numTestsPerEvictionRun = numTestsPerEvictionRun;
546        }
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            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            assertInitializationAllowed();
572            _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
573        }
574    
575        /**
576         * @see #getTestWhileIdle
577         */
578        public final boolean isTestWhileIdle() {
579            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            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            assertInitializationAllowed();
609            _testWhileIdle = testWhileIdle;
610            testPositionSet = true;
611        }
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            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            assertInitializationAllowed();
632            this.validationQuery = validationQuery;
633            if (!testPositionSet) {
634                setTestOnBorrow(true);
635            }
636        }
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            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            assertInitializationAllowed();
663            this.rollbackAfterValidation = rollbackAfterValidation;
664        }
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            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            if (instanceKey == null) {
694                throw new SQLException("Must set the ConnectionPoolDataSource " 
695                        + "through setDataSourceName or setConnectionPoolDataSource"
696                        + " before calling getConnection.");
697            }
698            getConnectionCalled = true;
699            PooledConnectionAndInfo info = null;
700            try {
701                info = getPooledConnectionAndInfo(username, password);
702            } catch (NoSuchElementException e) {
703                closeDueToException(info);
704                throw new SQLNestedException("Cannot borrow connection from pool", e);
705            } catch (RuntimeException e) {
706                closeDueToException(info);
707                throw e;
708            } catch (SQLException e) {            
709                closeDueToException(info);
710                throw e;
711            } catch (Exception e) {
712                closeDueToException(info);
713                throw new SQLNestedException("Cannot borrow connection from pool", e);
714            }
715            
716            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                    testCPDS(username, password);
720                } catch (SQLException ex) {
721                    // Password has not changed, so refuse client, but return connection to the pool
722                    closeDueToException(info);
723                    throw new SQLException("Given password did not match password used"
724                                           + " to create the PooledConnection.");
725                } catch (javax.naming.NamingException ne) {
726                    throw (SQLException) new SQLException(
727                            "NamingException encountered connecting to database").initCause(ne);
728                }
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                final UserPassKey upkey = info.getUserPassKey();
734                final PooledConnectionManager manager = getConnectionManager(upkey);
735                manager.invalidate(info.getPooledConnection()); // Destroy and remove from pool
736                manager.setPassword(upkey.getPassword()); // Reset the password on the factory if using CPDSConnectionFactory
737                info = null;
738                for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return 
739                    try {
740                        info = getPooledConnectionAndInfo(username, password);
741                    } catch (NoSuchElementException e) {
742                        closeDueToException(info);
743                        throw new SQLNestedException("Cannot borrow connection from pool", e);
744                    } catch (RuntimeException e) {
745                        closeDueToException(info);
746                        throw e;
747                    } catch (SQLException e) {            
748                        closeDueToException(info);
749                        throw e;
750                    } catch (Exception e) {
751                        closeDueToException(info);
752                        throw new SQLNestedException("Cannot borrow connection from pool", e);
753                    }
754                    if (info != null && password.equals(info.getPassword())) {
755                        break;
756                    } else {
757                        if (info != null) {
758                            manager.invalidate(info.getPooledConnection());
759                        }
760                        info = null;
761                    }
762                }  
763                if (info == null) {
764                    throw new SQLException("Cannot borrow connection from pool - password change failure.");
765                }
766            }
767    
768            Connection con = info.getPooledConnection().getConnection();
769            try { 
770                setupDefaults(con, username);
771                con.clearWarnings();
772                return con;
773            } catch (SQLException ex) {  
774                try {
775                    con.close();
776                } catch (Exception exc) { 
777                    getLogWriter().println(
778                         "ignoring exception during close: " + exc);
779                }
780                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            if (info != null) {
794                try {
795                    info.getPooledConnection().getConnection().close();
796                } 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                    getLogWriter().println("[ERROR] Could not return connection to "
801                        + "pool during exception handling. " + e.getMessage());   
802                }
803            }
804        }
805    
806        protected ConnectionPoolDataSource 
807            testCPDS(String username, String password)
808            throws javax.naming.NamingException, SQLException {
809            // The source of physical db connections
810            ConnectionPoolDataSource cpds = this.dataSource;
811            if (cpds == null) {            
812                Context ctx = null;
813                if (jndiEnvironment == null) {
814                    ctx = new InitialContext();                
815                } else {
816                    ctx = new InitialContext(jndiEnvironment);
817                }
818                Object ds = ctx.lookup(dataSourceName);
819                if (ds instanceof ConnectionPoolDataSource) {
820                    cpds = (ConnectionPoolDataSource) ds;
821                } else {
822                    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            PooledConnection conn = null;
831            try {
832                if (username != null) {
833                    conn = cpds.getPooledConnection(username, password);
834                }
835                else {
836                    conn = cpds.getPooledConnection();
837                }
838                if (conn == null) {
839                    throw new SQLException(
840                        "Cannot connect using the supplied username/password");
841                }
842            }
843            finally {
844                if (conn != null) {
845                    try {
846                        conn.close();
847                    }
848                    catch (SQLException e) {
849                        // at least we could connect
850                    }
851                }
852            }
853            return cpds;
854        }
855    
856        protected byte whenExhaustedAction(int maxActive, int maxWait) {
857            byte whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
858            if (maxActive <= 0) {
859                whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_GROW;
860            } else if (maxWait == 0) {
861                whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
862            }
863            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            final String className = getClass().getName();
885            final String factoryName = className + "Factory"; // XXX: not robust 
886            Reference ref = new Reference(className, factoryName, null);
887            ref.add(new StringRefAddr("instanceKey", instanceKey));
888            return ref;
889        }
890    }