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 }