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