diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index b48f93e30a..170092c3fd 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -1295,6 +1295,8 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements Exception error = null; try { result = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout); + } catch (SQLException ex) { + throw ex; } catch (Exception ex) { error = ex; } @@ -1327,7 +1329,14 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements } } + /** + * @deprecated + */ protected boolean testConnectionInternal(Connection conn) { + return testConnectionInternal(null, conn); + } + + protected boolean testConnectionInternal(DruidConnectionHolder holder, Connection conn) { String sqlFile = JdbcSqlStat.getContextSqlFile(); String sqlName = JdbcSqlStat.getContextSqlName(); @@ -1339,7 +1348,28 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements } try { if (validConnectionChecker != null) { - return validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout); + boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout); + long currentTimeMillis = System.currentTimeMillis(); + if (holder != null) { + holder.lastValidTimeMillis = currentTimeMillis; + } + + if (valid && JdbcConstants.MYSQL_DRIVER.equals(driverClass)) { // unexcepted branch + long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn); + long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs; + if (lastPacketReceivedTimeMs > 0 // + && mysqlIdleMillis >= timeBetweenEvictionRunsMillis) { + discardConnection(conn); + String errorMsg = "discard long time none received connection. " + + ", jdbcUrl : " + jdbcUrl + + ", jdbcUrl : " + jdbcUrl + + ", lastPacketReceivedIdleMillis : " + mysqlIdleMillis; + LOG.error(errorMsg); + return false; + } + } + + return valid; } if (conn.isClosed()) { @@ -1367,7 +1397,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements } return true; - } catch (Exception ex) { + } catch (Throwable ex) { // skip return false; } finally { diff --git a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java index 6fb3e548f1..b5ec95c3b6 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java +++ b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java @@ -51,6 +51,7 @@ public final class DruidConnectionHolder { protected final List statementEventListeners = new CopyOnWriteArrayList(); protected final long connectTimeMillis; protected volatile long lastActiveTimeMillis; + protected volatile long lastValidTimeMillis; private long useCount = 0; private long keepAliveCheckCount = 0; private long lastNotEmptyWaitNanos; diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index dfd4300706..53fb3ccbf8 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -23,17 +23,7 @@ import java.security.PrivilegedAction; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.ConcurrentModificationException; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.ServiceLoader; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; @@ -461,6 +451,17 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } } } + { + String property = properties.getProperty("druid.initConnectionSqls"); + if (property != null && property.length() > 0) { + try { + StringTokenizer tokenizer = new StringTokenizer(property, ";"); + setConnectionInitSqls(Collections.list(tokenizer)); + } catch (NumberFormatException e) { + LOG.error("illegal property 'druid.initConnectionSqls'", e); + } + } + } } public boolean isKillWhenSocketReadTimeout() { @@ -1175,27 +1176,39 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat return; } - String realDriverClassName = driver.getClass().getName(); - if (realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER) // - || realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER_6)) { - this.exceptionSorter = new MySqlExceptionSorter(); - } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER) - || realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER2)) { - this.exceptionSorter = new OracleExceptionSorter(); - } else if (realDriverClassName.equals("com.informix.jdbc.IfxDriver")) { - this.exceptionSorter = new InformixExceptionSorter(); - } else if (realDriverClassName.equals("com.sybase.jdbc2.jdbc.SybDriver")) { - this.exceptionSorter = new SybaseExceptionSorter(); + for (Class driverClass = driver.getClass();;) { + String realDriverClassName = driverClass.getName(); + if (realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER) // + || realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER_6)) { + this.exceptionSorter = new MySqlExceptionSorter(); + } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER) + || realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER2)) { + this.exceptionSorter = new OracleExceptionSorter(); + } else if (realDriverClassName.equals("com.informix.jdbc.IfxDriver")) { + this.exceptionSorter = new InformixExceptionSorter(); - } else if (realDriverClassName.equals(JdbcConstants.POSTGRESQL_DRIVER) - || realDriverClassName.equals(JdbcConstants.ENTERPRISEDB_DRIVER)) { - this.exceptionSorter = new PGExceptionSorter(); + } else if (realDriverClassName.equals("com.sybase.jdbc2.jdbc.SybDriver")) { + this.exceptionSorter = new SybaseExceptionSorter(); - } else if (realDriverClassName.equals("com.alibaba.druid.mock.MockDriver")) { - this.exceptionSorter = new MockExceptionSorter(); - } else if (realDriverClassName.contains("DB2")) { - this.exceptionSorter = new DB2ExceptionSorter(); + } else if (realDriverClassName.equals(JdbcConstants.POSTGRESQL_DRIVER) + || realDriverClassName.equals(JdbcConstants.ENTERPRISEDB_DRIVER)) { + this.exceptionSorter = new PGExceptionSorter(); + + } else if (realDriverClassName.equals("com.alibaba.druid.mock.MockDriver")) { + this.exceptionSorter = new MockExceptionSorter(); + } else if (realDriverClassName.contains("DB2")) { + this.exceptionSorter = new DB2ExceptionSorter(); + + } else { + Class superClass = driverClass.getSuperclass(); + if (superClass != null && superClass != Object.class) { + driverClass = superClass; + continue; + } + } + + break; } } @@ -1244,7 +1257,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } if (testOnBorrow) { - boolean validate = testConnectionInternal(poolableConnection.conn); + boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn); if (!validate) { if (LOG.isDebugEnabled()) { LOG.debug("skip not validate connection."); @@ -1275,25 +1288,14 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat if (idleMillis >= timeBetweenEvictionRunsMillis || idleMillis < 0 // unexcepted branch ) { - boolean validate = testConnectionInternal(poolableConnection.conn); + boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn); if (!validate) { if (LOG.isDebugEnabled()) { LOG.debug("skip not validate connection."); } discardConnection(realConnection); - continue; - } - - if (JdbcConstants.MYSQL_DRIVER.equals(driverClass)) { // unexcepted branch - long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(realConnection); - long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs; - if (lastPacketReceivedTimeMs > 0 // - && mysqlIdleMillis >= timeBetweenEvictionRunsMillis) { - discardConnection(realConnection); - LOG.warn("discard long time none received connection. millis : " + mysqlIdleMillis); - continue; - } + continue; } } } @@ -1567,7 +1569,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } if (testOnReturn) { - boolean validate = testConnectionInternal(physicalConnection); + boolean validate = testConnectionInternal(holder, physicalConnection); if (!validate) { JdbcUtils.close(physicalConnection); diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java index 24f6ad850a..4bee47b18f 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java @@ -54,28 +54,23 @@ import com.alibaba.druid.support.logging.LogFactory; * @author wenshao [szujobs@hotmail.com] */ public class DruidPooledConnection extends PoolableWrapper implements javax.sql.PooledConnection, Connection { - - private final static Log LOG = LogFactory.getLog(DruidPooledConnection.class); - - public static final int MAX_RECORD_SQL_COUNT = 10; - - protected Connection conn; - protected volatile DruidConnectionHolder holder; - protected TransactionInfo transactionInfo; - private final boolean dupCloseLogEnable; - protected volatile boolean traceEnable = false; - private boolean disable = false; - private boolean closed = false; - protected final Thread ownerThread; - private long connectedTimeMillis; - private long connectedTimeNano; - private volatile boolean running = false; - private volatile boolean abandoned = false; - - protected StackTraceElement[] connectStackTrace; - protected Throwable disableError = null; - - public ReentrantLock lock = new ReentrantLock(); + private final static Log LOG = LogFactory.getLog(DruidPooledConnection.class); + public static final int MAX_RECORD_SQL_COUNT = 10; + protected Connection conn; + protected volatile DruidConnectionHolder holder; + protected TransactionInfo transactionInfo; + private final boolean dupCloseLogEnable; + protected volatile boolean traceEnable = false; + private boolean disable = false; + private boolean closed = false; + protected final Thread ownerThread; + private long connectedTimeMillis; + private long connectedTimeNano; + private volatile boolean running = false; + private volatile boolean abandoned = false; + protected StackTraceElement[] connectStackTrace; + protected Throwable disableError = null; + public ReentrantLock lock = new ReentrantLock(); public DruidPooledConnection(DruidConnectionHolder holder){ super(holder.getConnection()); diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java index 093a4435f5..640450a874 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java @@ -262,13 +262,26 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { DruidConnectionHolder holder = conn.holder; DruidAbstractDataSource dataSource = holder.dataSource; + long currentTimeMillis = System.currentTimeMillis(); long lastActiveTimeMillis = holder.lastActiveTimeMillis; - long idleMillis = System.currentTimeMillis() - lastActiveTimeMillis; + long idleMillis = currentTimeMillis - lastActiveTimeMillis; + long lastValidIdleMillis = currentTimeMillis - holder.lastActiveTimeMillis; - LOG.error("CommunicationsException, druid version " + VERSION.getVersionNumber() + String errorMsg = "CommunicationsException, druid version " + VERSION.getVersionNumber() + ", jdbcUrl : " + dataSource.jdbcUrl + ", testWhileIdle " + dataSource.testWhileIdle - + ", idle millis " + idleMillis); + + ", idle millis " + idleMillis + + ", minIdle " + dataSource.minIdle + + ", poolingCount " + dataSource.getPoolingCount() + + ", timeBetweenEvictionRunsMillis " + dataSource.timeBetweenEvictionRunsMillis + + ", lastValidIdleMillis " + lastValidIdleMillis + + ", driver " + dataSource.driver.getClass().getName(); + + if (dataSource.exceptionSorter != null) { + errorMsg += ", exceptionSorter " + dataSource.exceptionSorter.getClass().getName(); + } + + LOG.error(errorMsg); } } diff --git a/src/main/java/com/alibaba/druid/pool/vendor/MySqlValidConnectionChecker.java b/src/main/java/com/alibaba/druid/pool/vendor/MySqlValidConnectionChecker.java index 4c74d0c713..e475dfd122 100644 --- a/src/main/java/com/alibaba/druid/pool/vendor/MySqlValidConnectionChecker.java +++ b/src/main/java/com/alibaba/druid/pool/vendor/MySqlValidConnectionChecker.java @@ -16,9 +16,11 @@ package com.alibaba.druid.pool.vendor; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.ResultSet; +import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; @@ -101,7 +103,15 @@ public class MySqlValidConnectionChecker extends ValidConnectionCheckerAdapter i validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT; } - ping.invoke(conn, true, validationQueryTimeout * 1000); + try { + ping.invoke(conn, true, validationQueryTimeout * 1000); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof SQLException) { + throw (SQLException) cause; + } + throw e; + } return true; } }