1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.dbcp;
19
20 import java.sql.CallableStatement;
21 import java.sql.Connection;
22 import java.sql.DatabaseMetaData;
23 import java.sql.PreparedStatement;
24 import java.sql.SQLException;
25 import java.sql.SQLWarning;
26 import java.sql.Statement;
27 import java.util.List;
28 import java.util.Map;
29
30 /**
31 * A base delegating implementation of {@link Connection}.
32 * <p>
33 * All of the methods from the {@link Connection} interface
34 * simply check to see that the {@link Connection} is active,
35 * and call the corresponding method on the "delegate"
36 * provided in my constructor.
37 * <p>
38 * Extends AbandonedTrace to implement Connection tracking and
39 * logging of code which created the Connection. Tracking the
40 * Connection ensures that the AbandonedObjectPool can close
41 * this connection and recycle it if its pool of connections
42 * is nearing exhaustion and this connection's last usage is
43 * older than the removeAbandonedTimeout.
44 *
45 * @author Rodney Waldhoff
46 * @author Glenn L. Nielsen
47 * @author James House
48 * @author Dirk Verbeeck
49 * @version $Revision: 500687 $ $Date: 2007-01-27 16:33:47 -0700 (Sat, 27 Jan 2007) $
50 */
51 public class DelegatingConnection extends AbandonedTrace
52 implements Connection {
53 /** My delegate {@link Connection}. */
54 protected Connection _conn = null;
55
56 protected boolean _closed = false;
57
58 /**
59 * Create a wrapper for the Connectin which traces this
60 * Connection in the AbandonedObjectPool.
61 *
62 * @param c the {@link Connection} to delegate all calls to.
63 */
64 public DelegatingConnection(Connection c) {
65 super();
66 _conn = c;
67 }
68
69 /**
70 * Create a wrapper for the Connection which traces
71 * the Statements created so that any unclosed Statements
72 * can be closed when this Connection is closed.
73 *
74 * @param c the {@link Connection} to delegate all calls to.
75 * @param config the configuration for tracing abandoned objects
76 * @deprecated AbandonedConfig is now deprecated.
77 */
78 public DelegatingConnection(Connection c, AbandonedConfig config) {
79 super(config);
80 _conn = c;
81 }
82
83 /**
84 * Returns a string representation of the metadata associated with
85 * the innnermost delegate connection.
86 *
87 * @since 1.2.2
88 */
89 public String toString() {
90 String s = null;
91
92 Connection c = this.getInnermostDelegate();
93 if (c != null) {
94 try {
95 if (c.isClosed()) {
96 s = "connection is closed";
97 }
98 else {
99 DatabaseMetaData meta = c.getMetaData();
100 if (meta != null) {
101 StringBuffer sb = new StringBuffer();
102 sb.append(meta.getURL());
103 sb.append(", UserName=");
104 sb.append(meta.getUserName());
105 sb.append(", ");
106 sb.append(meta.getDriverName());
107 s = sb.toString();
108 }
109 }
110 }
111 catch (SQLException ex) {
112 s = null;
113 }
114 }
115
116 if (s == null) {
117 s = super.toString();
118 }
119
120 return s;
121 }
122
123 /**
124 * Returns my underlying {@link Connection}.
125 * @return my underlying {@link Connection}.
126 */
127 public Connection getDelegate() {
128 return _conn;
129 }
130
131 /**
132 * Compares innermost delegate to the given connection.
133 *
134 * @param c connection to compare innermost delegate with
135 * @return true if innermost delegate equals <code>c</code>
136 * @since 1.2.2
137 */
138 public boolean innermostDelegateEquals(Connection c) {
139 Connection innerCon = getInnermostDelegate();
140 if (innerCon == null) {
141 return c == null;
142 } else {
143 return innerCon.equals(c);
144 }
145 }
146
147 public boolean equals(Object obj) {
148 if (obj == null) {
149 return false;
150 }
151 if (obj == this) {
152 return true;
153 }
154 Connection delegate = getInnermostDelegate();
155 if (delegate == null) {
156 return false;
157 }
158 if (obj instanceof DelegatingConnection) {
159 DelegatingConnection c = (DelegatingConnection) obj;
160 return c.innermostDelegateEquals(delegate);
161 }
162 else {
163 return delegate.equals(obj);
164 }
165 }
166
167 public int hashCode() {
168 Object obj = getInnermostDelegate();
169 if (obj == null) {
170 return 0;
171 }
172 return obj.hashCode();
173 }
174
175
176 /**
177 * If my underlying {@link Connection} is not a
178 * <tt>DelegatingConnection</tt>, returns it,
179 * otherwise recursively invokes this method on
180 * my delegate.
181 * <p>
182 * Hence this method will return the first
183 * delegate that is not a <tt>DelegatingConnection</tt>,
184 * or <tt>null</tt> when no non-<tt>DelegatingConnection</tt>
185 * delegate can be found by transversing this chain.
186 * <p>
187 * This method is useful when you may have nested
188 * <tt>DelegatingConnection</tt>s, and you want to make
189 * sure to obtain a "genuine" {@link Connection}.
190 */
191 public Connection getInnermostDelegate() {
192 Connection c = _conn;
193 while(c != null && c instanceof DelegatingConnection) {
194 c = ((DelegatingConnection)c).getDelegate();
195 if(this == c) {
196 return null;
197 }
198 }
199 return c;
200 }
201
202 /** Sets my delegate. */
203 public void setDelegate(Connection c) {
204 _conn = c;
205 }
206
207 /**
208 * Closes the underlying connection, and close
209 * any Statements that were not explicitly closed.
210 */
211 public void close() throws SQLException
212 {
213 passivate();
214 _conn.close();
215 }
216
217 protected void handleException(SQLException e) throws SQLException {
218 throw e;
219 }
220
221 public Statement createStatement() throws SQLException {
222 checkOpen();
223 try {
224 return new DelegatingStatement(this, _conn.createStatement());
225 }
226 catch (SQLException e) {
227 handleException(e);
228 return null;
229 }
230 }
231
232 public Statement createStatement(int resultSetType,
233 int resultSetConcurrency) throws SQLException {
234 checkOpen();
235 try {
236 return new DelegatingStatement
237 (this, _conn.createStatement(resultSetType,resultSetConcurrency));
238 }
239 catch (SQLException e) {
240 handleException(e);
241 return null;
242 }
243 }
244
245 public PreparedStatement prepareStatement(String sql) throws SQLException {
246 checkOpen();
247 try {
248 return new DelegatingPreparedStatement
249 (this, _conn.prepareStatement(sql));
250 }
251 catch (SQLException e) {
252 handleException(e);
253 return null;
254 }
255 }
256
257 public PreparedStatement prepareStatement(String sql,
258 int resultSetType,
259 int resultSetConcurrency) throws SQLException {
260 checkOpen();
261 try {
262 return new DelegatingPreparedStatement
263 (this, _conn.prepareStatement
264 (sql,resultSetType,resultSetConcurrency));
265 }
266 catch (SQLException e) {
267 handleException(e);
268 return null;
269 }
270 }
271
272 public CallableStatement prepareCall(String sql) throws SQLException {
273 checkOpen();
274 try {
275 return new DelegatingCallableStatement(this, _conn.prepareCall(sql));
276 }
277 catch (SQLException e) {
278 handleException(e);
279 return null;
280 }
281 }
282
283 public CallableStatement prepareCall(String sql,
284 int resultSetType,
285 int resultSetConcurrency) throws SQLException {
286 checkOpen();
287 try {
288 return new DelegatingCallableStatement
289 (this, _conn.prepareCall(sql, resultSetType,resultSetConcurrency));
290 }
291 catch (SQLException e) {
292 handleException(e);
293 return null;
294 }
295 }
296
297 public void clearWarnings() throws SQLException
298 { checkOpen(); try { _conn.clearWarnings(); } catch (SQLException e) { handleException(e); } }
299
300 public void commit() throws SQLException
301 { checkOpen(); try { _conn.commit(); } catch (SQLException e) { handleException(e); } }
302
303 public boolean getAutoCommit() throws SQLException
304 { checkOpen(); try { return _conn.getAutoCommit(); } catch (SQLException e) { handleException(e); return false; }
305 }
306 public String getCatalog() throws SQLException
307 { checkOpen(); try { return _conn.getCatalog(); } catch (SQLException e) { handleException(e); return null; } }
308
309 public DatabaseMetaData getMetaData() throws SQLException
310 { checkOpen(); try { return _conn.getMetaData(); } catch (SQLException e) { handleException(e); return null; } }
311
312 public int getTransactionIsolation() throws SQLException
313 { checkOpen(); try { return _conn.getTransactionIsolation(); } catch (SQLException e) { handleException(e); return -1; } }
314
315 public Map getTypeMap() throws SQLException
316 { checkOpen(); try { return _conn.getTypeMap(); } catch (SQLException e) { handleException(e); return null; } }
317
318 public SQLWarning getWarnings() throws SQLException
319 { checkOpen(); try { return _conn.getWarnings(); } catch (SQLException e) { handleException(e); return null; } }
320
321 public boolean isReadOnly() throws SQLException
322 { checkOpen(); try { return _conn.isReadOnly(); } catch (SQLException e) { handleException(e); return false; } }
323
324 public String nativeSQL(String sql) throws SQLException
325 { checkOpen(); try { return _conn.nativeSQL(sql); } catch (SQLException e) { handleException(e); return null; } }
326
327 public void rollback() throws SQLException
328 { checkOpen(); try { _conn.rollback(); } catch (SQLException e) { handleException(e); } }
329
330 public void setAutoCommit(boolean autoCommit) throws SQLException
331 { checkOpen(); try { _conn.setAutoCommit(autoCommit); } catch (SQLException e) { handleException(e); } }
332
333 public void setCatalog(String catalog) throws SQLException
334 { checkOpen(); try { _conn.setCatalog(catalog); } catch (SQLException e) { handleException(e); } }
335
336 public void setReadOnly(boolean readOnly) throws SQLException
337 { checkOpen(); try { _conn.setReadOnly(readOnly); } catch (SQLException e) { handleException(e); } }
338
339 public void setTransactionIsolation(int level) throws SQLException
340 { checkOpen(); try { _conn.setTransactionIsolation(level); } catch (SQLException e) { handleException(e); } }
341
342 public void setTypeMap(Map map) throws SQLException
343 { checkOpen(); try { _conn.setTypeMap(map); } catch (SQLException e) { handleException(e); } }
344
345 public boolean isClosed() throws SQLException {
346 if(_closed || _conn.isClosed()) {
347 return true;
348 }
349 return false;
350 }
351
352 protected void checkOpen() throws SQLException {
353 if(_closed) {
354 throw new SQLException
355 ("Connection " + _conn + " is closed.");
356 }
357 }
358
359 protected void activate() {
360 _closed = false;
361 setLastUsed();
362 if(_conn instanceof DelegatingConnection) {
363 ((DelegatingConnection)_conn).activate();
364 }
365 }
366
367 protected void passivate() throws SQLException {
368 try {
369 // The JDBC spec requires that a Connection close any open
370 // Statement's when it is closed.
371 List statements = getTrace();
372 if( statements != null) {
373 Statement[] set = new Statement[statements.size()];
374 statements.toArray(set);
375 for (int i = 0; i < set.length; i++) {
376 set[i].close();
377 }
378 clearTrace();
379 }
380 setLastUsed(0);
381 if(_conn instanceof DelegatingConnection) {
382 ((DelegatingConnection)_conn).passivate();
383 }
384 }
385 finally {
386 _closed = true;
387 }
388 }
389
390 // ------------------- JDBC 3.0 -----------------------------------------
391 // Will be commented by the build process on a JDBC 2.0 system
392
393 /* JDBC_3_ANT_KEY_BEGIN */
394
395 public int getHoldability() throws SQLException
396 { checkOpen(); try { return _conn.getHoldability(); } catch (SQLException e) { handleException(e); return 0; } }
397
398 public void setHoldability(int holdability) throws SQLException
399 { checkOpen(); try { _conn.setHoldability(holdability); } catch (SQLException e) { handleException(e); } }
400
401 public java.sql.Savepoint setSavepoint() throws SQLException
402 { checkOpen(); try { return _conn.setSavepoint(); } catch (SQLException e) { handleException(e); return null; } }
403
404 public java.sql.Savepoint setSavepoint(String name) throws SQLException
405 { checkOpen(); try { return _conn.setSavepoint(name); } catch (SQLException e) { handleException(e); return null; } }
406
407 public void rollback(java.sql.Savepoint savepoint) throws SQLException
408 { checkOpen(); try { _conn.rollback(savepoint); } catch (SQLException e) { handleException(e); } }
409
410 public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException
411 { checkOpen(); try { _conn.releaseSavepoint(savepoint); } catch (SQLException e) { handleException(e); } }
412
413 public Statement createStatement(int resultSetType,
414 int resultSetConcurrency,
415 int resultSetHoldability) throws SQLException {
416 checkOpen();
417 try {
418 return new DelegatingStatement(this, _conn.createStatement(
419 resultSetType, resultSetConcurrency, resultSetHoldability));
420 }
421 catch (SQLException e) {
422 handleException(e);
423 return null;
424 }
425 }
426
427 public PreparedStatement prepareStatement(String sql, int resultSetType,
428 int resultSetConcurrency,
429 int resultSetHoldability) throws SQLException {
430 checkOpen();
431 try {
432 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
433 sql, resultSetType, resultSetConcurrency, resultSetHoldability));
434 }
435 catch (SQLException e) {
436 handleException(e);
437 return null;
438 }
439 }
440
441 public CallableStatement prepareCall(String sql, int resultSetType,
442 int resultSetConcurrency,
443 int resultSetHoldability) throws SQLException {
444 checkOpen();
445 try {
446 return new DelegatingCallableStatement(this, _conn.prepareCall(
447 sql, resultSetType, resultSetConcurrency, resultSetHoldability));
448 }
449 catch (SQLException e) {
450 handleException(e);
451 return null;
452 }
453 }
454
455 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
456 checkOpen();
457 try {
458 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
459 sql, autoGeneratedKeys));
460 }
461 catch (SQLException e) {
462 handleException(e);
463 return null;
464 }
465 }
466
467 public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException {
468 checkOpen();
469 try {
470 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
471 sql, columnIndexes));
472 }
473 catch (SQLException e) {
474 handleException(e);
475 return null;
476 }
477 }
478
479 public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException {
480 checkOpen();
481 try {
482 return new DelegatingPreparedStatement(this, _conn.prepareStatement(
483 sql, columnNames));
484 }
485 catch (SQLException e) {
486 handleException(e);
487 return null;
488 }
489 }
490 /* JDBC_3_ANT_KEY_END */
491 }