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;
019
020 import java.sql.CallableStatement;
021 import java.sql.Connection;
022 import java.sql.DatabaseMetaData;
023 import java.sql.PreparedStatement;
024 import java.sql.SQLException;
025 import java.sql.SQLWarning;
026 import java.sql.Statement;
027 import java.util.Iterator;
028 import java.util.List;
029 import java.util.Map;
030 import java.sql.ResultSet;
031 /* JDBC_4_ANT_KEY_BEGIN */
032 import java.sql.Array;
033 import java.sql.Blob;
034 import java.sql.ClientInfoStatus;
035 import java.sql.Clob;
036 import java.sql.NClob;
037 import java.sql.SQLClientInfoException;
038 import java.sql.SQLXML;
039 import java.sql.Struct;
040 import java.util.Collections;
041 import java.util.Properties;
042 /* JDBC_4_ANT_KEY_END */
043
044 /**
045 * A base delegating implementation of {@link Connection}.
046 * <p>
047 * All of the methods from the {@link Connection} interface
048 * simply check to see that the {@link Connection} is active,
049 * and call the corresponding method on the "delegate"
050 * provided in my constructor.
051 * <p>
052 * Extends AbandonedTrace to implement Connection tracking and
053 * logging of code which created the Connection. Tracking the
054 * Connection ensures that the AbandonedObjectPool can close
055 * this connection and recycle it if its pool of connections
056 * is nearing exhaustion and this connection's last usage is
057 * older than the removeAbandonedTimeout.
058 *
059 * @author Rodney Waldhoff
060 * @author Glenn L. Nielsen
061 * @author James House
062 * @author Dirk Verbeeck
063 * @version $Revision: 896719 $ $Date: 2010-01-06 18:42:22 -0500 (Wed, 06 Jan 2010) $
064 */
065 public class DelegatingConnection extends AbandonedTrace
066 implements Connection {
067
068 /* JDBC_4_ANT_KEY_BEGIN */
069 private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES =
070 Collections.<String, ClientInfoStatus>emptyMap();
071 /* JDBC_4_ANT_KEY_END */
072
073 /** My delegate {@link Connection}. */
074 protected Connection _conn = null;
075
076 protected boolean _closed = false;
077
078 /**
079 * Create a wrapper for the Connection which traces this
080 * Connection in the AbandonedObjectPool.
081 *
082 * @param c the {@link Connection} to delegate all calls to.
083 */
084 public DelegatingConnection(Connection c) {
085 super();
086 _conn = c;
087 }
088
089 /**
090 * Create a wrapper for the Connection which traces
091 * the Statements created so that any unclosed Statements
092 * can be closed when this Connection is closed.
093 *
094 * @param c the {@link Connection} to delegate all calls to.
095 * @param config the configuration for tracing abandoned objects
096 */
097 public DelegatingConnection(Connection c, AbandonedConfig config) {
098 super(config);
099 _conn = c;
100 }
101
102 /**
103 * Returns a string representation of the metadata associated with
104 * the innnermost delegate connection.
105 *
106 * @since 1.2.2
107 */
108 public String toString() {
109 String s = null;
110
111 Connection c = this.getInnermostDelegateInternal();
112 if (c != null) {
113 try {
114 if (c.isClosed()) {
115 s = "connection is closed";
116 }
117 else {
118 DatabaseMetaData meta = c.getMetaData();
119 if (meta != null) {
120 StringBuffer sb = new StringBuffer();
121 sb.append(meta.getURL());
122 sb.append(", UserName=");
123 sb.append(meta.getUserName());
124 sb.append(", ");
125 sb.append(meta.getDriverName());
126 s = sb.toString();
127 }
128 }
129 }
130 catch (SQLException ex) {
131 // Ignore
132 }
133 }
134
135 if (s == null) {
136 s = super.toString();
137 }
138
139 return s;
140 }
141
142 /**
143 * Returns my underlying {@link Connection}.
144 * @return my underlying {@link Connection}.
145 */
146 public Connection getDelegate() {
147 return getDelegateInternal();
148 }
149
150 /**
151 * Should be final but can't be for compatibility with previous releases.
152 */
153 protected Connection getDelegateInternal() {
154 return _conn;
155 }
156
157 /**
158 * Compares innermost delegate to the given connection.
159 *
160 * @param c connection to compare innermost delegate with
161 * @return true if innermost delegate equals <code>c</code>
162 * @since 1.2.2
163 */
164 public boolean innermostDelegateEquals(Connection c) {
165 Connection innerCon = getInnermostDelegateInternal();
166 if (innerCon == null) {
167 return c == null;
168 } else {
169 return innerCon.equals(c);
170 }
171 }
172
173 /**
174 * This method considers two objects to be equal
175 * if the underlying jdbc objects are equal.
176 */
177 public boolean equals(Object obj) {
178 if (obj == null) {
179 return false;
180 }
181 if (obj == this) {
182 return true;
183 }
184 Connection delegate = getInnermostDelegateInternal();
185 if (delegate == null) {
186 return false;
187 }
188 if (obj instanceof DelegatingConnection) {
189 DelegatingConnection c = (DelegatingConnection) obj;
190 return c.innermostDelegateEquals(delegate);
191 }
192 else {
193 return delegate.equals(obj);
194 }
195 }
196
197 public int hashCode() {
198 Object obj = getInnermostDelegateInternal();
199 if (obj == null) {
200 return 0;
201 }
202 return obj.hashCode();
203 }
204
205
206 /**
207 * If my underlying {@link Connection} is not a
208 * <tt>DelegatingConnection</tt>, returns it,
209 * otherwise recursively invokes this method on
210 * my delegate.
211 * <p>
212 * Hence this method will return the first
213 * delegate that is not a <tt>DelegatingConnection</tt>,
214 * or <tt>null</tt> when no non-<tt>DelegatingConnection</tt>
215 * delegate can be found by traversing this chain.
216 * <p>
217 * This method is useful when you may have nested
218 * <tt>DelegatingConnection</tt>s, and you want to make
219 * sure to obtain a "genuine" {@link Connection}.
220 */
221 public Connection getInnermostDelegate() {
222 return getInnermostDelegateInternal();
223 }
224
225 protected final Connection getInnermostDelegateInternal() {
226 Connection c = _conn;
227 while(c != null && c instanceof DelegatingConnection) {
228 c = ((DelegatingConnection)c).getDelegateInternal();
229 if(this == c) {
230 return null;
231 }
232 }
233 return c;
234 }
235
236 /** Sets my delegate. */
237 public void setDelegate(Connection c) {
238 _conn = c;
239 }
240
241 /**
242 * Closes the underlying connection, and close
243 * any Statements that were not explicitly closed.
244 */
245 public void close() throws SQLException {
246 passivate();
247 _conn.close();
248 }
249
250 protected void handleException(SQLException e) throws SQLException {
251 throw e;
252 }
253
254 public Statement createStatement() throws SQLException {
255 checkOpen();
256 try {
257 return new DelegatingStatement(this, _conn.createStatement());
258 }
259 catch (SQLException e) {
260 handleException(e);
261 return null;
262 }
263 }
264
265 public Statement createStatement(int resultSetType,
266 int resultSetConcurrency) throws SQLException {
267 checkOpen();
268 try {
269 return new DelegatingStatement
270 (this, _conn.createStatement(resultSetType,resultSetConcurrency));
271 }
272 catch (SQLException e) {
273 handleException(e);
274 return null;
275 }
276 }
277
278 public PreparedStatement prepareStatement(String sql) throws SQLException {
279 checkOpen();
280 try {
281 return new DelegatingPreparedStatement
282 (this, _conn.prepareStatement(sql));
283 }
284 catch (SQLException e) {
285 handleException(e);
286 return null;
287 }
288 }
289
290 public PreparedStatement prepareStatement(String sql,
291 int resultSetType,
292 int resultSetConcurrency) throws SQLException {
293 checkOpen();
294 try {
295 return new DelegatingPreparedStatement
296 (this, _conn.prepareStatement
297 (sql,resultSetType,resultSetConcurrency));
298 }
299 catch (SQLException e) {
300 handleException(e);
301 return null;
302 }
303 }
304
305 public CallableStatement prepareCall(String sql) throws SQLException {
306 checkOpen();
307 try {
308 return new DelegatingCallableStatement(this, _conn.prepareCall(sql));
309 }
310 catch (SQLException e) {
311 handleException(e);
312 return null;
313 }
314 }
315
316 public CallableStatement prepareCall(String sql,
317 int resultSetType,
318 int resultSetConcurrency) throws SQLException {
319 checkOpen();
320 try {
321 return new DelegatingCallableStatement
322 (this, _conn.prepareCall(sql, resultSetType,resultSetConcurrency));
323 }
324 catch (SQLException e) {
325 handleException(e);
326 return null;
327 }
328 }
329
330 public void clearWarnings() throws SQLException
331 { checkOpen(); try { _conn.clearWarnings(); } catch (SQLException e) { handleException(e); } }
332
333 public void commit() throws SQLException
334 { checkOpen(); try { _conn.commit(); } catch (SQLException e) { handleException(e); } }
335
336 public boolean getAutoCommit() throws SQLException
337 { checkOpen(); try { return _conn.getAutoCommit(); } catch (SQLException e) { handleException(e); return false; }
338 }
339 public String getCatalog() throws SQLException
340 { checkOpen(); try { return _conn.getCatalog(); } catch (SQLException e) { handleException(e); return null; } }
341
342 public DatabaseMetaData getMetaData() throws SQLException {
343 checkOpen();
344 try {
345 return new DelegatingDatabaseMetaData(this, _conn.getMetaData());
346 } catch (SQLException e) {
347 handleException(e);
348 return null;
349 }
350 }
351
352 public int getTransactionIsolation() throws SQLException
353 { checkOpen(); try { return _conn.getTransactionIsolation(); } catch (SQLException e) { handleException(e); return -1; } }
354
355 public Map getTypeMap() throws SQLException
356 { checkOpen(); try { return _conn.getTypeMap(); } catch (SQLException e) { handleException(e); return null; } }
357
358 public SQLWarning getWarnings() throws SQLException
359 { checkOpen(); try { return _conn.getWarnings(); } catch (SQLException e) { handleException(e); return null; } }
360
361 public boolean isReadOnly() throws SQLException
362 { checkOpen(); try { return _conn.isReadOnly(); } catch (SQLException e) { handleException(e); return false; } }
363
364 public String nativeSQL(String sql) throws SQLException
365 { checkOpen(); try { return _conn.nativeSQL(sql); } catch (SQLException e) { handleException(e); return null; } }
366
367 public void rollback() throws SQLException
368 { checkOpen(); try { _conn.rollback(); } catch (SQLException e) { handleException(e); } }
369
370 public void setAutoCommit(boolean autoCommit) throws SQLException
371 { checkOpen(); try { _conn.setAutoCommit(autoCommit); } catch (SQLException e) { handleException(e); } }
372
373 public void setCatalog(String catalog) throws SQLException
374 { checkOpen(); try { _conn.setCatalog(catalog); } catch (SQLException e) { handleException(e); } }
375
376 public void setReadOnly(boolean readOnly) throws SQLException
377 { checkOpen(); try { _conn.setReadOnly(readOnly); } catch (SQLException e) { handleException(e); } }
378
379 public void setTransactionIsolation(int level) throws SQLException
380 { checkOpen(); try { _conn.setTransactionIsolation(level); } catch (SQLException e) { handleException(e); } }
381
382 public void setTypeMap(Map map) throws SQLException
383 { checkOpen(); try { _conn.setTypeMap(map); } catch (SQLException e) { handleException(e); } }
384
385 public boolean isClosed() throws SQLException {
386 return _closed || _conn.isClosed();
387 }
388
389 protected void checkOpen() throws SQLException {
390 if(_closed) {
391 if (null != _conn) {
392 String label = "";
393 try {
394 label = _conn.toString();
395 } catch (Exception ex) {
396 // ignore, leave label empty
397 }
398 throw new SQLException
399 ("Connection " + label + " is closed.");
400 } else {
401 throw new SQLException
402 ("Connection is null.");
403 }
404 }
405 }
406
407 protected void activate() {
408 _closed = false;
409 setLastUsed();
410 if(_conn instanceof DelegatingConnection) {
411 ((DelegatingConnection)_conn).activate();
412 }
413 }
414
415 protected void passivate() throws SQLException {
416 try {
417 // The JDBC spec requires that a Connection close any open
418 // Statement's when it is closed.
419 // DBCP-288. Not all the traced objects will be statements
420 List traces = getTrace();
421 if(traces != null) {
422 Iterator traceIter = traces.iterator();
423 while (traceIter.hasNext()) {
424 Object trace = traceIter.next();
425 if (trace instanceof Statement) {
426 ((Statement) trace).close();
427 } else if (trace instanceof ResultSet) {
428 // DBCP-265: Need to close the result sets that are
429 // generated via DatabaseMetaData
430 ((ResultSet) trace).close();
431 }
432 }
433 clearTrace();
434 }
435 setLastUsed(0);
436 if(_conn instanceof DelegatingConnection) {
437 ((DelegatingConnection)_conn).passivate();
438 }
439 }
440 finally {
441 _closed = true;
442 }
443 }
444
445 public int getHoldability() throws SQLException
446 { checkOpen(); try { return _conn.getHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
447
448 public void setHoldability(int holdability) throws SQLException
449 { checkOpen(); try { _conn.setHoldability(holdability); } catch (SQLException e) { handleException(e); } }
450
451 public java.sql.Savepoint setSavepoint() throws SQLException
452 { checkOpen(); try { return _conn.setSavepoint(); } catch (SQLException e) { handleException(e); return null; } }
453
454 public java.sql.Savepoint setSavepoint(String name) throws SQLException
455 { checkOpen(); try { return _conn.setSavepoint(name); } catch (SQLException e) { handleException(e); return null; } }
456
457 public void rollback(java.sql.Savepoint savepoint) throws SQLException
458 { checkOpen(); try { _conn.rollback(savepoint); } catch (SQLException e) { handleException(e); } }
459
460 public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException
461 { checkOpen(); try { _conn.releaseSavepoint(savepoint); } catch (SQLException e) { handleException(e); } }
462
463 public Statement createStatement(int resultSetType,
464 int resultSetConcurrency,
465 int resultSetHoldability) throws SQLException {
466 checkOpen();
467 try {
468 return new DelegatingStatement(this, _conn.createStatement(
469 resultSetType, resultSetConcurrency, resultSetHoldability));
470 }
471 catch (SQLException e) {
472 handleException(e);
473 return null;
474 }
475 }
476
477 public PreparedStatement prepareStatement(String sql, int resultSetType,
478 int resultSetConcurrency,
479 int resultSetHoldability) throws SQLException {
480 checkOpen();
481 try {
482 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
483 sql, resultSetType, resultSetConcurrency, resultSetHoldability));
484 }
485 catch (SQLException e) {
486 handleException(e);
487 return null;
488 }
489 }
490
491 public CallableStatement prepareCall(String sql, int resultSetType,
492 int resultSetConcurrency,
493 int resultSetHoldability) throws SQLException {
494 checkOpen();
495 try {
496 return new DelegatingCallableStatement(this, _conn.prepareCall(
497 sql, resultSetType, resultSetConcurrency, resultSetHoldability));
498 }
499 catch (SQLException e) {
500 handleException(e);
501 return null;
502 }
503 }
504
505 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
506 checkOpen();
507 try {
508 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
509 sql, autoGeneratedKeys));
510 }
511 catch (SQLException e) {
512 handleException(e);
513 return null;
514 }
515 }
516
517 public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException {
518 checkOpen();
519 try {
520 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
521 sql, columnIndexes));
522 }
523 catch (SQLException e) {
524 handleException(e);
525 return null;
526 }
527 }
528
529 public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException {
530 checkOpen();
531 try {
532 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
533 sql, columnNames));
534 }
535 catch (SQLException e) {
536 handleException(e);
537 return null;
538 }
539 }
540
541 /* JDBC_4_ANT_KEY_BEGIN */
542
543 public boolean isWrapperFor(Class<?> iface) throws SQLException {
544 return iface.isAssignableFrom(getClass()) || _conn.isWrapperFor(iface);
545 }
546
547 public <T> T unwrap(Class<T> iface) throws SQLException {
548 if (iface.isAssignableFrom(getClass())) {
549 return iface.cast(this);
550 } else if (iface.isAssignableFrom(_conn.getClass())) {
551 return iface.cast(_conn);
552 } else {
553 return _conn.unwrap(iface);
554 }
555 }
556
557 public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
558 checkOpen();
559 try {
560 return _conn.createArrayOf(typeName, elements);
561 }
562 catch (SQLException e) {
563 handleException(e);
564 return null;
565 }
566 }
567
568 public Blob createBlob() throws SQLException {
569 checkOpen();
570 try {
571 return _conn.createBlob();
572 }
573 catch (SQLException e) {
574 handleException(e);
575 return null;
576 }
577 }
578
579 public Clob createClob() throws SQLException {
580 checkOpen();
581 try {
582 return _conn.createClob();
583 }
584 catch (SQLException e) {
585 handleException(e);
586 return null;
587 }
588 }
589
590 public NClob createNClob() throws SQLException {
591 checkOpen();
592 try {
593 return _conn.createNClob();
594 }
595 catch (SQLException e) {
596 handleException(e);
597 return null;
598 }
599 }
600
601 public SQLXML createSQLXML() throws SQLException {
602 checkOpen();
603 try {
604 return _conn.createSQLXML();
605 }
606 catch (SQLException e) {
607 handleException(e);
608 return null;
609 }
610 }
611
612 public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
613 checkOpen();
614 try {
615 return _conn.createStruct(typeName, attributes);
616 }
617 catch (SQLException e) {
618 handleException(e);
619 return null;
620 }
621 }
622
623 public boolean isValid(int timeout) throws SQLException {
624 checkOpen();
625 try {
626 return _conn.isValid(timeout);
627 }
628 catch (SQLException e) {
629 handleException(e);
630 return false;
631 }
632 }
633
634 public void setClientInfo(String name, String value) throws SQLClientInfoException {
635 try {
636 checkOpen();
637 _conn.setClientInfo(name, value);
638 }
639 catch (SQLClientInfoException e) {
640 throw e;
641 }
642 catch (SQLException e) {
643 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
644 }
645 }
646
647 public void setClientInfo(Properties properties) throws SQLClientInfoException {
648 try {
649 checkOpen();
650 _conn.setClientInfo(properties);
651 }
652 catch (SQLClientInfoException e) {
653 throw e;
654 }
655 catch (SQLException e) {
656 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
657 }
658 }
659
660 public Properties getClientInfo() throws SQLException {
661 checkOpen();
662 try {
663 return _conn.getClientInfo();
664 }
665 catch (SQLException e) {
666 handleException(e);
667 return null;
668 }
669 }
670
671 public String getClientInfo(String name) throws SQLException {
672 checkOpen();
673 try {
674 return _conn.getClientInfo(name);
675 }
676 catch (SQLException e) {
677 handleException(e);
678 return null;
679 }
680 }
681 /* JDBC_4_ANT_KEY_END */
682 }