1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.dbcp.datasources;
  17. import java.io.Serializable;
  18. import java.io.PrintWriter;
  19. import java.sql.Connection;
  20. import java.sql.SQLException;
  21. import java.util.NoSuchElementException;
  22. import java.util.Properties;
  23. import javax.naming.Context;
  24. import javax.naming.InitialContext;
  25. import javax.naming.NamingException;
  26. import javax.naming.Reference;
  27. import javax.naming.StringRefAddr;
  28. import javax.naming.Referenceable;
  29. import javax.sql.ConnectionPoolDataSource;
  30. import javax.sql.DataSource;
  31. import javax.sql.PooledConnection;
  32. import org.apache.commons.dbcp.SQLNestedException;
  33. import org.apache.commons.pool.impl.GenericObjectPool;
  34. /**
  35. * <p>The base class for <code>SharedPoolDataSource</code> and
  36. * <code>PerUserPoolDataSource</code>. Many of the configuration properties
  37. * are shared and defined here. This class is declared public in order
  38. * to allow particular usage with commons-beanutils; do not make direct
  39. * use of it outside of commons-dbcp.
  40. * </p>
  41. *
  42. * <p>
  43. * A J2EE container will normally provide some method of initializing the
  44. * <code>DataSource</code> whose attributes are presented
  45. * as bean getters/setters and then deploying it via JNDI. It is then
  46. * available to an application as a source of pooled logical connections to
  47. * the database. The pool needs a source of physical connections. This
  48. * source is in the form of a <code>ConnectionPoolDataSource</code> that
  49. * can be specified via the {@link #setDataSourceName(String)} used to
  50. * lookup the source via JNDI.
  51. * </p>
  52. *
  53. * <p>
  54. * Although normally used within a JNDI environment, A DataSource
  55. * can be instantiated and initialized as any bean. In this case the
  56. * <code>ConnectionPoolDataSource</code> will likely be instantiated in
  57. * a similar manner. This class allows the physical source of connections
  58. * to be attached directly to this pool using the
  59. * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
  60. * </p>
  61. *
  62. * <p>
  63. * The dbcp package contains an adapter,
  64. * {@link org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS},
  65. * that can be used to allow the use of <code>DataSource</code>'s based on this
  66. * class with jdbc driver implementations that do not supply a
  67. * <code>ConnectionPoolDataSource</code>, but still
  68. * provide a {@link java.sql.Driver} implementation.
  69. * </p>
  70. *
  71. * <p>
  72. * The <a href="package-summary.html">package documentation</a> contains an
  73. * example using catalina and JNDI and it also contains a non-JNDI example.
  74. * </p>
  75. *
  76. * @author John D. McNally
  77. * @version $Revision: 1.11 $ $Date: 2004/02/28 12:18:17 $
  78. */
  79. public abstract class InstanceKeyDataSource
  80. implements DataSource, Referenceable, Serializable {
  81. private static final String GET_CONNECTION_CALLED
  82. = "A Connection was already requested from this source, "
  83. + "further initialization is not allowed.";
  84. private static final String BAD_TRANSACTION_ISOLATION
  85. = "The requested TransactionIsolation level is invalid.";
  86. /**
  87. * Internal constant to indicate the level is not set.
  88. */
  89. protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
  90. private boolean getConnectionCalled = false;
  91. private ConnectionPoolDataSource cpds = null;
  92. /** DataSource Name used to find the ConnectionPoolDataSource */
  93. private String dataSourceName = null;
  94. private boolean defaultAutoCommit = false;
  95. private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
  96. private int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
  97. private int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
  98. private int maxWait = (int)Math.min((long)Integer.MAX_VALUE,
  99. GenericObjectPool.DEFAULT_MAX_WAIT);
  100. private boolean defaultReadOnly = false;
  101. /** Description */
  102. private String description = null;
  103. /** Environment that may be used to set up a jndi initial context. */
  104. Properties jndiEnvironment = null;
  105. /** Login TimeOut in seconds */
  106. private int loginTimeout = 0;
  107. /** Log stream */
  108. private PrintWriter logWriter = null;
  109. private boolean _testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
  110. private boolean _testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
  111. private int _timeBetweenEvictionRunsMillis = (int)
  112. Math.min((long)Integer.MAX_VALUE,
  113. GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
  114. private int _numTestsPerEvictionRun =
  115. GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
  116. private int _minEvictableIdleTimeMillis = (int)
  117. Math.min((long)Integer.MAX_VALUE,
  118. GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
  119. private boolean _testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
  120. private String validationQuery = null;
  121. private boolean testPositionSet = false;
  122. protected String instanceKey = null;
  123. /**
  124. * Default no-arg constructor for Serialization
  125. */
  126. public InstanceKeyDataSource() {
  127. defaultAutoCommit = true;
  128. }
  129. /**
  130. * Throws an IllegalStateException, if a PooledConnection has already
  131. * been requested.
  132. */
  133. protected void assertInitializationAllowed()
  134. throws IllegalStateException {
  135. if (getConnectionCalled) {
  136. throw new IllegalStateException(GET_CONNECTION_CALLED);
  137. }
  138. }
  139. /**
  140. * Close pool being maintained by this datasource.
  141. */
  142. public abstract void close() throws Exception;
  143. // -------------------------------------------------------------------
  144. // Properties
  145. /**
  146. * Get the value of connectionPoolDataSource. This method will return
  147. * null, if the backing datasource is being accessed via jndi.
  148. *
  149. * @return value of connectionPoolDataSource.
  150. */
  151. public ConnectionPoolDataSource getConnectionPoolDataSource() {
  152. return cpds;
  153. }
  154. /**
  155. * Set the backend ConnectionPoolDataSource. This property should not be
  156. * set if using jndi to access the datasource.
  157. *
  158. * @param v Value to assign to connectionPoolDataSource.
  159. */
  160. public void setConnectionPoolDataSource(ConnectionPoolDataSource v) {
  161. assertInitializationAllowed();
  162. if (dataSourceName != null) {
  163. throw new IllegalStateException(
  164. "Cannot set the DataSource, if JNDI is used.");
  165. }
  166. if (cpds != null)
  167. {
  168. throw new IllegalStateException(
  169. "The CPDS has already been set. It cannot be altered.");
  170. }
  171. cpds = v;
  172. instanceKey = InstanceKeyObjectFactory.registerNewInstance(this);
  173. }
  174. /**
  175. * Get the name of the ConnectionPoolDataSource which backs this pool.
  176. * This name is used to look up the datasource from a jndi service
  177. * provider.
  178. *
  179. * @return value of dataSourceName.
  180. */
  181. public String getDataSourceName() {
  182. return dataSourceName;
  183. }
  184. /**
  185. * Set the name of the ConnectionPoolDataSource which backs this pool.
  186. * This name is used to look up the datasource from a jndi service
  187. * provider.
  188. *
  189. * @param v Value to assign to dataSourceName.
  190. */
  191. public void setDataSourceName(String v) {
  192. assertInitializationAllowed();
  193. if (cpds != null) {
  194. throw new IllegalStateException(
  195. "Cannot set the JNDI name for the DataSource, if already " +
  196. "set using setConnectionPoolDataSource.");
  197. }
  198. if (dataSourceName != null)
  199. {
  200. throw new IllegalStateException(
  201. "The DataSourceName has already been set. " +
  202. "It cannot be altered.");
  203. }
  204. this.dataSourceName = v;
  205. instanceKey = InstanceKeyObjectFactory.registerNewInstance(this);
  206. }
  207. /**
  208. * Get the value of defaultAutoCommit, which defines the state of
  209. * connections handed out from this pool. The value can be changed
  210. * on the Connection using Connection.setAutoCommit(boolean).
  211. * The default is true.
  212. *
  213. * @return value of defaultAutoCommit.
  214. */
  215. public boolean isDefaultAutoCommit() {
  216. return defaultAutoCommit;
  217. }
  218. /**
  219. * Set the value of defaultAutoCommit, which defines the state of
  220. * connections handed out from this pool. The value can be changed
  221. * on the Connection using Connection.setAutoCommit(boolean).
  222. * The default is true.
  223. *
  224. * @param v Value to assign to defaultAutoCommit.
  225. */
  226. public void setDefaultAutoCommit(boolean v) {
  227. assertInitializationAllowed();
  228. this.defaultAutoCommit = v;
  229. }
  230. /**
  231. * Get the value of defaultReadOnly, which defines the state of
  232. * connections handed out from this pool. The value can be changed
  233. * on the Connection using Connection.setReadOnly(boolean).
  234. * The default is false.
  235. *
  236. * @return value of defaultReadOnly.
  237. */
  238. public boolean isDefaultReadOnly() {
  239. return defaultReadOnly;
  240. }
  241. /**
  242. * Set the value of defaultReadOnly, which defines the state of
  243. * connections handed out from this pool. The value can be changed
  244. * on the Connection using Connection.setReadOnly(boolean).
  245. * The default is false.
  246. *
  247. * @param v Value to assign to defaultReadOnly.
  248. */
  249. public void setDefaultReadOnly(boolean v) {
  250. assertInitializationAllowed();
  251. this.defaultReadOnly = v;
  252. }
  253. /**
  254. * Get the value of defaultTransactionIsolation, which defines the state of
  255. * connections handed out from this pool. The value can be changed
  256. * on the Connection using Connection.setTransactionIsolation(int).
  257. * If this method returns -1, the default is JDBC driver dependent.
  258. *
  259. * @return value of defaultTransactionIsolation.
  260. */
  261. public int getDefaultTransactionIsolation() {
  262. return defaultTransactionIsolation;
  263. }
  264. /**
  265. * Set the value of defaultTransactionIsolation, which defines the state of
  266. * connections handed out from this pool. The value can be changed
  267. * on the Connection using Connection.setTransactionIsolation(int).
  268. * The default is JDBC driver dependent.
  269. *
  270. * @param v Value to assign to defaultTransactionIsolation
  271. */
  272. public void setDefaultTransactionIsolation(int v) {
  273. assertInitializationAllowed();
  274. switch (v) {
  275. case Connection.TRANSACTION_NONE:
  276. case Connection.TRANSACTION_READ_COMMITTED:
  277. case Connection.TRANSACTION_READ_UNCOMMITTED:
  278. case Connection.TRANSACTION_REPEATABLE_READ:
  279. case Connection.TRANSACTION_SERIALIZABLE:
  280. break;
  281. default:
  282. throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION);
  283. }
  284. this.defaultTransactionIsolation = v;
  285. }
  286. /**
  287. * Get the description. This property is defined by jdbc as for use with
  288. * GUI (or other) tools that might deploy the datasource. It serves no
  289. * internal purpose.
  290. *
  291. * @return value of description.
  292. */
  293. public String getDescription() {
  294. return description;
  295. }
  296. /**
  297. * Set the description. This property is defined by jdbc as for use with
  298. * GUI (or other) tools that might deploy the datasource. It serves no
  299. * internal purpose.
  300. *
  301. * @param v Value to assign to description.
  302. */
  303. public void setDescription(String v) {
  304. this.description = v;
  305. }
  306. /**
  307. * Get the value of jndiEnvironment which is used when instantiating
  308. * a jndi InitialContext. This InitialContext is used to locate the
  309. * backend ConnectionPoolDataSource.
  310. *
  311. * @return value of jndiEnvironment.
  312. */
  313. public String getJndiEnvironment(String key) {
  314. String value = null;
  315. if (jndiEnvironment != null) {
  316. value = jndiEnvironment.getProperty(key);
  317. }
  318. return value;
  319. }
  320. /**
  321. * Set the value of jndiEnvironment which is used when instantiating
  322. * a jndi InitialContext. This InitialContext is used to locate the
  323. * backend ConnectionPoolDataSource.
  324. *
  325. * @param v Value to assign to jndiEnvironment.
  326. */
  327. public void setJndiEnvironment(String key, String value) {
  328. if (jndiEnvironment == null) {
  329. jndiEnvironment = new Properties();
  330. }
  331. jndiEnvironment.setProperty(key, value);
  332. }
  333. /**
  334. * Get the value of loginTimeout.
  335. * @return value of loginTimeout.
  336. */
  337. public int getLoginTimeout() {
  338. return loginTimeout;
  339. }
  340. /**
  341. * Set the value of loginTimeout.
  342. * @param v Value to assign to loginTimeout.
  343. */
  344. public void setLoginTimeout(int v) {
  345. this.loginTimeout = v;
  346. }
  347. /**
  348. * Get the value of logWriter.
  349. * @return value of logWriter.
  350. */
  351. public PrintWriter getLogWriter() {
  352. if (logWriter == null) {
  353. logWriter = new PrintWriter(System.out);
  354. }
  355. return logWriter;
  356. }
  357. /**
  358. * Set the value of logWriter.
  359. * @param v Value to assign to logWriter.
  360. */
  361. public void setLogWriter(PrintWriter v) {
  362. this.logWriter = v;
  363. }
  364. /**
  365. * @see #getTestOnBorrow
  366. */
  367. public final boolean isTestOnBorrow() {
  368. return getTestOnBorrow();
  369. }
  370. /**
  371. * When <tt>true</tt>, objects will be
  372. * {*link PoolableObjectFactory#validateObject validated}
  373. * before being returned by the {*link #borrowObject}
  374. * method. If the object fails to validate,
  375. * it will be dropped from the pool, and we will attempt
  376. * to borrow another.
  377. *
  378. * @see #setTestOnBorrow
  379. */
  380. public boolean getTestOnBorrow() {
  381. return _testOnBorrow;
  382. }
  383. /**
  384. * When <tt>true</tt>, objects will be
  385. * {*link PoolableObjectFactory#validateObject validated}
  386. * before being returned by the {*link #borrowObject}
  387. * method. If the object fails to validate,
  388. * it will be dropped from the pool, and we will attempt
  389. * to borrow another.
  390. *
  391. * @see #getTestOnBorrow
  392. */
  393. public void setTestOnBorrow(boolean testOnBorrow) {
  394. assertInitializationAllowed();
  395. _testOnBorrow = testOnBorrow;
  396. testPositionSet = true;
  397. }
  398. /**
  399. * @see #getTestOnReturn
  400. */
  401. public final boolean isTestOnReturn() {
  402. return getTestOnReturn();
  403. }
  404. /**
  405. * When <tt>true</tt>, objects will be
  406. * {*link PoolableObjectFactory#validateObject validated}
  407. * before being returned to the pool within the
  408. * {*link #returnObject}.
  409. *
  410. * @see #setTestOnReturn
  411. */
  412. public boolean getTestOnReturn() {
  413. return _testOnReturn;
  414. }
  415. /**
  416. * When <tt>true</tt>, objects will be
  417. * {*link PoolableObjectFactory#validateObject validated}
  418. * before being returned to the pool within the
  419. * {*link #returnObject}.
  420. *
  421. * @see #getTestOnReturn
  422. */
  423. public void setTestOnReturn(boolean testOnReturn) {
  424. assertInitializationAllowed();
  425. _testOnReturn = testOnReturn;
  426. testPositionSet = true;
  427. }
  428. /**
  429. * Returns the number of milliseconds to sleep between runs of the
  430. * idle object evictor thread.
  431. * When non-positive, no idle object evictor thread will be
  432. * run.
  433. *
  434. * @see #setTimeBetweenEvictionRunsMillis
  435. */
  436. public int getTimeBetweenEvictionRunsMillis() {
  437. return _timeBetweenEvictionRunsMillis;
  438. }
  439. /**
  440. * Sets the number of milliseconds to sleep between runs of the
  441. * idle object evictor thread.
  442. * When non-positive, no idle object evictor thread will be
  443. * run.
  444. *
  445. * @see #getTimeBetweenEvictionRunsMillis
  446. */
  447. public void
  448. setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
  449. assertInitializationAllowed();
  450. _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
  451. }
  452. /**
  453. * Returns the number of objects to examine during each run of the
  454. * idle object evictor thread (if any).
  455. *
  456. * @see #setNumTestsPerEvictionRun
  457. * @see #setTimeBetweenEvictionRunsMillis
  458. */
  459. public int getNumTestsPerEvictionRun() {
  460. return _numTestsPerEvictionRun;
  461. }
  462. /**
  463. * Sets the number of objects to examine during each run of the
  464. * idle object evictor thread (if any).
  465. * <p>
  466. * When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt>
  467. * tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
  468. * idle objects will be tested per run.
  469. *
  470. * @see #getNumTestsPerEvictionRun
  471. * @see #setTimeBetweenEvictionRunsMillis
  472. */
  473. public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
  474. assertInitializationAllowed();
  475. _numTestsPerEvictionRun = numTestsPerEvictionRun;
  476. }
  477. /**
  478. * Returns the minimum amount of time an object may sit idle in the pool
  479. * before it is eligable for eviction by the idle object evictor
  480. * (if any).
  481. *
  482. * @see #setMinEvictableIdleTimeMillis
  483. * @see #setTimeBetweenEvictionRunsMillis
  484. */
  485. public int getMinEvictableIdleTimeMillis() {
  486. return _minEvictableIdleTimeMillis;
  487. }
  488. /**
  489. * Sets the minimum amount of time an object may sit idle in the pool
  490. * before it is eligable for eviction by the idle object evictor
  491. * (if any).
  492. * When non-positive, no objects will be evicted from the pool
  493. * due to idle time alone.
  494. *
  495. * @see #getMinEvictableIdleTimeMillis
  496. * @see #setTimeBetweenEvictionRunsMillis
  497. */
  498. public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
  499. assertInitializationAllowed();
  500. _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
  501. }
  502. /**
  503. * @see #getTestWhileIdle
  504. */
  505. public final boolean isTestWhileIdle() {
  506. return getTestWhileIdle();
  507. }
  508. /**
  509. * When <tt>true</tt>, objects will be
  510. * {*link PoolableObjectFactory#validateObject validated}
  511. * by the idle object evictor (if any). If an object
  512. * fails to validate, it will be dropped from the pool.
  513. *
  514. * @see #setTestWhileIdle
  515. * @see #setTimeBetweenEvictionRunsMillis
  516. */
  517. public boolean getTestWhileIdle() {
  518. return _testWhileIdle;
  519. }
  520. /**
  521. * When <tt>true</tt>, objects will be
  522. * {*link PoolableObjectFactory#validateObject validated}
  523. * by the idle object evictor (if any). If an object
  524. * fails to validate, it will be dropped from the pool.
  525. *
  526. * @see #getTestWhileIdle
  527. * @see #setTimeBetweenEvictionRunsMillis
  528. */
  529. public void setTestWhileIdle(boolean testWhileIdle) {
  530. assertInitializationAllowed();
  531. _testWhileIdle = testWhileIdle;
  532. testPositionSet = true;
  533. }
  534. /**
  535. * The SQL query that will be used to validate connections from this pool
  536. * before returning them to the caller. If specified, this query
  537. * <strong>MUST</strong> be an SQL SELECT statement that returns at least
  538. * one row.
  539. */
  540. public String getValidationQuery() {
  541. return (this.validationQuery);
  542. }
  543. /**
  544. * The SQL query that will be used to validate connections from this pool
  545. * before returning them to the caller. If specified, this query
  546. * <strong>MUST</strong> be an SQL SELECT statement that returns at least
  547. * one row. Default behavior is to test the connection when it is
  548. * borrowed.
  549. */
  550. public void setValidationQuery(String validationQuery) {
  551. assertInitializationAllowed();
  552. this.validationQuery = validationQuery;
  553. if (!testPositionSet) {
  554. setTestOnBorrow(true);
  555. }
  556. }
  557. // ----------------------------------------------------------------------
  558. // Instrumentation Methods
  559. // ----------------------------------------------------------------------
  560. // DataSource implementation
  561. /**
  562. * Attempt to establish a database connection.
  563. */
  564. public Connection getConnection() throws SQLException {
  565. return getConnection(null, null);
  566. }
  567. /**
  568. * Attempt to establish a database connection.
  569. */
  570. public Connection getConnection(String username, String password)
  571. throws SQLException {
  572. if (instanceKey == null) {
  573. throw new SQLException("Must set the ConnectionPoolDataSource "
  574. + "through setDataSourceName or setConnectionPoolDataSource"
  575. + " before calling getConnection.");
  576. }
  577. getConnectionCalled = true;
  578. PooledConnectionAndInfo info = null;
  579. try {
  580. info = getPooledConnectionAndInfo(username, password);
  581. } catch (NoSuchElementException e) {
  582. closeDueToException(info);
  583. throw new SQLNestedException("Cannot borrow connection from pool", e);
  584. } catch (RuntimeException e) {
  585. closeDueToException(info);
  586. throw e;
  587. } catch (SQLException e) {
  588. closeDueToException(info);
  589. throw e;
  590. } catch (Exception e) {
  591. closeDueToException(info);
  592. throw new SQLNestedException("Cannot borrow connection from pool", e);
  593. }
  594. if (!(null == password ? null == info.getPassword()
  595. : password.equals(info.getPassword()))) {
  596. closeDueToException(info);
  597. throw new SQLException("Given password did not match password used"
  598. + " to create the PooledConnection.");
  599. }
  600. Connection con = info.getPooledConnection().getConnection();
  601. setupDefaults(con, username);
  602. con.clearWarnings();
  603. return con;
  604. }
  605. protected abstract PooledConnectionAndInfo
  606. getPooledConnectionAndInfo(String username, String password)
  607. throws SQLException;
  608. protected abstract void setupDefaults(Connection con, String username)
  609. throws SQLException;
  610. private void closeDueToException(PooledConnectionAndInfo info) {
  611. if (info != null) {
  612. try {
  613. info.getPooledConnection().getConnection().close();
  614. } catch (Exception e) {
  615. // do not throw this exception because we are in the middle
  616. // of handling another exception. But record it because
  617. // it potentially leaks connections from the pool.
  618. getLogWriter().println("[ERROR] Could not return connection to "
  619. + "pool during exception handling. " + e.getMessage());
  620. }
  621. }
  622. }
  623. protected ConnectionPoolDataSource
  624. testCPDS(String username, String password)
  625. throws javax.naming.NamingException, SQLException {
  626. // The source of physical db connections
  627. ConnectionPoolDataSource cpds = this.cpds;
  628. if (cpds == null) {
  629. Context ctx = null;
  630. if (jndiEnvironment == null) {
  631. ctx = new InitialContext();
  632. } else {
  633. ctx = new InitialContext(jndiEnvironment);
  634. }
  635. Object ds = ctx.lookup(dataSourceName);
  636. if (ds instanceof ConnectionPoolDataSource) {
  637. cpds = (ConnectionPoolDataSource) ds;
  638. } else {
  639. throw new SQLException("Illegal configuration: "
  640. + "DataSource " + dataSourceName
  641. + " (" + ds.getClass().getName() + ")"
  642. + " doesn't implement javax.sql.ConnectionPoolDataSource");
  643. }
  644. }
  645. // try to get a connection with the supplied username/password
  646. PooledConnection conn = null;
  647. try {
  648. if (username != null) {
  649. conn = cpds.getPooledConnection(username, password);
  650. }
  651. else {
  652. conn = cpds.getPooledConnection();
  653. }
  654. if (conn == null) {
  655. throw new SQLException(
  656. "Cannot connect using the supplied username/password");
  657. }
  658. }
  659. finally {
  660. if (conn != null) {
  661. try {
  662. conn.close();
  663. }
  664. catch (SQLException e) {
  665. // at least we could connect
  666. }
  667. }
  668. }
  669. return cpds;
  670. }
  671. protected byte whenExhaustedAction(int maxActive, int maxWait) {
  672. byte whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
  673. if (maxActive <= 0) {
  674. whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_GROW;
  675. } else if (maxWait == 0) {
  676. whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
  677. }
  678. return whenExhausted;
  679. }
  680. // ----------------------------------------------------------------------
  681. // Referenceable implementation
  682. /**
  683. * <CODE>Referenceable</CODE> implementation prepares object for
  684. * binding in jndi.
  685. */
  686. public Reference getReference() throws NamingException {
  687. Reference ref = new Reference(getClass().getName(),
  688. InstanceKeyObjectFactory.class.getName(), null);
  689. ref.add(new StringRefAddr("instanceKey", instanceKey));
  690. return ref;
  691. }
  692. }