1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.dbcp2.cpdsadapter;
18
19 import java.sql.CallableStatement;
20 import java.sql.Connection;
21 import java.sql.PreparedStatement;
22 import java.sql.SQLException;
23 import java.sql.Statement;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27
28 import javax.sql.ConnectionEvent;
29 import javax.sql.ConnectionEventListener;
30 import javax.sql.PooledConnection;
31 import javax.sql.StatementEventListener;
32
33 import org.apache.commons.dbcp2.DelegatingConnection;
34 import org.apache.commons.dbcp2.DelegatingPreparedStatement;
35 import org.apache.commons.dbcp2.Jdbc41Bridge;
36 import org.apache.commons.dbcp2.PStmtKey;
37 import org.apache.commons.dbcp2.PoolableCallableStatement;
38 import org.apache.commons.dbcp2.PoolablePreparedStatement;
39 import org.apache.commons.dbcp2.PoolingConnection.StatementType;
40 import org.apache.commons.dbcp2.Utils;
41 import org.apache.commons.pool2.KeyedObjectPool;
42 import org.apache.commons.pool2.KeyedPooledObjectFactory;
43 import org.apache.commons.pool2.PooledObject;
44 import org.apache.commons.pool2.impl.DefaultPooledObject;
45
46
47
48
49
50
51 final class PooledConnectionImpl
52 implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
53
54 private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";
55
56
57
58
59 private Connection connection;
60
61
62
63
64 private final DelegatingConnection<?> delegatingConnection;
65
66
67
68
69 private Connection logicalConnection;
70
71
72
73
74 private final List<ConnectionEventListener> eventListeners;
75
76
77
78
79 private final List<StatementEventListener> statementEventListeners = Collections.synchronizedList(new ArrayList<>());
80
81
82
83
84 private boolean closed;
85
86
87 private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;
88
89
90
91
92 private boolean accessToUnderlyingConnectionAllowed;
93
94
95
96
97
98
99
100 PooledConnectionImpl(final Connection connection) {
101 this.connection = connection;
102 if (connection instanceof DelegatingConnection) {
103 this.delegatingConnection = (DelegatingConnection<?>) connection;
104 } else {
105 this.delegatingConnection = new DelegatingConnection<>(connection);
106 }
107 eventListeners = Collections.synchronizedList(new ArrayList<>());
108 closed = false;
109 }
110
111
112
113
114
115
116 @Override
117 public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
118 throws SQLException {
119 pooledObject.getObject().activate();
120 }
121
122
123
124
125 @Override
126 public void addConnectionEventListener(final ConnectionEventListener listener) {
127 if (!eventListeners.contains(listener)) {
128 eventListeners.add(listener);
129 }
130 }
131
132 @Override
133 public void addStatementEventListener(final StatementEventListener listener) {
134 if (!statementEventListeners.contains(listener)) {
135 statementEventListeners.add(listener);
136 }
137 }
138
139
140
141
142 private void assertOpen() throws SQLException {
143 if (closed || connection == null) {
144 throw new SQLException(CLOSED);
145 }
146 }
147
148
149
150
151
152
153
154
155 @Override
156 public void close() throws SQLException {
157 assertOpen();
158 closed = true;
159 try {
160 if (pStmtPool != null) {
161 try {
162 pStmtPool.close();
163 } finally {
164 pStmtPool = null;
165 }
166 }
167 } catch (final RuntimeException e) {
168 throw e;
169 } catch (final Exception e) {
170 throw new SQLException("Cannot close connection (return to pool failed)", e);
171 } finally {
172 try {
173 connection.close();
174 } finally {
175 connection = null;
176 }
177 }
178 }
179
180
181
182
183
184
185
186
187 protected PStmtKey createKey(final String sql) {
188 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull());
189 }
190
191
192
193
194
195
196
197
198
199
200
201 protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
202 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
203 }
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
219 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) {
239 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, resultSetHoldability);
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability,
262 final StatementType statementType) {
263 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, resultSetHoldability, statementType);
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final StatementType statementType) {
283 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, statementType);
284 }
285
286
287
288
289
290
291
292
293
294
295
296 protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
297 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
298 }
299
300
301
302
303
304
305
306
307
308
309 protected PStmtKey createKey(final String sql, final StatementType statementType) {
310 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), statementType);
311 }
312
313
314
315
316
317
318
319
320
321
322 protected PStmtKey createKey(final String sql, final String[] columnNames) {
323 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), columnNames);
324 }
325
326
327
328
329
330
331
332
333
334 @Override
335 public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) throws SQLException {
336 if (pooledObject != null) {
337 @SuppressWarnings("resource")
338 final DelegatingPreparedStatement object = pooledObject.getObject();
339 if (object != null) {
340 @SuppressWarnings("resource")
341 final Statement innermostDelegate = object.getInnermostDelegate();
342 if (innermostDelegate != null) {
343 innermostDelegate.close();
344 }
345 }
346 }
347 }
348
349
350
351
352 @Override
353 protected void finalize() throws Throwable {
354
355
356 Utils.close(connection, null);
357
358 if (logicalConnection != null && !logicalConnection.isClosed()) {
359 throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed.");
360 }
361 }
362
363 private String getCatalogOrNull() {
364 try {
365 return connection == null ? null : connection.getCatalog();
366 } catch (final SQLException e) {
367 return null;
368 }
369 }
370
371
372
373
374
375
376
377
378 @Override
379 public Connection getConnection() throws SQLException {
380 assertOpen();
381
382 if (logicalConnection != null && !logicalConnection.isClosed()) {
383
384
385 throw new SQLException("PooledConnection was reused, without its previous Connection being closed.");
386 }
387
388
389 logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed());
390 return logicalConnection;
391 }
392
393 private Connection getRawConnection() throws SQLException {
394 assertOpen();
395 return connection;
396 }
397
398 private String getSchemaOrNull() {
399 try {
400 return connection == null ? null : Jdbc41Bridge.getSchema(connection);
401 } catch (final SQLException e) {
402 return null;
403 }
404 }
405
406
407
408
409
410
411 public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
412 return this.accessToUnderlyingConnectionAllowed;
413 }
414
415
416
417
418
419
420
421 @SuppressWarnings("resource")
422 @Override
423 public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws SQLException {
424 if (null == key) {
425 throw new IllegalArgumentException("Prepared statement key is null or invalid.");
426 }
427 if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
428 final PreparedStatement statement = (PreparedStatement) key.createStatement(connection);
429 @SuppressWarnings({"rawtypes", "unchecked" })
430 final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool,
431 delegatingConnection);
432 return new DefaultPooledObject<>(pps);
433 }
434 final CallableStatement statement = (CallableStatement) key.createStatement(connection);
435 @SuppressWarnings("unchecked")
436 final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool,
437 (DelegatingConnection<Connection>) delegatingConnection);
438 return new DefaultPooledObject<>(pcs);
439 }
440
441
442
443
444 void notifyListeners() {
445 final ConnectionEvent event = new ConnectionEvent(this);
446 new ArrayList<>(eventListeners).forEach(listener -> listener.connectionClosed(event));
447 }
448
449
450
451
452
453
454
455
456
457
458 @Override
459 public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
460 throws SQLException {
461 @SuppressWarnings("resource")
462 final DelegatingPreparedStatement dps = pooledObject.getObject();
463 dps.clearParameters();
464 dps.passivate();
465 }
466
467
468
469
470
471
472
473
474
475
476
477
478 @SuppressWarnings("resource")
479 CallableStatement prepareCall(final String sql) throws SQLException {
480 if (pStmtPool == null) {
481 return getRawConnection().prepareCall(sql);
482 }
483 try {
484 return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
485 } catch (final RuntimeException e) {
486 throw e;
487 } catch (final Exception e) {
488 throw new SQLException("Borrow prepareCall from pool failed", e);
489 }
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511 @SuppressWarnings("resource")
512 CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
513 throws SQLException {
514 if (pStmtPool == null) {
515 return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
516 }
517 try {
518 return (CallableStatement) pStmtPool.borrowObject(
519 createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
520 } catch (final RuntimeException e) {
521 throw e;
522 } catch (final Exception e) {
523 throw new SQLException("Borrow prepareCall from pool failed", e);
524 }
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549 @SuppressWarnings("resource")
550 CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
551 final int resultSetHoldability) throws SQLException {
552 if (pStmtPool == null) {
553 return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
554 }
555 try {
556 return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
557 resultSetHoldability, StatementType.CALLABLE_STATEMENT));
558 } catch (final RuntimeException e) {
559 throw e;
560 } catch (final Exception e) {
561 throw new SQLException("Borrow prepareCall from pool failed", e);
562 }
563 }
564
565
566
567
568
569
570
571
572
573 @SuppressWarnings("resource")
574 PreparedStatement prepareStatement(final String sql) throws SQLException {
575 if (pStmtPool == null) {
576 return getRawConnection().prepareStatement(sql);
577 }
578 try {
579 return pStmtPool.borrowObject(createKey(sql));
580 } catch (final RuntimeException e) {
581 throw e;
582 } catch (final Exception e) {
583 throw new SQLException("Borrow prepareStatement from pool failed", e);
584 }
585 }
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600 @SuppressWarnings("resource")
601 PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
602 if (pStmtPool == null) {
603 return getRawConnection().prepareStatement(sql, autoGeneratedKeys);
604 }
605 try {
606 return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
607 } catch (final RuntimeException e) {
608 throw e;
609 } catch (final Exception e) {
610 throw new SQLException("Borrow prepareStatement from pool failed", e);
611 }
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632 @SuppressWarnings("resource")
633 PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
634 throws SQLException {
635 if (pStmtPool == null) {
636 return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency);
637 }
638 try {
639 return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
640 } catch (final RuntimeException e) {
641 throw e;
642 } catch (final Exception e) {
643 throw new SQLException("Borrow prepareStatement from pool failed", e);
644 }
645 }
646
647 @SuppressWarnings("resource")
648 PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
649 final int resultSetHoldability) throws SQLException {
650 if (pStmtPool == null) {
651 return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
652 }
653 try {
654 return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
655 } catch (final RuntimeException e) {
656 throw e;
657 } catch (final Exception e) {
658 throw new SQLException("Borrow prepareStatement from pool failed", e);
659 }
660 }
661
662 @SuppressWarnings("resource")
663 PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
664 if (pStmtPool == null) {
665 return getRawConnection().prepareStatement(sql, columnIndexes);
666 }
667 try {
668 return pStmtPool.borrowObject(createKey(sql, columnIndexes));
669 } catch (final RuntimeException e) {
670 throw e;
671 } catch (final Exception e) {
672 throw new SQLException("Borrow prepareStatement from pool failed", e);
673 }
674 }
675
676 @SuppressWarnings("resource")
677 PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
678 if (pStmtPool == null) {
679 return getRawConnection().prepareStatement(sql, columnNames);
680 }
681 try {
682 return pStmtPool.borrowObject(createKey(sql, columnNames));
683 } catch (final RuntimeException e) {
684 throw e;
685 } catch (final Exception e) {
686 throw new SQLException("Borrow prepareStatement from pool failed", e);
687 }
688 }
689
690
691
692
693 @Override
694 public void removeConnectionEventListener(final ConnectionEventListener listener) {
695 eventListeners.remove(listener);
696 }
697
698 @Override
699 public void removeStatementEventListener(final StatementEventListener listener) {
700 statementEventListeners.remove(listener);
701 }
702
703
704
705
706
707
708
709
710 public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
711 this.accessToUnderlyingConnectionAllowed = allow;
712 }
713
714 public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) {
715 pStmtPool = statementPool;
716 }
717
718
719
720
721 @Override
722 public synchronized String toString() {
723 final StringBuilder builder = new StringBuilder(super.toString());
724 builder.append("[connection=");
725 builder.append(connection);
726 builder.append(", delegatingConnection=");
727 builder.append(delegatingConnection);
728 builder.append(", logicalConnection=");
729 builder.append(logicalConnection);
730 builder.append(", eventListeners=");
731 builder.append(eventListeners);
732 builder.append(", statementEventListeners=");
733 builder.append(statementEventListeners);
734 builder.append(", closed=");
735 builder.append(closed);
736 builder.append(", pStmtPool=");
737 builder.append(pStmtPool);
738 builder.append(", accessToUnderlyingConnectionAllowed=");
739 builder.append(accessToUnderlyingConnectionAllowed);
740 builder.append("]");
741 return builder.toString();
742 }
743
744
745
746
747
748
749
750
751
752
753 @Override
754 public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
755 return true;
756 }
757 }