Author: psteitz
Date: Wed Jan 28 13:33:18 2015
New Revision: 1655301
URL: http://svn.apache.org/r1655301
Log:
Added fastFailValidation property to PC, BDS. JIRA: DBCP-427.
Modified:
commons/proper/dbcp/trunk/src/changes/changes.xml
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/Utils.java
commons/proper/dbcp/trunk/src/main/resources/org/apache/commons/dbcp2/LocalStrings.properties
commons/proper/dbcp/trunk/src/site/xdoc/configuration.xml
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestPoolableConnection.java
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TesterConnection.java
Modified: commons/proper/dbcp/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/changes/changes.xml?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/changes/changes.xml (original)
+++ commons/proper/dbcp/trunk/src/changes/changes.xml Wed Jan 28 13:33:18 2015
@@ -94,6 +94,11 @@ The <action> type attribute can be add,u
<action issue="DBCP-423" dev="psteitz" type="update">
Made Datasources implement AutoCloseable.
</action>
+ <action issue="DBCP-427" dev="psteitz" type="add" due-to="Vladimir
Konkov">
+ Added fastFailValidation property to PooloableConnection, configurable
in
+ BasicDataSource. When set to true, connections that have previously
thrown
+ fatal disconnection errors will fail validation immediately (no driver
calls).
+ </action>
</release>
<release version="2.0.1" date="24 May 2014" description="This is a bug fix
release.">
<action dev="markt" type="fix">
Modified:
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java
(original)
+++
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java
Wed Jan 28 13:33:18 2015
@@ -31,8 +31,10 @@ import java.sql.SQLFeatureNotSupportedEx
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Properties;
+import java.util.Set;
import java.util.logging.Logger;
import javax.management.InstanceAlreadyExistsException;
@@ -1267,13 +1269,15 @@ public class BasicDataSource implements
public long getMaxConnLifetimeMillis() {
return maxConnLifetimeMillis;
}
-
+
private boolean logExpiredConnections = true;
-
+
/**
* When {@link #getMaxConnLifetimeMillis()} is set to limit connection
lifetime,
* this property determines whether or not log messages are generated when
the
- * pool closes connections due to maximum lifetime exceeded.
+ * pool closes connections due to maximum lifetime exceeded.
+ *
+ * @since 2.1
*/
@Override
public boolean isLogExpiredConnections() {
@@ -1292,7 +1296,7 @@ public class BasicDataSource implements
public void setMaxConnLifetimeMillis(long maxConnLifetimeMillis) {
this.maxConnLifetimeMillis = maxConnLifetimeMillis;
}
-
+
/**
* When {@link #getMaxConnLifetimeMillis()} is set to limit connection
lifetime,
* this property determines whether or not log messages are generated when
the
@@ -1349,7 +1353,6 @@ public class BasicDataSource implements
this.enableAutoCommitOnReturn = enableAutoCommitOnReturn;
}
-
private boolean rollbackOnReturn = true;
/**
@@ -1370,6 +1373,97 @@ public class BasicDataSource implements
this.rollbackOnReturn = rollbackOnReturn;
}
+ private volatile Set<String> disconnectionSqlCodes;
+
+ /**
+ * Returns the set of SQL_STATE codes considered to signal fatal
conditions.
+ * @return fatal disconnection state codes
+ * @see #setDisconnectionSqlCodes(Collection)
+ * @since 2.1
+ */
+ public Set<String> getDisconnectionSqlCodes() {
+ Set<String> result = disconnectionSqlCodes;
+ if (result == null) {
+ return Collections.emptySet();
+ }
+ return result;
+ }
+
+ /**
+ * Provides the same data as {@link #getDisconnectionSqlCodes} but in an
+ * array so it is accessible via JMX.
+ * @since 2.1
+ */
+ @Override
+ public String[] getDisconnectionSqlCodesAsArray() {
+ Collection<String> result = getDisconnectionSqlCodes();
+ return result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Sets the SQL_STATE codes considered to signal fatal conditions.
+ * <p>
+ * Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES}
+ * (plus anything starting with {@link
Utils#DISCONNECTION_SQL_CODE_PREFIX}).
+ * If this property is non-null and {@link #isFastFailValidation()} is
+ * {@code true}, whenever connections created by this datasource generate
exceptions
+ * with SQL_STATE codes in this list, they will be marked as "fatally
disconnected"
+ * and subsequent validations will fail fast (no attempt at isValid or
validation
+ * query).</p>
+ * <p>
+ * If {@link #isFastFailValidation()} is {@code false} setting this
property has no
+ * effect.</p>
+ * <p>
+ * Note: this method currently has no effect once the pool has been
+ * initialized. The pool is initialized the first time one of the
+ * following methods is invoked: {@code getConnection, setLogwriter,
+ * setLoginTimeout, getLoginTimeout, getLogWriter}.</p>
+ *
+ * @param disconnectionSqlCodes SQL_STATE codes considered to signal fatal
conditions
+ * @since 2.1
+ */
+ public void setDisconnectionSqlCodes(Collection<String>
disconnectionSqlCodes) {
+ if (disconnectionSqlCodes != null && disconnectionSqlCodes.size() > 0)
{
+ HashSet<String> newVal = null;
+ for (String s : disconnectionSqlCodes) {
+ if (s != null && s.trim().length() > 0) {
+ if (newVal == null) {
+ newVal = new HashSet<String>();
+ }
+ newVal.add(s);
+ }
+ }
+ this.disconnectionSqlCodes = newVal;
+ } else {
+ this.disconnectionSqlCodes = null;
+ }
+ }
+
+ private boolean fastFailValidation;
+
+ /**
+ * True means that validation will fail immediately for connections that
+ * have previously thrown SQLExceptions with SQL_STATE indicating fatal
+ * disconnection errors.
+ *
+ * @return true if connections created by this datasource will fast fail
validation.
+ * @see #setDisconnectionSqlCodes(Collection)
+ * @since 2.1
+ */
+ @Override
+ public boolean isFastFailValidation() {
+ return fastFailValidation;
+ }
+
+ /**
+ * @see #isFastFailValidation()
+ * @param fastFailValidation true means connections created by this
factory will
+ * fast fail validation
+ * @since 2.1
+ */
+ public void setFastFailValidation(boolean fastFailValidation) {
+ this.fastFailValidation = fastFailValidation;
+ }
// ----------------------------------------------------- Instance Variables
Modified:
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
(original)
+++
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceFactory.java
Wed Jan 28 13:33:18 2015
@@ -98,6 +98,12 @@ public class BasicDataSourceFactory impl
private static final String PROP_ROLLBACK_ON_RETURN = "rollbackOnReturn";
private static final String PROP_ENABLE_AUTOCOMMIT_ON_RETURN =
"enableAutoCommitOnReturn";
private static final String PROP_DEFAULT_QUERYTIMEOUT =
"defaultQueryTimeout";
+ private static final String PROP_FASTFAIL_VALIDATION =
"fastFailValidation";
+
+ /**
+ * Value string must be of the form [STATE_CODE;]*
+ */
+ private static final String PROP_DISCONNECTION_SQL_CODES =
"disconnectionSqlCodes";
private static final String[] ALL_PROPERTIES = {
PROP_DEFAULTAUTOCOMMIT,
@@ -139,7 +145,9 @@ public class BasicDataSourceFactory impl
PROP_LOGEXPIREDCONNECTIONS,
PROP_ROLLBACK_ON_RETURN,
PROP_ENABLE_AUTOCOMMIT_ON_RETURN,
- PROP_DEFAULT_QUERYTIMEOUT
+ PROP_DEFAULT_QUERYTIMEOUT,
+ PROP_FASTFAIL_VALIDATION,
+ PROP_DISCONNECTION_SQL_CODES
};
// -------------------------------------------------- ObjectFactory Methods
@@ -389,15 +397,7 @@ public class BasicDataSourceFactory impl
value = properties.getProperty(PROP_CONNECTIONINITSQLS);
if (value != null) {
- StringTokenizer tokenizer = new StringTokenizer(value, ";");
- // Have to jump through these hoops as StringTokenizer implements
- // Enumeration<Object> rather than Enumeration<String>
- Collection<String> tokens =
- new ArrayList<>(tokenizer.countTokens());
- while (tokenizer.hasMoreTokens()) {
- tokens.add(tokenizer.nextToken());
- }
- dataSource.setConnectionInitSqls(tokens);
+ dataSource.setConnectionInitSqls(parseList(value, ';'));
}
value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
@@ -414,7 +414,7 @@ public class BasicDataSourceFactory impl
if (value != null) {
dataSource.setMaxConnLifetimeMillis(Long.parseLong(value));
}
-
+
value = properties.getProperty(PROP_LOGEXPIREDCONNECTIONS);
if (value != null) {
dataSource.setLogExpiredConnections(Boolean.valueOf(value).booleanValue());
@@ -440,6 +440,15 @@ public class BasicDataSourceFactory impl
dataSource.setDefaultQueryTimeout(Integer.valueOf(value));
}
+ value = properties.getProperty(PROP_FASTFAIL_VALIDATION);
+ if (value != null) {
+
dataSource.setFastFailValidation(Boolean.valueOf(value).booleanValue());
+ }
+
+ value = properties.getProperty(PROP_DISCONNECTION_SQL_CODES);
+ if (value != null) {
+ dataSource.setDisconnectionSqlCodes(parseList(value, ','));
+ }
// DBCP-215
// Trick to make sure that initialSize connections are created
@@ -465,4 +474,20 @@ public class BasicDataSourceFactory impl
}
return p;
}
+
+ /**
+ * Parse list of property values from a delimited string
+ * @param value delimited list of values
+ * @param delimiter character used to separate values in the list
+ * @return String Collection of values
+ */
+ private static Collection<String> parseList(String value, char delimiter) {
+ StringTokenizer tokenizer = new StringTokenizer(value,
Character.toString(delimiter));
+ Collection<String> tokens =
+ new ArrayList<String>(tokenizer.countTokens());
+ while (tokenizer.hasMoreTokens()) {
+ tokens.add(tokenizer.nextToken());
+ }
+ return tokens;
+ }
}
Modified:
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java
(original)
+++
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java
Wed Jan 28 13:33:18 2015
@@ -215,10 +215,11 @@ public interface BasicDataSourceMXBean {
* @return {@link BasicDataSource#getMaxConnLifetimeMillis()}
*/
long getMaxConnLifetimeMillis();
-
+
/**
* See {@link BasicDataSource#isLogExpiredConnections()}
* @return {@link BasicDataSource#isLogExpiredConnections()}
+ * @since 2.1
*/
boolean isLogExpiredConnections();
@@ -251,4 +252,18 @@ public interface BasicDataSourceMXBean {
* @return {@link BasicDataSource#isClosed()}
*/
boolean isClosed();
+
+ /**
+ * See {@link BasicDataSource#isFastFailValidation()}
+ * @return {@link BasicDataSource#isFastFailValidation()}
+ * @since 2.1
+ */
+ boolean isFastFailValidation();
+
+ /**
+ * See {@link BasicDataSource#getDisconnectionSqlCodesAsArray()}
+ * @return {@link BasicDataSource#getDisconnectionSqlCodesAsArray()}
+ * @since 2.1
+ */
+ String[] getDisconnectionSqlCodesAsArray();
}
Modified:
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java
(original)
+++
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnection.java
Wed Jan 28 13:33:18 2015
@@ -21,6 +21,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.Collection;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
@@ -45,7 +46,7 @@ import org.apache.commons.pool2.ObjectPo
public class PoolableConnection extends DelegatingConnection<Connection>
implements PoolableConnectionMXBean {
- private static MBeanServer MBEAN_SERVER = null;
+ private static MBeanServer MBEAN_SERVER = null;
static {
try {
@@ -56,7 +57,7 @@ public class PoolableConnection extends
}
/** The pool to which I should return. */
- private ObjectPool<PoolableConnection> _pool = null;
+ private final ObjectPool<PoolableConnection> _pool;
private final ObjectName _jmxName;
@@ -66,15 +67,38 @@ public class PoolableConnection extends
private String lastValidationSql = null;
/**
+ * Indicate that unrecoverable SQLException was thrown when using this
connection.
+ * Such a connection should be considered broken and not pass validation
in the future.
+ */
+ private boolean _fatalSqlExceptionThrown = false;
+
+ /**
+ * SQL_STATE codes considered to signal fatal conditions. Overrides the
+ * defaults in {@link Utils#DISCONNECTION_SQL_CODES} (plus anything
starting
+ * with {@link Utils#DISCONNECTION_SQL_CODE_PREFIX}).
+ */
+ private final Collection<String> _disconnectionSqlCodes;
+
+ /** Whether or not to fast fail validation after fatal connection errors */
+ private final boolean _fastFailValidation;
+
+ /**
*
* @param conn my underlying connection
* @param pool the pool to which I should return when closed
+ * @param jmxName JMX name
+ * @param disconnectSqlCodes SQL_STATE codes considered fatal
disconnection errors
+ * @param fastFailValidation true means fatal disconnection errors cause
subsequent
+ * validations to fail immediately (no attempt to run query or
isValid)
*/
public PoolableConnection(Connection conn,
- ObjectPool<PoolableConnection> pool, ObjectName jmxName) {
+ ObjectPool<PoolableConnection> pool, ObjectName jmxName,
Collection<String> disconnectSqlCodes,
+ boolean fastFailValidation) {
super(conn);
_pool = pool;
_jmxName = jmxName;
+ _disconnectionSqlCodes = disconnectSqlCodes;
+ _fastFailValidation = fastFailValidation;
if (jmxName != null) {
try {
@@ -86,6 +110,17 @@ public class PoolableConnection extends
}
}
+ /**
+ *
+ * @param conn my underlying connection
+ * @param pool the pool to which I should return when closed
+ * @param jmxName JMX name
+ */
+ public PoolableConnection(Connection conn,
+ ObjectPool<PoolableConnection> pool, ObjectName jmxName) {
+ this(conn, pool, jmxName, null, false);
+ }
+
@Override
protected void passivate() throws SQLException {
@@ -218,8 +253,29 @@ public class PoolableConnection extends
return toString();
}
-
+ /**
+ * Validates the connection, using the following algorithm:
+ * <ol>
+ * <li>If {@code fastFailValidation} (constructor argument) is {@code
true} and
+ * this connection has previously thrown a fatal disconnection
exception,
+ * a {@code SQLException} is thrown. </li>
+ * <li>If {@code sql} is null, the driver's
+ * #{@link Connection#isValid(int) isvalid(timeout)} is called.
+ * If it returns {@code false}, {@code SQLException} is thrown;
+ * otherwise, this method returns successfully.</li>
+ * <li>If {@code sql} is not null, it is executed as a query and if the
resulting
+ * {@code ResultSet} contains at least one row, this method returns
+ * successfully. If not, {@code SQLException} is thrown.</li>
+ * </ol>
+ * @param sql validation query
+ * @param timeout validation timeout
+ * @throws SQLException if validation fails or an SQLException occurs
during validation
+ */
public void validate(String sql, int timeout) throws SQLException {
+ if (_fastFailValidation && _fatalSqlExceptionThrown) {
+ throw new
SQLException(Utils.getMessage("poolableConnection.validate.fastFail"));
+ }
+
if (sql == null || sql.length() == 0) {
if (timeout < 0) {
timeout = 0;
@@ -250,5 +306,38 @@ public class PoolableConnection extends
throw sqle;
}
}
+
+ /**
+ * Checks the SQLState of the input exception and any nested SQLExceptions
it wraps.
+ * <p>
+ * If {@link #getDisconnectSqlCodes() disconnectSQLCodes} has been set,
sql states
+ * are compared to those in the configured list of fatal exception codes.
If this
+ * property is not set, codes are compared against the default codes in
+ * #{@link Utils.DISCONNECTION_SQL_CODES} and in this case anything
starting with
+ * #{link Utils.DISCONNECTION_SQL_CODE_PREFIX} is considered a
disconnection.</p>
+ *
+ * @param e SQLException to be examined
+ * @return true if the exception signals a disconnection
+ */
+ private boolean isDisconnectionSqlException(SQLException e) {
+ boolean fatalException = false;
+ String sqlState = e.getSQLState();
+ if (sqlState != null) {
+ fatalException = _disconnectionSqlCodes == null ?
sqlState.startsWith(Utils.DISCONNECTION_SQL_CODE_PREFIX)
+ || Utils.DISCONNECTION_SQL_CODES.contains(sqlState) :
_disconnectionSqlCodes.contains(sqlState);
+ if (!fatalException) {
+ if (e.getNextException() != null) {
+ fatalException =
isDisconnectionSqlException(e.getNextException());
+ }
+ }
+ }
+ return fatalException;
+ }
+
+ @Override
+ protected void handleException(SQLException e) throws SQLException {
+ _fatalSqlExceptionThrown |= isDisconnectionSqlException(e);
+ super.handleException(e);
+ }
}
Modified:
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
(original)
+++
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/PoolableConnectionFactory.java
Wed Jan 28 13:33:18 2015
@@ -190,7 +190,6 @@ public class PoolableConnectionFactory
this.rollbackOnReturn = rollbackOnReturn;
}
-
public Integer getDefaultQueryTimeout() {
return defaultQueryTimeout;
}
@@ -199,6 +198,58 @@ public class PoolableConnectionFactory
this.defaultQueryTimeout = defaultQueryTimeout;
}
+ /**
+ * SQL_STATE codes considered to signal fatal conditions.
+ * <p>
+ * Overrides the defaults in {@link Utils#DISCONNECTION_SQL_CODES}
+ * (plus anything starting with {@link
Utils#DISCONNECTION_SQL_CODE_PREFIX}).
+ * If this property is non-null and {@link #isFastFailValidation()} is
+ * {@code true}, whenever connections created by this factory generate
exceptions
+ * with SQL_STATE codes in this list, they will be marked as "fatally
disconnected"
+ * and subsequent validations will fail fast (no attempt at isValid or
validation
+ * query).</p>
+ * <p>
+ * If {@link #isFastFailValidation()} is {@code false} setting this
property has no
+ * effect.</p>
+ *
+ * @return SQL_STATE codes overriding defaults
+ * @since 2.1
+ */
+ public Collection<String> getDisconnectionSqlCodes() {
+ return _disconnectionSqlCodes;
+ }
+
+ /**
+ * @see #getDisconnectionSqlCodes()
+ * @param disconnectionSqlCodes
+ * @since 2.1
+ */
+ public void setDisconnectionSqlCodes(Collection<String>
disconnectionSqlCodes) {
+ _disconnectionSqlCodes = disconnectionSqlCodes;
+ }
+
+ /**
+ * True means that validation will fail immediately for connections that
+ * have previously thrown SQLExceptions with SQL_STATE indicating fatal
+ * disconnection errors.
+ *
+ * @return true if connections created by this factory will fast fail
validation.
+ * @see #setDisconnectionSqlCodes(Collection)
+ * @since 2.1
+ */
+ public boolean isFastFailValidation() {
+ return _fastFailValidation;
+ }
+
+ /**
+ * @see #isFastFailValidation()
+ * @param fastFailValidation true means connections created by this
factory will
+ * fast fail validation
+ * @since 2.1
+ */
+ public void setFastFailValidation(boolean fastFailValidation) {
+ _fastFailValidation = fastFailValidation;
+ }
@Override
public PooledObject<PoolableConnection> makeObject() throws Exception {
@@ -251,7 +302,8 @@ public class PoolableConnectionFactory
Constants.JMX_CONNECTION_BASE_EXT + connIndex);
}
- PoolableConnection pc = new PoolableConnection(conn,_pool,
connJmxName);
+ PoolableConnection pc = new PoolableConnection(conn,_pool, connJmxName,
+ _disconnectionSqlCodes,
_fastFailValidation);
return new DefaultPooledObject<>(pc);
}
@@ -396,6 +448,8 @@ public class PoolableConnectionFactory
private volatile String _validationQuery = null;
private volatile int _validationQueryTimeout = -1;
private Collection<String> _connectionInitSqls = null;
+ private Collection<String> _disconnectionSqlCodes = null;
+ private boolean _fastFailValidation = false;
private volatile ObjectPool<PoolableConnection> _pool = null;
private Boolean _defaultReadOnly = null;
private Boolean _defaultAutoCommit = null;
Modified:
commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/Utils.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/Utils.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/Utils.java
(original)
+++ commons/proper/dbcp/trunk/src/main/java/org/apache/commons/dbcp2/Utils.java
Wed Jan 28 13:33:18 2015
@@ -22,7 +22,9 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.MessageFormat;
+import java.util.HashSet;
import java.util.ResourceBundle;
+import java.util.Set;
/**
* Utility methods.
@@ -37,6 +39,31 @@ public final class Utils {
public static final boolean IS_SECURITY_ENABLED =
System.getSecurityManager() != null;
+ /** Any SQL_STATE starting with this value is considered a fatal
disconnect */
+ public static final String DISCONNECTION_SQL_CODE_PREFIX = "08";
+
+ /**
+ * SQL codes of fatal connection errors.
+ * <ul>
+ * <li>57P01 (ADMIN SHUTDOWN)</li>
+ * <li>57P02 (CRASH SHUTDOWN)</li>
+ * <li>57P03 (CANNOT CONNECT NOW)</li>
+ * <li>01002 (SQL92 disconnect error)</li>
+ * <li>JZ0C0 (Sybase disconnect error)</li>
+ * <li>JZ0C1 (Sybase disconnect error)</li>
+ * </ul>
+ */
+ public static final Set<String> DISCONNECTION_SQL_CODES;
+
+ static {
+ DISCONNECTION_SQL_CODES = new HashSet<String>();
+ DISCONNECTION_SQL_CODES.add("57P01"); // ADMIN SHUTDOWN
+ DISCONNECTION_SQL_CODES.add("57P02"); // CRASH SHUTDOWN
+ DISCONNECTION_SQL_CODES.add("57P03"); // CANNOT CONNECT NOW
+ DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
+ DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
+ DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
+ }
private Utils() {
// not instantiable
Modified:
commons/proper/dbcp/trunk/src/main/resources/org/apache/commons/dbcp2/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/main/resources/org/apache/commons/dbcp2/LocalStrings.properties?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/main/resources/org/apache/commons/dbcp2/LocalStrings.properties
(original)
+++
commons/proper/dbcp/trunk/src/main/resources/org/apache/commons/dbcp2/LocalStrings.properties
Wed Jan 28 13:33:18 2015
@@ -15,9 +15,11 @@
connectionFactory.lifetimeExceeded=The lifetime of the connection [{0}]
milliseconds exceeds the maximum permitted value of [{1}] milliseconds
-poolableConnectionFactory.validateObject.fail=Failed to validate a poolable
connection
+poolableConnectionFactory.validateObject.fail=Failed to validate a poolable
connection.
-swallowedExceptionLogger.onSwallowedException=An internal object pool
swallowed an Exception
+poolableConnection.validate.fastFail=Fatal SQLException was thrown previously
on this connection.
+
+swallowedExceptionLogger.onSwallowedException=An internal object pool
swallowed an Exception.
poolingDataSource.factoryConfig=PoolableConnectionFactory not linked to pool.
Calling setPool() to fix the configuration.
Modified: commons/proper/dbcp/trunk/src/site/xdoc/configuration.xml
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/site/xdoc/configuration.xml?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/site/xdoc/configuration.xml (original)
+++ commons/proper/dbcp/trunk/src/site/xdoc/configuration.xml Wed Jan 28
13:33:18 2015
@@ -363,8 +363,8 @@ value less than the maximum number of cu
</source>
<p>
<img src="images/icon_info_sml.gif"/>
-Default is false, it is a potential dangerous operation and misbehaving
programs can do harmfull things. (closing the underlying or continue using it
when the guarded connection is already closed)
-Be carefull and only use when you need direct access to driver specific
extentions.
+Default is false, it is a potential dangerous operation and misbehaving
programs can do harmful things. (closing the underlying or continue using it
when the guarded connection is already closed)
+Be careful and only use when you need direct access to driver specific
extensions.
</p>
<p>
<img src="images/icon_warning_sml.gif"/>
@@ -420,6 +420,39 @@ default (300 sec). Traversing a resultse
or CallableStatement or using one of these to execute a query (using one of
the execute methods) resets
the lastUsed property of the parent connection.
</p>
+<table>
+<hr><th>Parameter</th><th>Default</th><th>Description</th></hr>
+<tr>
+ <td>fastFailValidation</td>
+ <td>false</td>
+ <td>
+ When this property is true, validation fails fast for connections that
have
+ thrown "fatal" SQLExceptions. Requests to validate disconnected
connections
+ fail immediately, with no call to the driver's isValid method or attempt
to
+ execute a validation query.<br/>
+ The SQL_STATE codes considered to signal fatal errors are by default the
following:
+ <ul>
+ <li>57P01 (ADMIN SHUTDOWN)</li>
+ <li>57P02 (CRASH SHUTDOWN)</li>
+ <li>57P03 (CANNOT CONNECT NOW)</li>
+ <li>01002 (SQL92 disconnect error)</li>
+ <li>JZ0C0 (Sybase disconnect error)</li>
+ <li>JZ0C1 (Sybase disconnect error)</li>
+ <li>Any SQL_STATE code that starts with "08"</li>
+ </ul>
+ To override this default set of disconnection codes, set the
+ <code>disconnectionSqlCodes</code> property.
+ </td>
+</tr>
+<tr>
+ <td>disconnectionSqlCodes</td>
+ <td>null</td>
+ <td>Comma-delimited list of SQL_STATE codes considered to signal fatal
disconnection
+ errors. Setting this property has no effect unless
+ <code>fastFailValidation</code> is set to <code>true.</code>
+ </td>
+</tr>
+</table>
</section>
Modified:
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java
(original)
+++
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestBasicDataSourceFactory.java
Wed Jan 28 13:33:18 2015
@@ -19,6 +19,7 @@ package org.apache.commons.dbcp2;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.sql.Connection;
import java.util.Properties;
@@ -74,6 +75,8 @@ public class TestBasicDataSourceFactory
properties.setProperty("poolPreparedStatements", "true");
properties.setProperty("maxOpenPreparedStatements", "10");
properties.setProperty("lifo", "true");
+ properties.setProperty("fastFailValidation", "true");
+ properties.setProperty("disconnectionSqlCodes", "XXX,YYY");
BasicDataSource ds =
BasicDataSourceFactory.createDataSource(properties);
@@ -111,5 +114,8 @@ public class TestBasicDataSourceFactory
assertEquals(true, ds.isPoolPreparedStatements());
assertEquals(10, ds.getMaxOpenPreparedStatements());
assertEquals(true, ds.getLifo());
+ assertEquals(true, ds.isFastFailValidation());
+ assertTrue(ds.getDisconnectionSqlCodes().contains("XXX"));
+ assertTrue(ds.getDisconnectionSqlCodes().contains("YYY"));
}
}
Modified:
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestPoolableConnection.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestPoolableConnection.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestPoolableConnection.java
(original)
+++
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TestPoolableConnection.java
Wed Jan 28 13:33:18 2015
@@ -18,12 +18,14 @@ package org.apache.commons.dbcp2;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import java.sql.Connection;
import java.sql.SQLException;
+import java.util.ArrayList;
-import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -34,7 +36,7 @@ import org.junit.Test;
*/
public class TestPoolableConnection {
- private ObjectPool<PoolableConnection> pool = null;
+ private GenericObjectPool<PoolableConnection> pool = null;
@Before
public void setUp() throws Exception {
@@ -45,9 +47,15 @@ public class TestPoolableConnection {
factory.setDefaultAutoCommit(Boolean.TRUE);
factory.setDefaultReadOnly(Boolean.TRUE);
+
pool = new GenericObjectPool<>(factory);
factory.setPool(pool);
}
+
+ @After
+ public void tearDown() {
+ pool.close();
+ }
@Test
public void testConnectionPool() throws Exception {
@@ -108,4 +116,82 @@ public class TestPoolableConnection {
Assert.assertEquals(0, pool.getNumActive());
Assert.assertEquals(1, pool.getNumIdle());
}
+
+ @Test
+ public void testFastFailValidation() throws Exception {
+ pool.setTestOnReturn(true);
+ PoolableConnectionFactory factory = (PoolableConnectionFactory)
pool.getFactory();
+ factory.setFastFailValidation(true);
+ PoolableConnection conn = pool.borrowObject();
+ TesterConnection nativeConnection = (TesterConnection)
conn.getInnermostDelegate();
+
+ // Set up non-fatal exception
+ nativeConnection.setFailure(new SQLException("Not fatal error.",
"Invalid syntax."));
+ try {
+ conn.createStatement();
+ fail("Should throw SQL exception.");
+ } catch (SQLException ignored) {
+ // cleanup failure
+ nativeConnection.setFailure(null);
+ }
+
+ // validate should not fail - error was not fatal and condition was
cleaned up
+ conn.validate("SELECT 1", 1000);
+
+ // now set up fatal failure
+ nativeConnection.setFailure(new SQLException("Fatal connection
error.", "01002"));
+
+ try {
+ conn.createStatement();
+ fail("Should throw SQL exception.");
+ } catch (SQLException ignored) {
+ // cleanup failure
+ nativeConnection.setFailure(null);
+ }
+
+ // validate should now fail because of previous fatal error, despite
cleanup
+ try {
+ conn.validate("SELECT 1", 1000);
+ fail("Should throw SQL exception on validation.");
+ } catch (SQLException notValid){
+ // expected - fatal error && fastFailValidation
+ }
+
+ // verify that bad connection does not get returned to the pool
+ conn.close(); // testOnReturn triggers validate, which should fail
+ assertEquals("The pool should have no active connections",
+ 0, pool.getNumActive());
+ assertEquals("The pool should have no idle connections",
+ 0, pool.getNumIdle());
+ }
+
+ @Test
+ public void testFastFailValidationCustomCodes() throws Exception {
+ pool.setTestOnReturn(true);
+ PoolableConnectionFactory factory = (PoolableConnectionFactory)
pool.getFactory();
+ factory.setFastFailValidation(true);
+ ArrayList<String> disconnectionSqlCodes = new ArrayList<String>();
+ disconnectionSqlCodes.add("XXX");
+ factory.setDisconnectionSqlCodes(disconnectionSqlCodes);
+ PoolableConnection conn = pool.borrowObject();
+ TesterConnection nativeConnection = (TesterConnection)
conn.getInnermostDelegate();
+
+ // Set up fatal exception
+ nativeConnection.setFailure(new SQLException("Fatal connection
error.", "XXX"));
+
+ try {
+ conn.createStatement();
+ fail("Should throw SQL exception.");
+ } catch (SQLException ignored) {
+ // cleanup failure
+ nativeConnection.setFailure(null);
+ }
+
+ // verify that bad connection does not get returned to the pool
+ conn.close(); // testOnReturn triggers validate, which should fail
+ assertEquals("The pool should have no active connections",
+ 0, pool.getNumActive());
+ assertEquals("The pool should have no idle connections",
+ 0, pool.getNumIdle());
+ }
}
Modified:
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TesterConnection.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TesterConnection.java?rev=1655301&r1=1655300&r2=1655301&view=diff
==============================================================================
---
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TesterConnection.java
(original)
+++
commons/proper/dbcp/trunk/src/test/java/org/apache/commons/dbcp2/TesterConnection.java
Wed Jan 28 13:33:18 2015
@@ -234,7 +234,11 @@ public class TesterConnection implements
protected void checkFailure() throws SQLException {
if (failure != null) {
- throw new SQLException("TesterConnection failure", failure);
+ if(failure instanceof SQLException) {
+ throw (SQLException)failure;
+ } else {
+ throw new SQLException("TesterConnection failure", failure);
+ }
}
}