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