View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.dbcp2.datasources;
19  
20  import java.sql.Connection;
21  import java.sql.ResultSet;
22  import java.sql.SQLException;
23  import java.sql.Statement;
24  import java.time.Duration;
25  import java.util.Collections;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentHashMap;
29  
30  import javax.sql.ConnectionPoolDataSource;
31  import javax.sql.PooledConnection;
32  
33  import org.apache.commons.dbcp2.Utils;
34  import org.apache.commons.pool2.PooledObject;
35  
36  /**
37   * Abstracts services for connection factories in this package.
38   */
39  class AbstractConnectionFactory {
40  
41      protected final ConnectionPoolDataSource cpds;
42      protected Duration maxConnDuration = Duration.ofMillis(-1);
43      protected final boolean rollbackAfterValidation;
44  
45      /**
46       * Map of PooledConnectionAndInfo instances
47       */
48      protected final Map<PooledConnection, PooledConnectionAndInfo> pcMap = new ConcurrentHashMap<>();
49  
50      /**
51       * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated.
52       */
53      protected final Set<PooledConnection> validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
54      protected final String validationQuery;
55      protected final Duration validationQueryTimeoutDuration;
56  
57      public AbstractConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final Duration validationQueryTimeoutDuration, final boolean rollbackAfterValidation) {
58          this.cpds = cpds;
59          this.validationQuery = validationQuery;
60          this.validationQueryTimeoutDuration = validationQueryTimeoutDuration;
61          this.rollbackAfterValidation = rollbackAfterValidation;
62      }
63  
64      /**
65       * Sets the maximum lifetime of a connection after which the connection will always fail activation,
66       * passivation and validation.
67       *
68       * @param duration
69       *            A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds.
70       * @since 2.10.0
71       */
72      void setMaxConn(final Duration duration) {
73          this.maxConnDuration = duration;
74      }
75  
76      /**
77       * Converts a duration to seconds where a duration less than one second becomes 1 second.
78       *
79       * @param duration the duration to convert.
80       * @return a duration to seconds where a duration less than one second becomes 1 second.
81       * @throws ArithmeticException if the query validation timeout does not fit as seconds in an int.
82       */
83      private int toSeconds(final Duration duration) {
84          if (duration.isNegative() || duration.isZero()) {
85              return 0;
86          }
87          final long seconds = validationQueryTimeoutDuration.getSeconds();
88          return seconds != 0 ? Math.toIntExact(seconds) : 1;
89      }
90  
91      protected void validateLifetime(final PooledObject<PooledConnectionAndInfo> pooledObject) throws SQLException {
92          Utils.validateLifetime(pooledObject, maxConnDuration);
93      }
94  
95      public boolean validateObject(final PooledObject<PooledConnectionAndInfo> pooledObject) {
96          try {
97              validateLifetime(pooledObject);
98          } catch (final Exception e) {
99              return false;
100         }
101         boolean valid = false;
102         final PooledConnection pooledConn = pooledObject.getObject().getPooledConnection();
103         Connection conn = null;
104         // logical Connection from the PooledConnection must be closed
105         // before another one can be requested and closing it will
106         // generate an event. Keep track so we know not to return
107         // the PooledConnection
108         validatingSet.add(pooledConn);
109         try {
110             final int timeoutSeconds = toSeconds(validationQueryTimeoutDuration);
111             if (validationQuery == null) {
112                 try {
113                     conn = pooledConn.getConnection();
114                     valid = conn.isValid(timeoutSeconds);
115                 } catch (final SQLException e) {
116                     valid = false;
117                 }
118             } else {
119                 Statement stmt = null;
120                 ResultSet rset = null;
121                 try {
122                     conn = pooledConn.getConnection();
123                     stmt = conn.createStatement();
124                     if (timeoutSeconds > 0) {
125                         stmt.setQueryTimeout(timeoutSeconds);
126                     }
127                     rset = stmt.executeQuery(validationQuery);
128                     valid = rset.next();
129                     if (rollbackAfterValidation) {
130                         conn.rollback();
131                     }
132                 } catch (final Exception e) {
133                     valid = false;
134                 } finally {
135                     Utils.closeQuietly((AutoCloseable) rset);
136                     Utils.closeQuietly((AutoCloseable) stmt);
137                 }
138             }
139         } finally {
140             Utils.closeQuietly((AutoCloseable) conn);
141             validatingSet.remove(pooledConn);
142         }
143         return valid;
144     }
145 
146 }