1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.dbcp2;
18
19 import java.sql.Array;
20 import java.sql.Blob;
21 import java.sql.CallableStatement;
22 import java.sql.ClientInfoStatus;
23 import java.sql.Clob;
24 import java.sql.Connection;
25 import java.sql.DatabaseMetaData;
26 import java.sql.NClob;
27 import java.sql.PreparedStatement;
28 import java.sql.SQLClientInfoException;
29 import java.sql.SQLException;
30 import java.sql.SQLWarning;
31 import java.sql.SQLXML;
32 import java.sql.Savepoint;
33 import java.sql.Statement;
34 import java.sql.Struct;
35 import java.time.Duration;
36 import java.time.Instant;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Properties;
42 import java.util.concurrent.Executor;
43
44 import org.apache.commons.dbcp2.managed.ManagedConnection;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class DelegatingConnection<C extends Connection> extends AbandonedTrace implements Connection {
64
65 private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES = Collections
66 .<String, ClientInfoStatus>emptyMap();
67
68
69 private volatile C connection;
70
71 private volatile boolean closed;
72 private volatile boolean cacheState = true;
73 private Boolean cachedAutoCommit;
74 private Boolean cachedReadOnly;
75 private String cachedCatalog;
76 private String cachedSchema;
77 private Duration defaultQueryTimeoutDuration;
78
79
80
81
82
83
84 public DelegatingConnection(final C connection) {
85 this.connection = connection;
86 }
87
88 @Override
89 public void abort(final Executor executor) throws SQLException {
90 try {
91 Jdbc41Bridge.abort(connection, executor);
92 } catch (final SQLException e) {
93 handleException(e);
94 }
95 }
96
97
98
99
100 protected void activate() {
101 closed = false;
102 setLastUsed();
103 if (connection instanceof DelegatingConnection) {
104 ((DelegatingConnection<?>) connection).activate();
105 }
106 }
107
108
109
110
111
112
113 protected void checkOpen() throws SQLException {
114 if (closed) {
115 if (null != connection) {
116 String label;
117 try {
118 label = connection.toString();
119 } catch (final Exception e) {
120
121 label = "";
122 }
123 throw new SQLException("Connection " + label + " is closed.");
124 }
125 throw new SQLException("Connection is null.");
126 }
127 }
128
129
130
131
132
133 public void clearCachedState() {
134 cachedAutoCommit = null;
135 cachedReadOnly = null;
136 cachedSchema = null;
137 cachedCatalog = null;
138 if (connection instanceof DelegatingConnection) {
139 ((DelegatingConnection<?>) connection).clearCachedState();
140 }
141 }
142
143 @Override
144 public void clearWarnings() throws SQLException {
145 checkOpen();
146 try {
147 connection.clearWarnings();
148 } catch (final SQLException e) {
149 handleException(e);
150 }
151 }
152
153
154
155
156
157
158
159
160
161
162 @Override
163 public void close() throws SQLException {
164 if (!closed) {
165 closeInternal();
166 }
167 }
168
169
170
171
172
173
174 protected final void closeInternal() throws SQLException {
175 try {
176 passivate();
177 } finally {
178 if (connection != null) {
179 boolean connectionIsClosed;
180 try {
181 connectionIsClosed = connection.isClosed();
182 } catch (final SQLException e) {
183
184 connectionIsClosed = false;
185 }
186 try {
187
188
189
190 if (!connectionIsClosed) {
191 connection.close();
192 }
193 } finally {
194 closed = true;
195 }
196 } else {
197 closed = true;
198 }
199 }
200 }
201
202 @Override
203 public void commit() throws SQLException {
204 checkOpen();
205 try {
206 connection.commit();
207 } catch (final SQLException e) {
208 handleException(e);
209 }
210 }
211
212 @Override
213 public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
214 checkOpen();
215 try {
216 return connection.createArrayOf(typeName, elements);
217 } catch (final SQLException e) {
218 handleException(e);
219 return null;
220 }
221 }
222
223 @Override
224 public Blob createBlob() throws SQLException {
225 checkOpen();
226 try {
227 return connection.createBlob();
228 } catch (final SQLException e) {
229 handleException(e);
230 return null;
231 }
232 }
233
234 @Override
235 public Clob createClob() throws SQLException {
236 checkOpen();
237 try {
238 return connection.createClob();
239 } catch (final SQLException e) {
240 handleException(e);
241 return null;
242 }
243 }
244
245 @Override
246 public NClob createNClob() throws SQLException {
247 checkOpen();
248 try {
249 return connection.createNClob();
250 } catch (final SQLException e) {
251 handleException(e);
252 return null;
253 }
254 }
255
256 @Override
257 public SQLXML createSQLXML() throws SQLException {
258 checkOpen();
259 try {
260 return connection.createSQLXML();
261 } catch (final SQLException e) {
262 handleException(e);
263 return null;
264 }
265 }
266
267 @SuppressWarnings("resource")
268 @Override
269 public Statement createStatement() throws SQLException {
270 checkOpen();
271 try {
272 return init(new DelegatingStatement(this, connection.createStatement()));
273 } catch (final SQLException e) {
274 handleException(e);
275 return null;
276 }
277 }
278
279 @SuppressWarnings("resource")
280 @Override
281 public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
282 checkOpen();
283 try {
284 return init(new DelegatingStatement(this, connection.createStatement(resultSetType, resultSetConcurrency)));
285 } catch (final SQLException e) {
286 handleException(e);
287 return null;
288 }
289 }
290
291 @SuppressWarnings("resource")
292 @Override
293 public Statement createStatement(final int resultSetType, final int resultSetConcurrency,
294 final int resultSetHoldability) throws SQLException {
295 checkOpen();
296 try {
297 return init(new DelegatingStatement(this,
298 connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)));
299 } catch (final SQLException e) {
300 handleException(e);
301 return null;
302 }
303 }
304
305 @Override
306 public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
307 checkOpen();
308 try {
309 return connection.createStruct(typeName, attributes);
310 } catch (final SQLException e) {
311 handleException(e);
312 return null;
313 }
314 }
315
316 @Override
317 public boolean getAutoCommit() throws SQLException {
318 checkOpen();
319 if (cacheState && cachedAutoCommit != null) {
320 return cachedAutoCommit;
321 }
322 try {
323 cachedAutoCommit = connection.getAutoCommit();
324 return cachedAutoCommit;
325 } catch (final SQLException e) {
326 handleException(e);
327 return false;
328 }
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342 public boolean getCacheState() {
343 return cacheState;
344 }
345
346 @Override
347 public String getCatalog() throws SQLException {
348 checkOpen();
349 if (cacheState && cachedCatalog != null) {
350 return cachedCatalog;
351 }
352 try {
353 cachedCatalog = connection.getCatalog();
354 return cachedCatalog;
355 } catch (final SQLException e) {
356 handleException(e);
357 return null;
358 }
359 }
360
361 @Override
362 public Properties getClientInfo() throws SQLException {
363 checkOpen();
364 try {
365 return connection.getClientInfo();
366 } catch (final SQLException e) {
367 handleException(e);
368 return null;
369 }
370 }
371
372 @Override
373 public String getClientInfo(final String name) throws SQLException {
374 checkOpen();
375 try {
376 return connection.getClientInfo(name);
377 } catch (final SQLException e) {
378 handleException(e);
379 return null;
380 }
381 }
382
383
384
385
386
387
388
389
390 @Deprecated
391 public Integer getDefaultQueryTimeout() {
392 return defaultQueryTimeoutDuration == null ? null : (int) defaultQueryTimeoutDuration.getSeconds();
393 }
394
395
396
397
398
399
400
401
402 public Duration getDefaultQueryTimeoutDuration() {
403 return defaultQueryTimeoutDuration;
404 }
405
406
407
408
409
410
411 public C getDelegate() {
412 return getDelegateInternal();
413 }
414
415
416
417
418
419
420 protected final C getDelegateInternal() {
421 return connection;
422 }
423
424 @Override
425 public int getHoldability() throws SQLException {
426 checkOpen();
427 try {
428 return connection.getHoldability();
429 } catch (final SQLException e) {
430 handleException(e);
431 return 0;
432 }
433 }
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449 public Connection getInnermostDelegate() {
450 return getInnermostDelegateInternal();
451 }
452
453
454
455
456
457
458
459 @SuppressWarnings("resource")
460 public final Connection getInnermostDelegateInternal() {
461 Connection conn = connection;
462 while (conn instanceof DelegatingConnection) {
463 conn = ((DelegatingConnection<?>) conn).getDelegateInternal();
464 if (this == conn) {
465 return null;
466 }
467 }
468 return conn;
469 }
470
471 @Override
472 public DatabaseMetaData getMetaData() throws SQLException {
473 checkOpen();
474 try {
475 return new DelegatingDatabaseMetaData(this, connection.getMetaData());
476 } catch (final SQLException e) {
477 handleException(e);
478 return null;
479 }
480 }
481
482 @Override
483 public int getNetworkTimeout() throws SQLException {
484 checkOpen();
485 try {
486 return Jdbc41Bridge.getNetworkTimeout(connection);
487 } catch (final SQLException e) {
488 handleException(e);
489 return 0;
490 }
491 }
492
493 @Override
494 public String getSchema() throws SQLException {
495 checkOpen();
496 if (cacheState && cachedSchema != null) {
497 return cachedSchema;
498 }
499 try {
500 cachedSchema = Jdbc41Bridge.getSchema(connection);
501 return cachedSchema;
502 } catch (final SQLException e) {
503 handleException(e);
504 return null;
505 }
506 }
507
508 @Override
509 public int getTransactionIsolation() throws SQLException {
510 checkOpen();
511 try {
512 return connection.getTransactionIsolation();
513 } catch (final SQLException e) {
514 handleException(e);
515 return -1;
516 }
517 }
518
519 @Override
520 public Map<String, Class<?>> getTypeMap() throws SQLException {
521 checkOpen();
522 try {
523 return connection.getTypeMap();
524 } catch (final SQLException e) {
525 handleException(e);
526 return null;
527 }
528 }
529
530 @Override
531 public SQLWarning getWarnings() throws SQLException {
532 checkOpen();
533 try {
534 return connection.getWarnings();
535 } catch (final SQLException e) {
536 handleException(e);
537 return null;
538 }
539 }
540
541
542
543
544
545
546
547 protected void handleException(final SQLException e) throws SQLException {
548 throw e;
549 }
550
551
552
553
554
555
556
557
558
559 protected <T extends Throwable> T handleExceptionNoThrow(final T e) {
560 return e;
561 }
562
563
564
565
566
567
568
569
570
571 private <T extends DelegatingStatement> T init(final T delegatingStatement) throws SQLException {
572 if (defaultQueryTimeoutDuration != null && defaultQueryTimeoutDuration.getSeconds() != delegatingStatement.getQueryTimeout()) {
573 delegatingStatement.setQueryTimeout((int) defaultQueryTimeoutDuration.getSeconds());
574 }
575 return delegatingStatement;
576 }
577
578
579
580
581
582
583
584
585 @SuppressWarnings("resource")
586 public boolean innermostDelegateEquals(final Connection c) {
587 final Connection innerCon = getInnermostDelegateInternal();
588 if (innerCon == null) {
589 return c == null;
590 }
591 return innerCon.equals(c);
592 }
593
594 @Override
595 public boolean isClosed() throws SQLException {
596 return closed || connection == null || connection.isClosed();
597 }
598
599
600
601
602
603
604 protected boolean isClosedInternal() {
605 return closed;
606 }
607
608 @Override
609 public boolean isReadOnly() throws SQLException {
610 checkOpen();
611 if (cacheState && cachedReadOnly != null) {
612 return cachedReadOnly;
613 }
614 try {
615 cachedReadOnly = connection.isReadOnly();
616 return cachedReadOnly;
617 } catch (final SQLException e) {
618 handleException(e);
619 return false;
620 }
621 }
622
623
624
625
626
627
628
629
630
631
632 public boolean isValid(final Duration timeout) throws SQLException {
633 if (isClosed()) {
634 return false;
635 }
636 try {
637 return connection.isValid((int) timeout.getSeconds());
638 } catch (final SQLException e) {
639 handleException(e);
640 return false;
641 }
642 }
643
644
645
646
647 @Override
648 @Deprecated
649 public boolean isValid(final int timeoutSeconds) throws SQLException {
650 return isValid(Duration.ofSeconds(timeoutSeconds));
651 }
652
653 @Override
654 public boolean isWrapperFor(final Class<?> iface) throws SQLException {
655 if (iface.isAssignableFrom(getClass()) || iface.isAssignableFrom(connection.getClass())) {
656 return true;
657 }
658 return connection.isWrapperFor(iface);
659 }
660
661 @Override
662 public String nativeSQL(final String sql) throws SQLException {
663 checkOpen();
664 try {
665 return connection.nativeSQL(sql);
666 } catch (final SQLException e) {
667 handleException(e);
668 return null;
669 }
670 }
671
672
673
674
675
676
677 protected void passivate() throws SQLException {
678
679
680
681 final List<AbandonedTrace> traceList = getTrace();
682 if (!Utils.isEmpty(traceList)) {
683 final List<Exception> thrownList = new ArrayList<>();
684 traceList.forEach(trace -> trace.close(thrownList::add));
685 clearTrace();
686 if (!thrownList.isEmpty()) {
687 throw new SQLExceptionList(thrownList);
688 }
689 }
690 setLastUsed(Instant.EPOCH);
691 }
692
693 @SuppressWarnings("resource")
694 @Override
695 public CallableStatement prepareCall(final String sql) throws SQLException {
696 checkOpen();
697 try {
698 return init(new DelegatingCallableStatement(this, connection.prepareCall(sql)));
699 } catch (final SQLException e) {
700 handleException(e);
701 return null;
702 }
703 }
704
705 @SuppressWarnings("resource")
706 @Override
707 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
708 throws SQLException {
709 checkOpen();
710 try {
711 return init(new DelegatingCallableStatement(this,
712 connection.prepareCall(sql, resultSetType, resultSetConcurrency)));
713 } catch (final SQLException e) {
714 handleException(e);
715 return null;
716 }
717 }
718
719 @SuppressWarnings("resource")
720 @Override
721 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
722 final int resultSetHoldability) throws SQLException {
723 checkOpen();
724 try {
725 return init(new DelegatingCallableStatement(this,
726 connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
727 } catch (final SQLException e) {
728 handleException(e);
729 return null;
730 }
731 }
732
733 @SuppressWarnings("resource")
734 @Override
735 public PreparedStatement prepareStatement(final String sql) throws SQLException {
736 checkOpen();
737 try {
738 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql)));
739 } catch (final SQLException e) {
740 handleException(e);
741 return null;
742 }
743 }
744
745 @SuppressWarnings("resource")
746 @Override
747 public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
748 checkOpen();
749 try {
750 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, autoGeneratedKeys)));
751 } catch (final SQLException e) {
752 handleException(e);
753 return null;
754 }
755 }
756
757 @SuppressWarnings("resource")
758 @Override
759 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
760 throws SQLException {
761 checkOpen();
762 try {
763 return init(new DelegatingPreparedStatement(this,
764 connection.prepareStatement(sql, resultSetType, resultSetConcurrency)));
765 } catch (final SQLException e) {
766 handleException(e);
767 return null;
768 }
769 }
770
771 @SuppressWarnings("resource")
772 @Override
773 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
774 final int resultSetHoldability) throws SQLException {
775 checkOpen();
776 try {
777 return init(new DelegatingPreparedStatement(this,
778 connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)));
779 } catch (final SQLException e) {
780 handleException(e);
781 return null;
782 }
783 }
784
785 @SuppressWarnings("resource")
786 @Override
787 public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
788 checkOpen();
789 try {
790 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnIndexes)));
791 } catch (final SQLException e) {
792 handleException(e);
793 return null;
794 }
795 }
796
797 @SuppressWarnings("resource")
798 @Override
799 public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
800 checkOpen();
801 try {
802 return init(new DelegatingPreparedStatement(this, connection.prepareStatement(sql, columnNames)));
803 } catch (final SQLException e) {
804 handleException(e);
805 return null;
806 }
807 }
808
809 @Override
810 public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
811 checkOpen();
812 try {
813 connection.releaseSavepoint(savepoint);
814 } catch (final SQLException e) {
815 handleException(e);
816 }
817 }
818
819 @Override
820 public void rollback() throws SQLException {
821 checkOpen();
822 try {
823 connection.rollback();
824 } catch (final SQLException e) {
825 handleException(e);
826 }
827 }
828
829 @Override
830 public void rollback(final Savepoint savepoint) throws SQLException {
831 checkOpen();
832 try {
833 connection.rollback(savepoint);
834 } catch (final SQLException e) {
835 handleException(e);
836 }
837 }
838
839 @Override
840 public void setAutoCommit(final boolean autoCommit) throws SQLException {
841 checkOpen();
842 try {
843 connection.setAutoCommit(autoCommit);
844 if (cacheState) {
845 cachedAutoCommit = connection.getAutoCommit();
846 }
847 } catch (final SQLException e) {
848 cachedAutoCommit = null;
849 handleException(e);
850 }
851 }
852
853
854
855
856
857
858
859
860
861
862
863
864 public void setCacheState(final boolean cacheState) {
865 this.cacheState = cacheState;
866 }
867
868 @Override
869 public void setCatalog(final String catalog) throws SQLException {
870 checkOpen();
871 try {
872 connection.setCatalog(catalog);
873 if (cacheState) {
874 cachedCatalog = connection.getCatalog();
875 }
876 } catch (final SQLException e) {
877 cachedCatalog = null;
878 handleException(e);
879 }
880 }
881
882 @Override
883 public void setClientInfo(final Properties properties) throws SQLClientInfoException {
884 try {
885 checkOpen();
886 connection.setClientInfo(properties);
887 } catch (final SQLClientInfoException e) {
888 throw e;
889 } catch (final SQLException e) {
890 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
891 }
892 }
893
894 @Override
895 public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
896 try {
897 checkOpen();
898 connection.setClientInfo(name, value);
899 } catch (final SQLClientInfoException e) {
900 throw e;
901 } catch (final SQLException e) {
902 throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
903 }
904 }
905
906
907
908
909
910
911 protected void setClosedInternal(final boolean closed) {
912 this.closed = closed;
913 }
914
915
916
917
918
919
920
921
922
923 public void setDefaultQueryTimeout(final Duration defaultQueryTimeoutDuration) {
924 this.defaultQueryTimeoutDuration = defaultQueryTimeoutDuration;
925 }
926
927
928
929
930
931
932
933
934
935 @Deprecated
936 public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
937 this.defaultQueryTimeoutDuration = defaultQueryTimeoutSeconds == null ? null : Duration.ofSeconds(defaultQueryTimeoutSeconds);
938 }
939
940
941
942
943
944
945
946 public void setDelegate(final C connection) {
947 this.connection = connection;
948 }
949
950 @Override
951 public void setHoldability(final int holdability) throws SQLException {
952 checkOpen();
953 try {
954 connection.setHoldability(holdability);
955 } catch (final SQLException e) {
956 handleException(e);
957 }
958 }
959
960 @Override
961 public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
962 checkOpen();
963 try {
964 Jdbc41Bridge.setNetworkTimeout(connection, executor, milliseconds);
965 } catch (final SQLException e) {
966 handleException(e);
967 }
968 }
969
970 @Override
971 public void setReadOnly(final boolean readOnly) throws SQLException {
972 checkOpen();
973 try {
974 connection.setReadOnly(readOnly);
975 if (cacheState) {
976 cachedReadOnly = connection.isReadOnly();
977 }
978 } catch (final SQLException e) {
979 cachedReadOnly = null;
980 handleException(e);
981 }
982 }
983
984 @Override
985 public Savepoint setSavepoint() throws SQLException {
986 checkOpen();
987 try {
988 return connection.setSavepoint();
989 } catch (final SQLException e) {
990 handleException(e);
991 return null;
992 }
993 }
994
995 @Override
996 public Savepoint setSavepoint(final String name) throws SQLException {
997 checkOpen();
998 try {
999 return connection.setSavepoint(name);
1000 } catch (final SQLException e) {
1001 handleException(e);
1002 return null;
1003 }
1004 }
1005
1006 @Override
1007 public void setSchema(final String schema) throws SQLException {
1008 checkOpen();
1009 try {
1010 Jdbc41Bridge.setSchema(connection, schema);
1011 if (cacheState) {
1012 cachedSchema = Jdbc41Bridge.getSchema(connection);
1013 }
1014 } catch (final SQLException e) {
1015 cachedSchema = null;
1016 handleException(e);
1017 }
1018 }
1019
1020 @Override
1021 public void setTransactionIsolation(final int level) throws SQLException {
1022 checkOpen();
1023 try {
1024 connection.setTransactionIsolation(level);
1025 } catch (final SQLException e) {
1026 handleException(e);
1027 }
1028 }
1029
1030 @Override
1031 public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
1032 checkOpen();
1033 try {
1034 connection.setTypeMap(map);
1035 } catch (final SQLException e) {
1036 handleException(e);
1037 }
1038 }
1039
1040
1041
1042
1043 @SuppressWarnings("resource")
1044 @Override
1045 public synchronized String toString() {
1046 String str = null;
1047
1048 final Connection conn = this.getInnermostDelegateInternal();
1049 if (conn != null) {
1050 try {
1051 if (conn.isClosed()) {
1052 str = "connection is closed";
1053 } else {
1054 final StringBuilder sb = new StringBuilder();
1055 sb.append(hashCode());
1056 final DatabaseMetaData meta = conn.getMetaData();
1057 if (meta != null) {
1058 sb.append(", URL=");
1059 sb.append(meta.getURL());
1060 sb.append(", ");
1061 sb.append(meta.getDriverName());
1062 str = sb.toString();
1063 }
1064 }
1065 } catch (final SQLException ignored) {
1066
1067 }
1068 }
1069 return str != null ? str : super.toString();
1070 }
1071
1072 @Override
1073 public <T> T unwrap(final Class<T> iface) throws SQLException {
1074 if (iface.isAssignableFrom(getClass())) {
1075 return iface.cast(this);
1076 }
1077 if (iface.isAssignableFrom(connection.getClass())) {
1078 return iface.cast(connection);
1079 }
1080 return connection.unwrap(iface);
1081 }
1082 }