1
17
18 package org.apache.tomcat.dbcp.dbcp2;
19
20 import java.sql.Connection;
21 import java.sql.SQLException;
22 import java.sql.Statement;
23 import java.util.Collection;
24 import java.util.Objects;
25 import java.util.concurrent.atomic.AtomicLong;
26
27 import javax.management.ObjectName;
28
29 import org.apache.juli.logging.Log;
30 import org.apache.juli.logging.LogFactory;
31 import org.apache.tomcat.dbcp.pool2.KeyedObjectPool;
32 import org.apache.tomcat.dbcp.pool2.ObjectPool;
33 import org.apache.tomcat.dbcp.pool2.PooledObject;
34 import org.apache.tomcat.dbcp.pool2.PooledObjectFactory;
35 import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject;
36 import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool;
37 import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPoolConfig;
38
39
44 public class PoolableConnectionFactory implements PooledObjectFactory<PoolableConnection> {
45
46 private static final Log log = LogFactory.getLog(PoolableConnectionFactory.class);
47
48
51 static final int UNKNOWN_TRANSACTION_ISOLATION = -1;
52
53 private final ConnectionFactory connectionFactory;
54
55 private final ObjectName dataSourceJmxObjectName;
56
57 private volatile String validationQuery;
58
59 private volatile int validationQueryTimeoutSeconds = -1;
60
61 private Collection<String> connectionInitSqls;
62
63 private Collection<String> disconnectionSqlCodes;
64
65 private boolean fastFailValidation = true;
66
67 private volatile ObjectPool<PoolableConnection> pool;
68
69 private Boolean defaultReadOnly;
70
71 private Boolean defaultAutoCommit;
72
73 private boolean autoCommitOnReturn = true;
74
75 private boolean rollbackOnReturn = true;
76
77 private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION;
78
79 private String defaultCatalog;
80
81 private String defaultSchema;
82
83 private boolean cacheState;
84
85 private boolean poolStatements;
86
87 private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
88
89 private long maxConnLifetimeMillis = -1;
90
91 private final AtomicLong connectionIndex = new AtomicLong(0);
92
93 private Integer defaultQueryTimeoutSeconds;
94
95
103 public PoolableConnectionFactory(final ConnectionFactory connFactory, final ObjectName dataSourceJmxObjectName) {
104 this.connectionFactory = connFactory;
105 this.dataSourceJmxObjectName = dataSourceJmxObjectName;
106 }
107
108 @Override
109 public void activateObject(final PooledObject<PoolableConnection> p) throws Exception {
110
111 validateLifetime(p);
112
113 final PoolableConnection conn = p.getObject();
114 conn.activate();
115
116 if (defaultAutoCommit != null && conn.getAutoCommit() != defaultAutoCommit.booleanValue()) {
117 conn.setAutoCommit(defaultAutoCommit.booleanValue());
118 }
119 if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION
120 && conn.getTransactionIsolation() != defaultTransactionIsolation) {
121 conn.setTransactionIsolation(defaultTransactionIsolation);
122 }
123 if (defaultReadOnly != null && conn.isReadOnly() != defaultReadOnly.booleanValue()) {
124 conn.setReadOnly(defaultReadOnly.booleanValue());
125 }
126 if (defaultCatalog != null && !defaultCatalog.equals(conn.getCatalog())) {
127 conn.setCatalog(defaultCatalog);
128 }
129 if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(conn))) {
130 Jdbc41Bridge.setSchema(conn, defaultSchema);
131 }
132 conn.setDefaultQueryTimeout(defaultQueryTimeoutSeconds);
133 }
134
135 @Override
136 public void destroyObject(final PooledObject<PoolableConnection> p) throws Exception {
137 p.getObject().reallyClose();
138 }
139
140
144 public boolean getCacheState() {
145 return cacheState;
146 }
147
148
152 public ConnectionFactory getConnectionFactory() {
153 return connectionFactory;
154 }
155
156 protected AtomicLong getConnectionIndex() {
157 return connectionIndex;
158 }
159
160
164 public Collection<String> getConnectionInitSqls() {
165 return connectionInitSqls;
166 }
167
168
172 public ObjectName getDataSourceJmxName() {
173 return dataSourceJmxObjectName;
174 }
175
176
180 public ObjectName getDataSourceJmxObjectName() {
181 return dataSourceJmxObjectName;
182 }
183
184
188 public Boolean getDefaultAutoCommit() {
189 return defaultAutoCommit;
190 }
191
192
196 public String getDefaultCatalog() {
197 return defaultCatalog;
198 }
199
200
203 public Integer getDefaultQueryTimeout() {
204 return defaultQueryTimeoutSeconds;
205 }
206
207
211 public Integer getDefaultQueryTimeoutSeconds() {
212 return defaultQueryTimeoutSeconds;
213 }
214
215
219 public Boolean getDefaultReadOnly() {
220 return defaultReadOnly;
221 }
222
223
227 public String getDefaultSchema() {
228 return defaultSchema;
229 }
230
231
235 public int getDefaultTransactionIsolation() {
236 return defaultTransactionIsolation;
237 }
238
239
255 public Collection<String> getDisconnectionSqlCodes() {
256 return disconnectionSqlCodes;
257 }
258
259
263 public long getMaxConnLifetimeMillis() {
264 return maxConnLifetimeMillis;
265 }
266
267 protected int getMaxOpenPreparedStatements() {
268 return maxOpenPreparedStatements;
269 }
270
271
276 public synchronized ObjectPool<PoolableConnection> getPool() {
277 return pool;
278 }
279
280
284 public boolean getPoolStatements() {
285 return poolStatements;
286 }
287
291 public String getValidationQuery() {
292 return validationQuery;
293 }
294
298 public int getValidationQueryTimeoutSeconds() {
299 return validationQueryTimeoutSeconds;
300 }
301 protected void initializeConnection(final Connection conn) throws SQLException {
302 final Collection<String> sqls = connectionInitSqls;
303 if (conn.isClosed()) {
304 throw new SQLException("initializeConnection: connection closed");
305 }
306 if (null != sqls) {
307 try (Statement stmt = conn.createStatement()) {
308 for (final String sql : sqls) {
309 Objects.requireNonNull(sql, "null connectionInitSqls element");
310 stmt.execute(sql);
311 }
312 }
313 }
314 }
315
316
320 public boolean isAutoCommitOnReturn() {
321 return autoCommitOnReturn;
322 }
323
324
328 @Deprecated
329 public boolean isEnableAutoCommitOnReturn() {
330 return autoCommitOnReturn;
331 }
332
333
342 public boolean isFastFailValidation() {
343 return fastFailValidation;
344 }
345
346
349 public boolean isRollbackOnReturn() {
350 return rollbackOnReturn;
351 }
352
353 @Override
354 public PooledObject<PoolableConnection> makeObject() throws Exception {
355 Connection conn = connectionFactory.createConnection();
356 if (conn == null) {
357 throw new IllegalStateException("Connection factory returned null from createConnection");
358 }
359 try {
360 initializeConnection(conn);
361 } catch (final SQLException sqle) {
362
363 try {
364 conn.close();
365 } catch (final SQLException ignore) {
366
367 }
368
369 throw sqle;
370 }
371
372 final long connIndex = connectionIndex.getAndIncrement();
373
374 if (poolStatements) {
375 conn = new PoolingConnection(conn);
376 final GenericKeyedObjectPoolConfig<DelegatingPreparedStatement> config = new GenericKeyedObjectPoolConfig<>();
377 config.setMaxTotalPerKey(-1);
378 config.setBlockWhenExhausted(false);
379 config.setMaxWaitMillis(0);
380 config.setMaxIdlePerKey(1);
381 config.setMaxTotal(maxOpenPreparedStatements);
382 if (dataSourceJmxObjectName != null) {
383 final StringBuilder base = new StringBuilder(dataSourceJmxObjectName.toString());
384 base.append(Constants.JMX_CONNECTION_BASE_EXT);
385 base.append(Long.toString(connIndex));
386 config.setJmxNameBase(base.toString());
387 config.setJmxNamePrefix(Constants.JMX_STATEMENT_POOL_PREFIX);
388 } else {
389 config.setJmxEnabled(false);
390 }
391 final PoolingConnection poolingConn = (PoolingConnection) conn;
392 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = new GenericKeyedObjectPool<>(
393 poolingConn, config);
394 poolingConn.setStatementPool(stmtPool);
395 poolingConn.setCacheState(cacheState);
396 }
397
398
399 ObjectName connJmxName;
400 if (dataSourceJmxObjectName == null) {
401 connJmxName = null;
402 } else {
403 connJmxName = new ObjectName(
404 dataSourceJmxObjectName.toString() + Constants.JMX_CONNECTION_BASE_EXT + connIndex);
405 }
406
407 final PoolableConnection pc = new PoolableConnection(conn, pool, connJmxName, disconnectionSqlCodes,
408 fastFailValidation);
409 pc.setCacheState(cacheState);
410
411 return new DefaultPooledObject<>(pc);
412 }
413
414 @Override
415 public void passivateObject(final PooledObject<PoolableConnection> p) throws Exception {
416
417 validateLifetime(p);
418
419 final PoolableConnection conn = p.getObject();
420 Boolean connAutoCommit = null;
421 if (rollbackOnReturn) {
422 connAutoCommit = Boolean.valueOf(conn.getAutoCommit());
423 if (!connAutoCommit.booleanValue() && !conn.isReadOnly()) {
424 conn.rollback();
425 }
426 }
427
428 conn.clearWarnings();
429
430
431
432 if (autoCommitOnReturn) {
433 if (connAutoCommit == null) {
434 connAutoCommit = Boolean.valueOf(conn.getAutoCommit());
435 }
436 if (!connAutoCommit.booleanValue()) {
437 conn.setAutoCommit(true);
438 }
439 }
440
441 conn.passivate();
442 }
443
444 public void setAutoCommitOnReturn(final boolean autoCommitOnReturn) {
445 this.autoCommitOnReturn = autoCommitOnReturn;
446 }
447
448 public void setCacheState(final boolean cacheState) {
449 this.cacheState = cacheState;
450 }
451
452
459 public void setConnectionInitSql(final Collection<String> connectionInitSqls) {
460 this.connectionInitSqls = connectionInitSqls;
461 }
462
463
469 public void setDefaultAutoCommit(final Boolean defaultAutoCommit) {
470 this.defaultAutoCommit = defaultAutoCommit;
471 }
472
473
479 public void setDefaultCatalog(final String defaultCatalog) {
480 this.defaultCatalog = defaultCatalog;
481 }
482
483 public void setDefaultQueryTimeout(final Integer defaultQueryTimeoutSeconds) {
484 this.defaultQueryTimeoutSeconds = defaultQueryTimeoutSeconds;
485 }
486
492 public void setDefaultReadOnly(final Boolean defaultReadOnly) {
493 this.defaultReadOnly = defaultReadOnly;
494 }
495
496
503 public void setDefaultSchema(final String defaultSchema) {
504 this.defaultSchema = defaultSchema;
505 }
506
507
513 public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) {
514 this.defaultTransactionIsolation = defaultTransactionIsolation;
515 }
516
517
523 public void setDisconnectionSqlCodes(final Collection<String> disconnectionSqlCodes) {
524 this.disconnectionSqlCodes = disconnectionSqlCodes;
525 }
526
527
531 @Deprecated
532 public void setEnableAutoCommitOnReturn(final boolean autoCommitOnReturn) {
533 this.autoCommitOnReturn = autoCommitOnReturn;
534 }
535
536
542 public void setFastFailValidation(final boolean fastFailValidation) {
543 this.fastFailValidation = fastFailValidation;
544 }
545
546
553 public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) {
554 this.maxConnLifetimeMillis = maxConnLifetimeMillis;
555 }
556
557
563 public void setMaxOpenPreparedStatements(final int maxOpenPreparedStatements) {
564 this.maxOpenPreparedStatements = maxOpenPreparedStatements;
565 }
566
567
574 @Deprecated
575 public void setMaxOpenPrepatedStatements(final int maxOpenPreparedStatements) {
576 setMaxOpenPreparedStatements(maxOpenPreparedStatements);
577 }
578
579
585 public synchronized void setPool(final ObjectPool<PoolableConnection> pool) {
586 if (null != this.pool && pool != this.pool) {
587 try {
588 this.pool.close();
589 } catch (final Exception e) {
590
591 }
592 }
593 this.pool = pool;
594 }
595
596 public void setPoolStatements(final boolean poolStatements) {
597 this.poolStatements = poolStatements;
598 }
599
600 public void setRollbackOnReturn(final boolean rollbackOnReturn) {
601 this.rollbackOnReturn = rollbackOnReturn;
602 }
603
604
611 public void setValidationQuery(final String validationQuery) {
612 this.validationQuery = validationQuery;
613 }
614
615
622 public void setValidationQueryTimeout(final int validationQueryTimeoutSeconds) {
623 this.validationQueryTimeoutSeconds = validationQueryTimeoutSeconds;
624 }
625
626 public void validateConnection(final PoolableConnection conn) throws SQLException {
627 if (conn.isClosed()) {
628 throw new SQLException("validateConnection: connection closed");
629 }
630 conn.validate(validationQuery, validationQueryTimeoutSeconds);
631 }
632
633 private void validateLifetime(final PooledObject<PoolableConnection> p) throws Exception {
634 if (maxConnLifetimeMillis > 0) {
635 final long lifetime = System.currentTimeMillis() - p.getCreateTime();
636 if (lifetime > maxConnLifetimeMillis) {
637 throw new LifetimeExceededException(Utils.getMessage("connectionFactory.lifetimeExceeded",
638 Long.valueOf(lifetime), Long.valueOf(maxConnLifetimeMillis)));
639 }
640 }
641 }
642
643 @Override
644 public boolean validateObject(final PooledObject<PoolableConnection> p) {
645 try {
646 validateLifetime(p);
647
648 validateConnection(p.getObject());
649 return true;
650 } catch (final Exception e) {
651 if (log.isDebugEnabled()) {
652 log.debug(Utils.getMessage("poolableConnectionFactory.validateObject.fail"), e);
653 }
654 return false;
655 }
656 }
657 }
658