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.Connection;
20 import java.sql.SQLException;
21 import java.sql.Statement;
22 import java.time.Duration;
23 import java.util.Collection;
24 import java.util.Objects;
25 import java.util.concurrent.atomic.AtomicLong;
26
27 import javax.management.MalformedObjectNameException;
28 import javax.management.ObjectName;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.commons.pool2.DestroyMode;
33 import org.apache.commons.pool2.KeyedObjectPool;
34 import org.apache.commons.pool2.ObjectPool;
35 import org.apache.commons.pool2.PooledObject;
36 import org.apache.commons.pool2.PooledObjectFactory;
37 import org.apache.commons.pool2.impl.DefaultPooledObject;
38 import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
39 import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
40
41
42
43
44
45
46 public class PoolableConnectionFactory implements PooledObjectFactory<PoolableConnection> {
47
48 private static final Log log = LogFactory.getLog(PoolableConnectionFactory.class);
49
50
51
52
53 static final int UNKNOWN_TRANSACTION_ISOLATION = -1;
54
55 private final ConnectionFactory connectionFactory;
56
57 private final ObjectName dataSourceJmxObjectName;
58
59 private volatile String validationQuery;
60
61 private volatile Duration validationQueryTimeoutDuration = Duration.ofSeconds(-1);
62
63 private Collection<String> connectionInitSqls;
64
65 private Collection<String> disconnectionSqlCodes;
66
67 private Collection<String> disconnectionIgnoreSqlCodes;
68
69 private volatile boolean fastFailValidation = true;
70
71 private volatile ObjectPool<PoolableConnection> pool;
72
73 private Boolean defaultReadOnly;
74
75 private Boolean defaultAutoCommit;
76
77 private volatile boolean autoCommitOnReturn = true;
78
79 private volatile boolean rollbackOnReturn = true;
80
81 private volatile int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION;
82
83 private String defaultCatalog;
84
85 private String defaultSchema;
86
87 private volatile boolean cacheState;
88
89 private volatile boolean poolStatements;
90
91 private volatile boolean clearStatementPoolOnReturn;
92
93 private volatile int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
94
95 private Duration maxConnDuration = Duration.ofMillis(-1);
96
97 private final AtomicLong connectionIndex = new AtomicLong();
98
99 private Duration defaultQueryTimeoutDuration;
100
101
102
103
104
105
106
107
108
109 public PoolableConnectionFactory(final ConnectionFactory connFactory, final ObjectName dataSourceJmxObjectName) {
110 this.connectionFactory = connFactory;
111 this.dataSourceJmxObjectName = dataSourceJmxObjectName;
112 }
113
114 @Override
115 public void activateObject(final PooledObject<PoolableConnection> p) throws SQLException {
116 validateLifetime(p);
117 final PoolableConnection poolableConnection = p.getObject();
118 poolableConnection.activate();
119 if (defaultAutoCommit != null && poolableConnection.getAutoCommit() != defaultAutoCommit) {
120 poolableConnection.setAutoCommit(defaultAutoCommit);
121 }
122 if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION && poolableConnection.getTransactionIsolation() != defaultTransactionIsolation) {
123 poolableConnection.setTransactionIsolation(defaultTransactionIsolation);
124 }
125 if (defaultReadOnly != null && poolableConnection.isReadOnly() != defaultReadOnly) {
126 poolableConnection.setReadOnly(defaultReadOnly);
127 }
128 if (defaultCatalog != null && !defaultCatalog.equals(poolableConnection.getCatalog())) {
129 poolableConnection.setCatalog(defaultCatalog);
130 }
131 if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(poolableConnection))) {
132 Jdbc41Bridge.setSchema(poolableConnection, defaultSchema);
133 }
134 poolableConnection.setDefaultQueryTimeout(defaultQueryTimeoutDuration);
135 }
136
137 @Override
138 public void destroyObject(final PooledObject<PoolableConnection> p) throws SQLException {
139 p.getObject().reallyClose();
140 }
141
142
143
144
145 @Override
146 public void destroyObject(final PooledObject<PoolableConnection> p, final DestroyMode mode) throws SQLException {
147 if (mode == DestroyMode.ABANDONED) {
148 Jdbc41Bridge.abort(p.getObject().getInnermostDelegate(), Runnable::run);
149 } else {
150 p.getObject().reallyClose();
151 }
152 }
153
154
155
156
157
158
159
160 public boolean getCacheState() {
161 return cacheState;
162 }
163
164
165
166
167
168
169
170 public ConnectionFactory getConnectionFactory() {
171 return connectionFactory;
172 }
173
174
175
176
177
178
179 protected AtomicLong getConnectionIndex() {
180 return connectionIndex;
181 }
182
183
184
185
186
187
188
189 public Collection<String> getConnectionInitSqls() {
190 return connectionInitSqls;
191 }
192
193
194
195
196
197
198
199 public ObjectName getDataSourceJmxName() {
200 return dataSourceJmxObjectName;
201 }
202
203
204
205
206
207
208
209 public ObjectName getDataSourceJmxObjectName() {
210 return dataSourceJmxObjectName;
211 }
212
213
214
215
216
217
218
219 public Boolean getDefaultAutoCommit() {
220 return defaultAutoCommit;
221 }
222
223
224
225
226
227
228
229 public String getDefaultCatalog() {
230 return defaultCatalog;
231 }
232
233
234
235
236
237
238
239 @Deprecated
240 public Integer getDefaultQueryTimeout() {
241 return getDefaultQueryTimeoutSeconds();
242 }
243
244
245
246
247
248
249
250 public Duration getDefaultQueryTimeoutDuration() {
251 return defaultQueryTimeoutDuration;
252 }
253
254
255
256
257
258
259
260
261 @Deprecated
262 public Integer getDefaultQueryTimeoutSeconds() {
263 return defaultQueryTimeoutDuration == null ? null : (int) defaultQueryTimeoutDuration.getSeconds();
264 }
265
266
267
268
269
270
271
272 public Boolean getDefaultReadOnly() {
273 return defaultReadOnly;
274 }
275
276
277
278
279
280
281
282 public String getDefaultSchema() {
283 return defaultSchema;
284 }
285
286
287
288
289
290
291
292 public int getDefaultTransactionIsolation() {
293 return defaultTransactionIsolation;
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307 public Collection<String> getDisconnectionIgnoreSqlCodes() {
308 return disconnectionIgnoreSqlCodes;
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327 public Collection<String> getDisconnectionSqlCodes() {
328 return disconnectionSqlCodes;
329 }
330
331
332
333
334
335
336
337 public Duration getMaxConnDuration() {
338 return maxConnDuration;
339 }
340
341
342
343
344
345
346
347 public long getMaxConnLifetimeMillis() {
348 return maxConnDuration.toMillis();
349 }
350
351
352
353
354
355
356 protected int getMaxOpenPreparedStatements() {
357 return maxOpenPreparedStatements;
358 }
359
360
361
362
363
364
365 public synchronized ObjectPool<PoolableConnection> getPool() {
366 return pool;
367 }
368
369
370
371
372
373
374
375 public boolean getPoolStatements() {
376 return poolStatements;
377 }
378
379
380
381
382
383
384
385 public String getValidationQuery() {
386 return validationQuery;
387 }
388
389
390
391
392
393
394
395 public Duration getValidationQueryTimeoutDuration() {
396 return validationQueryTimeoutDuration;
397 }
398
399
400
401
402
403
404
405
406 @Deprecated
407 public int getValidationQueryTimeoutSeconds() {
408 return (int) validationQueryTimeoutDuration.getSeconds();
409 }
410
411
412
413
414
415
416
417
418 protected void initializeConnection(final Connection conn) throws SQLException {
419 final Collection<String> sqls = connectionInitSqls;
420 if (conn.isClosed()) {
421 throw new SQLException("initializeConnection: connection closed");
422 }
423 if (!Utils.isEmpty(sqls)) {
424 try (Statement statement = conn.createStatement()) {
425 for (final String sql : sqls) {
426 statement.execute(Objects.requireNonNull(sql, "null connectionInitSqls element"));
427 }
428 }
429 }
430 }
431
432
433
434
435
436
437
438 public boolean isAutoCommitOnReturn() {
439 return autoCommitOnReturn;
440 }
441
442
443
444
445
446
447
448 @Deprecated
449 public boolean isEnableAutoCommitOnReturn() {
450 return autoCommitOnReturn;
451 }
452
453
454
455
456
457
458
459
460
461
462 public boolean isFastFailValidation() {
463 return fastFailValidation;
464 }
465
466
467
468
469
470
471 public boolean isRollbackOnReturn() {
472 return rollbackOnReturn;
473 }
474
475 @Override
476 public PooledObject<PoolableConnection> makeObject() throws SQLException {
477 Connection conn = connectionFactory.createConnection();
478 if (conn == null) {
479 throw new IllegalStateException("Connection factory returned null from createConnection");
480 }
481 try {
482 initializeConnection(conn);
483 } catch (final SQLException e) {
484
485 Utils.closeQuietly((AutoCloseable) conn);
486
487 throw e;
488 }
489
490 final long connIndex = connectionIndex.getAndIncrement();
491
492 if (poolStatements) {
493 conn = new PoolingConnection(conn);
494 final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
495 config.setMaxTotalPerKey(-1);
496 config.setBlockWhenExhausted(false);
497 config.setMaxWait(Duration.ZERO);
498 config.setMaxIdlePerKey(1);
499 config.setMaxTotal(maxOpenPreparedStatements);
500 if (dataSourceJmxObjectName != null) {
501 final StringBuilder base = new StringBuilder(dataSourceJmxObjectName.toString());
502 base.append(Constants.JMX_CONNECTION_BASE_EXT);
503 base.append(connIndex);
504 config.setJmxNameBase(base.toString());
505 config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX);
506 } else {
507 config.setJmxEnabled(false);
508 }
509 final PoolingConnection poolingConn = (PoolingConnection) conn;
510 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(poolingConn, config);
511 poolingConn.setStatementPool(stmtPool);
512 poolingConn.setClearStatementPoolOnReturn(clearStatementPoolOnReturn);
513 poolingConn.setCacheState(cacheState);
514 }
515
516
517 final ObjectName connJmxName;
518 if (dataSourceJmxObjectName == null) {
519 connJmxName = null;
520 } else {
521 final String name = dataSourceJmxObjectName.toString() + Constants.JMX_CONNECTION_BASE_EXT + connIndex;
522 try {
523 connJmxName = new ObjectName(name);
524 } catch (final MalformedObjectNameException e) {
525 Utils.closeQuietly((AutoCloseable) conn);
526 throw new SQLException(name, e);
527 }
528 }
529
530 final PoolableConnection pc = new PoolableConnection(conn, pool, connJmxName,
531 disconnectionSqlCodes, disconnectionIgnoreSqlCodes, fastFailValidation);
532 pc.setCacheState(cacheState);
533
534 return new DefaultPooledObject<>(pc);
535 }
536
537 @Override
538 public void passivateObject(final PooledObject<PoolableConnection> p) throws SQLException {
539
540 validateLifetime(p);
541
542 final PoolableConnection conn = p.getObject();
543 Boolean connAutoCommit = null;
544 if (rollbackOnReturn) {
545 connAutoCommit = conn.getAutoCommit();
546 if (!connAutoCommit && !conn.isReadOnly()) {
547 conn.rollback();
548 }
549 }
550
551 conn.clearWarnings();
552
553
554
555 if (autoCommitOnReturn) {
556 if (connAutoCommit == null) {
557 connAutoCommit = conn.getAutoCommit();
558 }
559 if (!connAutoCommit) {
560 conn.setAutoCommit(true);
561 }
562 }
563
564 conn.passivate();
565 }
566
567
568
569
570
571
572 public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) {
573 this.autoCommitOnReturn = autoCommitOnReturn;
574 }
575
576
577
578
579
580
581 public void setCacheState(final boolean cacheState) {
582 this.cacheState = cacheState;
583 }
584
585
586
587
588
589
590
591
592 public void setClearStatementPoolOnReturn(final boolean clearStatementPoolOnReturn) {
593 this.clearStatementPoolOnReturn = clearStatementPoolOnReturn;
594 }
595
596
597
598
599
600
601
602
603 public void setConnectionInitSql(final Collection<String> connectionInitSqls) {
604 this.connectionInitSqls = connectionInitSqls;
605 }
606
607
608
609
610
611
612 public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
613 this.defaultAutoCommit = defaultAutoCommit;
614 }
615
616
617
618
619
620
621
622 public void setDefaultCatalog(final String defaultCatalog) {
623 this.defaultCatalog = defaultCatalog;
624 }
625
626
627
628
629
630
631
632 public void setDefaultQueryTimeout(final Duration defaultQueryTimeoutDuration) {
633 this.defaultQueryTimeoutDuration = defaultQueryTimeoutDuration;
634 }
635
636
637
638
639
640
641
642 @Deprecated
643 public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
644 this.defaultQueryTimeoutDuration = defaultQueryTimeoutSeconds == null ? null : Duration.ofSeconds(defaultQueryTimeoutSeconds);
645 }
646
647
648
649
650
651
652
653 public void setDefaultReadOnly(final Boolean defaultReadOnly) {
654 this.defaultReadOnly = defaultReadOnly;
655 }
656
657
658
659
660
661
662
663
664 public void setDefaultSchema(final String defaultSchema) {
665 this.defaultSchema = defaultSchema;
666 }
667
668
669
670
671
672
673
674 public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
675 this.defaultTransactionIsolation = defaultTransactionIsolation;
676 }
677
678
679
680
681
682
683
684
685
686
687 public void setDisconnectionIgnoreSqlCodes(final Collection<String> disconnectionIgnoreSqlCodes) {
688 Utils.checkSqlCodes(disconnectionIgnoreSqlCodes, this.disconnectionSqlCodes);
689 this.disconnectionIgnoreSqlCodes = disconnectionIgnoreSqlCodes;
690 }
691
692
693
694
695
696
697
698
699
700
701 public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
702 Utils.checkSqlCodes(disconnectionSqlCodes, this.disconnectionIgnoreSqlCodes);
703 this.disconnectionSqlCodes = disconnectionSqlCodes;
704 }
705
706
707
708
709
710
711 @Deprecated
712 public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) {
713 this.autoCommitOnReturn = autoCommitOnReturn;
714 }
715
716
717
718
719
720
721
722
723 public void setFastFailValidation(final boolean fastFailValidation) {
724 this.fastFailValidation = fastFailValidation;
725 }
726
727
728
729
730
731
732
733
734
735 public void setMaxConn(final Duration maxConnDuration) {
736 this.maxConnDuration = maxConnDuration;
737 }
738
739
740
741
742
743
744
745
746
747 @Deprecated
748 public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
749 this.maxConnDuration = Duration.ofMillis(maxConnLifetimeMillis);
750 }
751
752
753
754
755
756
757
758 public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) {
759 this.maxOpenPreparedStatements = maxOpenPreparedStatements;
760 }
761
762
763
764
765
766
767
768
769 @Deprecated
770 public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) {
771 setMaxOpenPreparedStatements(maxOpenPreparedStatements);
772 }
773
774
775
776
777
778
779
780 public synchronized void setPool(final ObjectPool<PoolableConnection> pool) {
781 if (null != this.pool && pool != this.pool) {
782 Utils.closeQuietly(this.pool);
783 }
784 this.pool = pool;
785 }
786
787
788
789
790
791
792 public void setPoolStatements(final boolean poolStatements) {
793 this.poolStatements = poolStatements;
794 }
795
796
797
798
799
800
801 public void setRollbackOnReturn(final boolean rollbackOnReturn) {
802 this.rollbackOnReturn = rollbackOnReturn;
803 }
804
805
806
807
808
809
810
811
812 public void setValidationQuery(final String validationQuery) {
813 this.validationQuery = validationQuery;
814 }
815
816
817
818
819
820
821
822
823 public void setValidationQueryTimeout(final Duration validationQueryTimeoutDuration) {
824 this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
825 }
826
827
828
829
830
831
832
833
834
835 @Deprecated
836 public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
837 this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds);
838 }
839
840
841
842
843
844
845
846 public void validateConnection(final PoolableConnection conn) throws SQLException {
847 if (conn.isClosed()) {
848 throw new SQLException("validateConnection: connection closed");
849 }
850 conn.validate(validationQuery, validationQueryTimeoutDuration);
851 }
852
853 private void validateLifetime(final PooledObject<PoolableConnection> p) throws LifetimeExceededException {
854 Utils.validateLifetime(p, maxConnDuration);
855 }
856
857 @Override
858 public boolean validateObject(final PooledObject<PoolableConnection> p) {
859 try {
860 validateLifetime(p);
861 validateConnection(p.getObject());
862 return true;
863 } catch (final Exception e) {
864 if (log.isDebugEnabled()) {
865 log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e);
866 }
867 return false;
868 }
869 }
870 }