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