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