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 catalina and JNDI and it also contains a non-JNDI example.
080: * </p>
081: *
082: * @author John D. McNally
083: * @version $Revision: 500687 $ $Date: 2007-01-27 16:33:47 -0700 (Sat, 27 Jan 2007) $
084: */
085: public abstract class InstanceKeyDataSource implements DataSource,
086: Referenceable, Serializable {
087: private static final String GET_CONNECTION_CALLED = "A Connection was already requested from this source, "
088: + "further initialization is not allowed.";
089: private static final String BAD_TRANSACTION_ISOLATION = "The requested TransactionIsolation level is invalid.";
090: /**
091: * Internal constant to indicate the level is not set.
092: */
093: protected static final int UNKNOWN_TRANSACTIONISOLATION = -1;
094:
095: private boolean getConnectionCalled = false;
096:
097: private ConnectionPoolDataSource cpds = null;
098: /** DataSource Name used to find the ConnectionPoolDataSource */
099: private String dataSourceName = null;
100: private boolean defaultAutoCommit = false;
101: private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
102: private int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
103: private int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
104: private int maxWait = (int) Math.min((long) Integer.MAX_VALUE,
105: GenericObjectPool.DEFAULT_MAX_WAIT);
106: private boolean defaultReadOnly = false;
107: /** Description */
108: private String description = null;
109: /** Environment that may be used to set up a jndi initial context. */
110: Properties jndiEnvironment = null;
111: /** Login TimeOut in seconds */
112: private int loginTimeout = 0;
113: /** Log stream */
114: private PrintWriter logWriter = null;
115: private boolean _testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
116: private boolean _testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
117: private int _timeBetweenEvictionRunsMillis = (int) Math
118: .min(
119: (long) Integer.MAX_VALUE,
120: GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS);
121: private int _numTestsPerEvictionRun = GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
122: private int _minEvictableIdleTimeMillis = (int) Math.min(
123: (long) Integer.MAX_VALUE,
124: GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
125: private boolean _testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
126: private String validationQuery = null;
127: private boolean rollbackAfterValidation = false;
128: private boolean testPositionSet = false;
129:
130: protected String instanceKey = null;
131:
132: /**
133: * Default no-arg constructor for Serialization
134: */
135: public InstanceKeyDataSource() {
136: defaultAutoCommit = true;
137: }
138:
139: /**
140: * Throws an IllegalStateException, if a PooledConnection has already
141: * been requested.
142: */
143: protected void assertInitializationAllowed()
144: throws IllegalStateException {
145: if (getConnectionCalled) {
146: throw new IllegalStateException(GET_CONNECTION_CALLED);
147: }
148: }
149:
150: /**
151: * Close pool being maintained by this datasource.
152: */
153: public abstract void close() throws Exception;
154:
155: // -------------------------------------------------------------------
156: // Properties
157:
158: /**
159: * Get the value of connectionPoolDataSource. This method will return
160: * null, if the backing datasource is being accessed via jndi.
161: *
162: * @return value of connectionPoolDataSource.
163: */
164: public ConnectionPoolDataSource getConnectionPoolDataSource() {
165: return cpds;
166: }
167:
168: /**
169: * Set the backend ConnectionPoolDataSource. This property should not be
170: * set if using jndi to access the datasource.
171: *
172: * @param v Value to assign to connectionPoolDataSource.
173: */
174: public void setConnectionPoolDataSource(ConnectionPoolDataSource v) {
175: assertInitializationAllowed();
176: if (dataSourceName != null) {
177: throw new IllegalStateException(
178: "Cannot set the DataSource, if JNDI is used.");
179: }
180: if (cpds != null) {
181: throw new IllegalStateException(
182: "The CPDS has already been set. It cannot be altered.");
183: }
184: cpds = v;
185: instanceKey = InstanceKeyObjectFactory
186: .registerNewInstance(this );
187: }
188:
189: /**
190: * Get the name of the ConnectionPoolDataSource which backs this pool.
191: * This name is used to look up the datasource from a jndi service
192: * provider.
193: *
194: * @return value of dataSourceName.
195: */
196: public String getDataSourceName() {
197: return dataSourceName;
198: }
199:
200: /**
201: * Set the name of the ConnectionPoolDataSource which backs this pool.
202: * This name is used to look up the datasource from a jndi service
203: * provider.
204: *
205: * @param v Value to assign to dataSourceName.
206: */
207: public void setDataSourceName(String v) {
208: assertInitializationAllowed();
209: if (cpds != null) {
210: throw new IllegalStateException(
211: "Cannot set the JNDI name for the DataSource, if already "
212: + "set using setConnectionPoolDataSource.");
213: }
214: if (dataSourceName != null) {
215: throw new IllegalStateException(
216: "The DataSourceName has already been set. "
217: + "It cannot be altered.");
218: }
219: this .dataSourceName = v;
220: instanceKey = InstanceKeyObjectFactory
221: .registerNewInstance(this );
222: }
223:
224: /**
225: * Get the value of defaultAutoCommit, which defines the state of
226: * connections handed out from this pool. The value can be changed
227: * on the Connection using Connection.setAutoCommit(boolean).
228: * The default is true.
229: *
230: * @return value of defaultAutoCommit.
231: */
232: public boolean isDefaultAutoCommit() {
233: return defaultAutoCommit;
234: }
235:
236: /**
237: * Set the value of defaultAutoCommit, which defines the state of
238: * connections handed out from this pool. The value can be changed
239: * on the Connection using Connection.setAutoCommit(boolean).
240: * The default is true.
241: *
242: * @param v Value to assign to defaultAutoCommit.
243: */
244: public void setDefaultAutoCommit(boolean v) {
245: assertInitializationAllowed();
246: this .defaultAutoCommit = v;
247: }
248:
249: /**
250: * Get the value of defaultReadOnly, which defines the state of
251: * connections handed out from this pool. The value can be changed
252: * on the Connection using Connection.setReadOnly(boolean).
253: * The default is false.
254: *
255: * @return value of defaultReadOnly.
256: */
257: public boolean isDefaultReadOnly() {
258: return defaultReadOnly;
259: }
260:
261: /**
262: * Set the value of defaultReadOnly, which defines the state of
263: * connections handed out from this pool. The value can be changed
264: * on the Connection using Connection.setReadOnly(boolean).
265: * The default is false.
266: *
267: * @param v Value to assign to defaultReadOnly.
268: */
269: public void setDefaultReadOnly(boolean v) {
270: assertInitializationAllowed();
271: this .defaultReadOnly = v;
272: }
273:
274: /**
275: * Get the value of defaultTransactionIsolation, which defines the state of
276: * connections handed out from this pool. The value can be changed
277: * on the Connection using Connection.setTransactionIsolation(int).
278: * If this method returns -1, the default is JDBC driver dependent.
279: *
280: * @return value of defaultTransactionIsolation.
281: */
282: public int getDefaultTransactionIsolation() {
283: return defaultTransactionIsolation;
284: }
285:
286: /**
287: * Set the value of defaultTransactionIsolation, which defines the state of
288: * connections handed out from this pool. The value can be changed
289: * on the Connection using Connection.setTransactionIsolation(int).
290: * The default is JDBC driver dependent.
291: *
292: * @param v Value to assign to defaultTransactionIsolation
293: */
294: public void setDefaultTransactionIsolation(int v) {
295: assertInitializationAllowed();
296: switch (v) {
297: case Connection.TRANSACTION_NONE:
298: case Connection.TRANSACTION_READ_COMMITTED:
299: case Connection.TRANSACTION_READ_UNCOMMITTED:
300: case Connection.TRANSACTION_REPEATABLE_READ:
301: case Connection.TRANSACTION_SERIALIZABLE:
302: break;
303: default:
304: throw new IllegalArgumentException(
305: BAD_TRANSACTION_ISOLATION);
306: }
307: this .defaultTransactionIsolation = v;
308: }
309:
310: /**
311: * Get the description. This property is defined by jdbc as for use with
312: * GUI (or other) tools that might deploy the datasource. It serves no
313: * internal purpose.
314: *
315: * @return value of description.
316: */
317: public String getDescription() {
318: return description;
319: }
320:
321: /**
322: * Set the description. This property is defined by jdbc as for use with
323: * GUI (or other) tools that might deploy the datasource. It serves no
324: * internal purpose.
325: *
326: * @param v Value to assign to description.
327: */
328: public void setDescription(String v) {
329: this .description = v;
330: }
331:
332: /**
333: * Get the value of jndiEnvironment which is used when instantiating
334: * a jndi InitialContext. This InitialContext is used to locate the
335: * backend ConnectionPoolDataSource.
336: *
337: * @return value of jndiEnvironment.
338: */
339: public String getJndiEnvironment(String key) {
340: String value = null;
341: if (jndiEnvironment != null) {
342: value = jndiEnvironment.getProperty(key);
343: }
344: return value;
345: }
346:
347: /**
348: * Sets the value of the given JNDI environment property to be used when
349: * instantiating a JNDI InitialContext. This InitialContext is used to
350: * locate the backend ConnectionPoolDataSource.
351: *
352: * @param key the JNDI environment property to set.
353: * @param value the value assigned to specified JNDI environment property.
354: */
355: public void setJndiEnvironment(String key, String value) {
356: if (jndiEnvironment == null) {
357: jndiEnvironment = new Properties();
358: }
359: jndiEnvironment.setProperty(key, value);
360: }
361:
362: /**
363: * Get the value of loginTimeout.
364: * @return value of loginTimeout.
365: */
366: public int getLoginTimeout() {
367: return loginTimeout;
368: }
369:
370: /**
371: * Set the value of loginTimeout.
372: * @param v Value to assign to loginTimeout.
373: */
374: public void setLoginTimeout(int v) {
375: this .loginTimeout = v;
376: }
377:
378: /**
379: * Get the value of logWriter.
380: * @return value of logWriter.
381: */
382: public PrintWriter getLogWriter() {
383: if (logWriter == null) {
384: logWriter = new PrintWriter(System.out);
385: }
386: return logWriter;
387: }
388:
389: /**
390: * Set the value of logWriter.
391: * @param v Value to assign to logWriter.
392: */
393: public void setLogWriter(PrintWriter v) {
394: this .logWriter = v;
395: }
396:
397: /**
398: * @see #getTestOnBorrow
399: */
400: public final boolean isTestOnBorrow() {
401: return getTestOnBorrow();
402: }
403:
404: /**
405: * When <tt>true</tt>, objects will be
406: * {*link PoolableObjectFactory#validateObject validated}
407: * before being returned by the {*link #borrowObject}
408: * method. If the object fails to validate,
409: * it will be dropped from the pool, and we will attempt
410: * to borrow another.
411: *
412: * @see #setTestOnBorrow
413: */
414: public boolean getTestOnBorrow() {
415: return _testOnBorrow;
416: }
417:
418: /**
419: * When <tt>true</tt>, objects will be
420: * {*link PoolableObjectFactory#validateObject validated}
421: * before being returned by the {*link #borrowObject}
422: * method. If the object fails to validate,
423: * it will be dropped from the pool, and we will attempt
424: * to borrow another. For a <code>true</code> value to have any effect,
425: * the <code>validationQuery</code> property must be set to a non-null
426: * string.
427: *
428: * @see #getTestOnBorrow
429: */
430: public void setTestOnBorrow(boolean testOnBorrow) {
431: assertInitializationAllowed();
432: _testOnBorrow = testOnBorrow;
433: testPositionSet = true;
434: }
435:
436: /**
437: * @see #getTestOnReturn
438: */
439: public final boolean isTestOnReturn() {
440: return getTestOnReturn();
441: }
442:
443: /**
444: * When <tt>true</tt>, objects will be
445: * {*link PoolableObjectFactory#validateObject validated}
446: * before being returned to the pool within the
447: * {*link #returnObject}.
448: *
449: * @see #setTestOnReturn
450: */
451: public boolean getTestOnReturn() {
452: return _testOnReturn;
453: }
454:
455: /**
456: * When <tt>true</tt>, objects will be
457: * {*link PoolableObjectFactory#validateObject validated}
458: * before being returned to the pool within the
459: * {*link #returnObject}. For a <code>true</code> value to have any effect,
460: * the <code>validationQuery</code> property must be set to a non-null
461: * string.
462: *
463: * @see #getTestOnReturn
464: */
465: public void setTestOnReturn(boolean testOnReturn) {
466: assertInitializationAllowed();
467: _testOnReturn = testOnReturn;
468: testPositionSet = true;
469: }
470:
471: /**
472: * Returns the number of milliseconds to sleep between runs of the
473: * idle object evictor thread.
474: * When non-positive, no idle object evictor thread will be
475: * run.
476: *
477: * @see #setTimeBetweenEvictionRunsMillis
478: */
479: public int getTimeBetweenEvictionRunsMillis() {
480: return _timeBetweenEvictionRunsMillis;
481: }
482:
483: /**
484: * Sets the number of milliseconds to sleep between runs of the
485: * idle object evictor thread.
486: * When non-positive, no idle object evictor thread will be
487: * run.
488: *
489: * @see #getTimeBetweenEvictionRunsMillis
490: */
491: public void setTimeBetweenEvictionRunsMillis(
492: int timeBetweenEvictionRunsMillis) {
493: assertInitializationAllowed();
494: _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
495: }
496:
497: /**
498: * Returns the number of objects to examine during each run of the
499: * idle object evictor thread (if any).
500: *
501: * @see #setNumTestsPerEvictionRun
502: * @see #setTimeBetweenEvictionRunsMillis
503: */
504: public int getNumTestsPerEvictionRun() {
505: return _numTestsPerEvictionRun;
506: }
507:
508: /**
509: * Sets the number of objects to examine during each run of the
510: * idle object evictor thread (if any).
511: * <p>
512: * When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt>
513: * tests will be run. I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
514: * idle objects will be tested per run.
515: *
516: * @see #getNumTestsPerEvictionRun
517: * @see #setTimeBetweenEvictionRunsMillis
518: */
519: public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
520: assertInitializationAllowed();
521: _numTestsPerEvictionRun = numTestsPerEvictionRun;
522: }
523:
524: /**
525: * Returns the minimum amount of time an object may sit idle in the pool
526: * before it is eligable for eviction by the idle object evictor
527: * (if any).
528: *
529: * @see #setMinEvictableIdleTimeMillis
530: * @see #setTimeBetweenEvictionRunsMillis
531: */
532: public int getMinEvictableIdleTimeMillis() {
533: return _minEvictableIdleTimeMillis;
534: }
535:
536: /**
537: * Sets the minimum amount of time an object may sit idle in the pool
538: * before it is eligable for eviction by the idle object evictor
539: * (if any).
540: * When non-positive, no objects will be evicted from the pool
541: * due to idle time alone.
542: *
543: * @see #getMinEvictableIdleTimeMillis
544: * @see #setTimeBetweenEvictionRunsMillis
545: */
546: public void setMinEvictableIdleTimeMillis(
547: int minEvictableIdleTimeMillis) {
548: assertInitializationAllowed();
549: _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
550: }
551:
552: /**
553: * @see #getTestWhileIdle
554: */
555: public final boolean isTestWhileIdle() {
556: return getTestWhileIdle();
557: }
558:
559: /**
560: * When <tt>true</tt>, objects will be
561: * {*link PoolableObjectFactory#validateObject validated}
562: * by the idle object evictor (if any). If an object
563: * fails to validate, it will be dropped from the pool.
564: *
565: * @see #setTestWhileIdle
566: * @see #setTimeBetweenEvictionRunsMillis
567: */
568: public boolean getTestWhileIdle() {
569: return _testWhileIdle;
570: }
571:
572: /**
573: * When <tt>true</tt>, objects will be
574: * {*link PoolableObjectFactory#validateObject validated}
575: * by the idle object evictor (if any). If an object
576: * fails to validate, it will be dropped from the pool. For a
577: * <code>true</code> value to have any effect,
578: * the <code>validationQuery</code> property must be set to a non-null
579: * string.
580: *
581: * @see #getTestWhileIdle
582: * @see #setTimeBetweenEvictionRunsMillis
583: */
584: public void setTestWhileIdle(boolean testWhileIdle) {
585: assertInitializationAllowed();
586: _testWhileIdle = testWhileIdle;
587: testPositionSet = true;
588: }
589:
590: /**
591: * The SQL query that will be used to validate connections from this pool
592: * before returning them to the caller. If specified, this query
593: * <strong>MUST</strong> be an SQL SELECT statement that returns at least
594: * one row.
595: */
596: public String getValidationQuery() {
597: return (this .validationQuery);
598: }
599:
600: /**
601: * The SQL query that will be used to validate connections from this pool
602: * before returning them to the caller. If specified, this query
603: * <strong>MUST</strong> be an SQL SELECT statement that returns at least
604: * one row. Default behavior is to test the connection when it is
605: * borrowed.
606: */
607: public void setValidationQuery(String validationQuery) {
608: assertInitializationAllowed();
609: this .validationQuery = validationQuery;
610: if (!testPositionSet) {
611: setTestOnBorrow(true);
612: }
613: }
614:
615: /**
616: * Whether a rollback will be issued after executing the SQL query
617: * that will be used to validate connections from this pool
618: * before returning them to the caller.
619: *
620: * @return true if a rollback will be issued after executing the
621: * validation query
622: * @since 1.2.2
623: */
624: public boolean isRollbackAfterValidation() {
625: return (this .rollbackAfterValidation);
626: }
627:
628: /**
629: * Whether a rollback will be issued after executing the SQL query
630: * that will be used to validate connections from this pool
631: * before returning them to the caller. Default behavior is NOT
632: * to issue a rollback. The setting will only have an effect
633: * if a validation query is set
634: *
635: * @param rollbackAfterValidation new property value
636: * @since 1.2.2
637: */
638: public void setRollbackAfterValidation(
639: boolean rollbackAfterValidation) {
640: assertInitializationAllowed();
641: this .rollbackAfterValidation = rollbackAfterValidation;
642: }
643:
644: // ----------------------------------------------------------------------
645: // Instrumentation Methods
646:
647: // ----------------------------------------------------------------------
648: // DataSource implementation
649:
650: /**
651: * Attempt to establish a database connection.
652: */
653: public Connection getConnection() throws SQLException {
654: return getConnection(null, null);
655: }
656:
657: /**
658: * Attempt to establish a database connection.
659: */
660: public Connection getConnection(String username, String password)
661: throws SQLException {
662: if (instanceKey == null) {
663: throw new SQLException(
664: "Must set the ConnectionPoolDataSource "
665: + "through setDataSourceName or setConnectionPoolDataSource"
666: + " before calling getConnection.");
667: }
668: getConnectionCalled = true;
669: PooledConnectionAndInfo info = null;
670: try {
671: info = getPooledConnectionAndInfo(username, password);
672: } catch (NoSuchElementException e) {
673: closeDueToException(info);
674: throw new SQLNestedException(
675: "Cannot borrow connection from pool", e);
676: } catch (RuntimeException e) {
677: closeDueToException(info);
678: throw e;
679: } catch (SQLException e) {
680: closeDueToException(info);
681: throw e;
682: } catch (Exception e) {
683: closeDueToException(info);
684: throw new SQLNestedException(
685: "Cannot borrow connection from pool", e);
686: }
687:
688: if (!(null == password ? null == info.getPassword() : password
689: .equals(info.getPassword()))) {
690: closeDueToException(info);
691: throw new SQLException(
692: "Given password did not match password used"
693: + " to create the PooledConnection.");
694: }
695:
696: Connection con = info.getPooledConnection().getConnection();
697: setupDefaults(con, username);
698: con.clearWarnings();
699: return con;
700: }
701:
702: protected abstract PooledConnectionAndInfo getPooledConnectionAndInfo(
703: String username, String password) throws SQLException;
704:
705: protected abstract void setupDefaults(Connection con,
706: String username) throws SQLException;
707:
708: private void closeDueToException(PooledConnectionAndInfo info) {
709: if (info != null) {
710: try {
711: info.getPooledConnection().getConnection().close();
712: } catch (Exception e) {
713: // do not throw this exception because we are in the middle
714: // of handling another exception. But record it because
715: // it potentially leaks connections from the pool.
716: getLogWriter().println(
717: "[ERROR] Could not return connection to "
718: + "pool during exception handling. "
719: + e.getMessage());
720: }
721: }
722: }
723:
724: protected ConnectionPoolDataSource testCPDS(String username,
725: String password) throws javax.naming.NamingException,
726: SQLException {
727: // The source of physical db connections
728: ConnectionPoolDataSource cpds = this .cpds;
729: if (cpds == null) {
730: Context ctx = null;
731: if (jndiEnvironment == null) {
732: ctx = new InitialContext();
733: } else {
734: ctx = new InitialContext(jndiEnvironment);
735: }
736: Object ds = ctx.lookup(dataSourceName);
737: if (ds instanceof ConnectionPoolDataSource) {
738: cpds = (ConnectionPoolDataSource) ds;
739: } else {
740: throw new SQLException(
741: "Illegal configuration: "
742: + "DataSource "
743: + dataSourceName
744: + " ("
745: + ds.getClass().getName()
746: + ")"
747: + " doesn't implement javax.sql.ConnectionPoolDataSource");
748: }
749: }
750:
751: // try to get a connection with the supplied username/password
752: PooledConnection conn = null;
753: try {
754: if (username != null) {
755: conn = cpds.getPooledConnection(username, password);
756: } else {
757: conn = cpds.getPooledConnection();
758: }
759: if (conn == null) {
760: throw new SQLException(
761: "Cannot connect using the supplied username/password");
762: }
763: } finally {
764: if (conn != null) {
765: try {
766: conn.close();
767: } catch (SQLException e) {
768: // at least we could connect
769: }
770: }
771: }
772: return cpds;
773: }
774:
775: protected byte whenExhaustedAction(int maxActive, int maxWait) {
776: byte whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
777: if (maxActive <= 0) {
778: whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_GROW;
779: } else if (maxWait == 0) {
780: whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
781: }
782: return whenExhausted;
783: }
784:
785: // ----------------------------------------------------------------------
786: // Referenceable implementation
787:
788: /**
789: * Retrieves the Reference of this object.
790: * <strong>Note:</strong> <code>InstanceKeyDataSource</code> subclasses
791: * should override this method. The implementaion included below
792: * is not robust and will be removed at the next major version DBCP
793: * release.
794: *
795: * @return The non-null Reference of this object.
796: * @exception NamingException If a naming exception was encountered
797: * while retrieving the reference.
798: */
799: // TODO: Remove the implementation of this method at next major
800: // version release.
801: public Reference getReference() throws NamingException {
802: final String className = getClass().getName();
803: final String factoryName = className + "Factory"; // XXX: not robust
804: Reference ref = new Reference(className, factoryName, null);
805: ref.add(new StringRefAddr("instanceKey", instanceKey));
806: return ref;
807: }
808: }
|