From 9876b1987400a751e1c4f213263b259096029ceb Mon Sep 17 00:00:00 2001 From: Zhanhui Li Date: Tue, 3 Jun 2014 23:28:13 +0800 Subject: [PATCH 1/6] Add a new profile: mac-profile-oracle-jdk to support jdk 1.7+ on mac --- pom.xml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pom.xml b/pom.xml index 1f3364104d..5e554150ab 100644 --- a/pom.xml +++ b/pom.xml @@ -310,6 +310,33 @@ + + + mac-profile-oracle-jdk + + false + + ${java.home}/../lib/jconsole.jar + + + + + com.alibaba + jconsole + 1.8.0 + system + ${java.home}/../lib/jconsole.jar + + + + com.alibaba + tools + 1.8.0 + system + ${java.home}/../lib/tools.jar + + + From b72056f18133fb2dc80a98a48aae71733c7257af Mon Sep 17 00:00:00 2001 From: gitriver Date: Wed, 4 Jun 2014 21:35:02 +0800 Subject: [PATCH 2/6] =?UTF-8?q?ServletPathMatcher=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8C=85=E5=90=AB*=E7=9A=84=E6=A8=A1=E5=BC=8F=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=20pattern:=20/druid/*/index.html=20source:/druid/admi?= =?UTF-8?q?n/index.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../druid/util/ServletPathMatcher.java | 83 +++++++++++-------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java index 3bd6090c3d..73dcedb9bb 100644 --- a/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java +++ b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java @@ -17,45 +17,56 @@ package com.alibaba.druid.util; public class ServletPathMatcher implements PatternMatcher { private final static ServletPathMatcher instance = new ServletPathMatcher(); - + public static ServletPathMatcher getInstance() { return instance; } - /** - *

- * three type: endsWithMatch(eg. /xxx*=/xxx/xyz), startsWithMatch(eg. *.xxx=abc.xxx), equals(eg. /xxx=/xxx). - *

- * Notice: *xxx* will match *xxxyyyy. endsWithMatch first. - */ - @Override - public boolean matches(String pattern, String source) { - if (pattern == null || source == null) { - return false; - } - pattern = pattern.trim(); - source = source.trim(); - if (pattern.endsWith("*")) { - // pattern: /druid* source:/druid/index.html - int length = pattern.length() - 1; - if (source.length() >= length) { - if (pattern.substring(0, length).equals(source.substring(0, length))) { - return true; - } - } - } else if (pattern.startsWith("*")) { - // pattern: *.html source:/xx/xx.html - int length = pattern.length() - 1; - if (source.length() >= length && source.endsWith(pattern.substring(1))) { - return true; - } - } else { - // pattern: /druid/index.html source:/druid/index.html - if (pattern.equals(source)) { - return true; - } - } - return false; - } + /** + *

+ * three type: endsWithMatch(eg. /xxx*=/xxx/xyz), startsWithMatch(eg. + * *.xxx=abc.xxx), equals(eg. /xxx=/xxx). + *

+ * Notice: *xxx* will match *xxxyyyy. endsWithMatch first. + */ + @Override + public boolean matches(String pattern, String source) { + if (pattern == null || source == null) { + return false; + } + pattern = pattern.trim(); + source = source.trim(); + if (pattern.endsWith("*")) { + // pattern: /druid* source:/druid/index.html + int length = pattern.length() - 1; + if (source.length() >= length) { + if (pattern.substring(0, length).equals( + source.substring(0, length))) { + return true; + } + } + } else if (pattern.startsWith("*")) { + // pattern: *.html source:/xx/xx.html + int length = pattern.length() - 1; + if (source.length() >= length + && source.endsWith(pattern.substring(1))) { + return true; + } + } else if (pattern.contains("*")) { + // pattern: /druid/*/index.html source:/druid/admin/index.html + int start = pattern.indexOf("*"); + int end = pattern.lastIndexOf("*"); + if (source.startsWith(pattern.substring(0, start)) + && source.endsWith(pattern.substring(end + 1))) { + return true; + } + } else { + // pattern: /druid/index.html source:/druid/index.html + if (pattern.equals(source)) { + return true; + } + } + return false; + } } From 5ababcd422904e010e47aa5b059bf313dcdd6ab3 Mon Sep 17 00:00:00 2001 From: gitriver Date: Wed, 4 Jun 2014 21:39:50 +0800 Subject: [PATCH 3/6] =?UTF-8?q?ServletPathMatcher=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8C=85=E5=90=AB*=E7=9A=84=E6=A8=A1=E5=BC=8F=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=20pattern:=20/druid/*/index.html=20source:/druid/admi?= =?UTF-8?q?n/index.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/alibaba/druid/util/ServletPathMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java index 73dcedb9bb..401c45a0ff 100644 --- a/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java +++ b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java @@ -53,7 +53,7 @@ public class ServletPathMatcher implements PatternMatcher { return true; } } else if (pattern.contains("*")) { - // pattern: /druid/*/index.html source:/druid/admin/index.html + // pattern: /druid/*/index.html source:/druid/admin/index.html int start = pattern.indexOf("*"); int end = pattern.lastIndexOf("*"); if (source.startsWith(pattern.substring(0, start)) From 3a215edc9ef7970f997cca46d692269c2aae12fb Mon Sep 17 00:00:00 2001 From: gitriver Date: Thu, 5 Jun 2014 12:11:18 +0800 Subject: [PATCH 4/6] =?UTF-8?q?init=E6=96=B9=E6=B3=95=E9=92=88=E5=AF=B9Jdb?= =?UTF-8?q?cUtils.getDbType=E8=B0=83=E7=94=A8=E4=B8=A4=E6=AC=A1,=E5=AF=BC?= =?UTF-8?q?=E8=87=B4MYSQL=E6=88=96mariaDB=E7=9A=84=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=9C=AA=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alibaba/druid/pool/DruidDataSource.java | 4814 ++++++++--------- 1 file changed, 2405 insertions(+), 2409 deletions(-) diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index b2c6cd46f4..8c4ad23747 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -1,2409 +1,2405 @@ -/* - * Copyright 1999-2011 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.druid.pool; - -import static com.alibaba.druid.util.Utils.getBoolean; - -import java.io.Closeable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.sql.Connection; -import java.sql.SQLException; -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.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import javax.management.JMException; -import javax.management.ObjectName; -import javax.naming.NamingException; -import javax.naming.Reference; -import javax.naming.Referenceable; -import javax.naming.StringRefAddr; -import javax.sql.ConnectionEvent; -import javax.sql.ConnectionEventListener; -import javax.sql.ConnectionPoolDataSource; -import javax.sql.PooledConnection; - -import com.alibaba.druid.Constants; -import com.alibaba.druid.TransactionTimeoutException; -import com.alibaba.druid.VERSION; -import com.alibaba.druid.filter.AutoLoad; -import com.alibaba.druid.filter.Filter; -import com.alibaba.druid.filter.FilterChainImpl; -import com.alibaba.druid.mock.MockDriver; -import com.alibaba.druid.pool.DruidPooledPreparedStatement.PreparedStatementKey; -import com.alibaba.druid.pool.vendor.DB2ExceptionSorter; -import com.alibaba.druid.pool.vendor.InformixExceptionSorter; -import com.alibaba.druid.pool.vendor.MSSQLValidConnectionChecker; -import com.alibaba.druid.pool.vendor.MockExceptionSorter; -import com.alibaba.druid.pool.vendor.MySqlExceptionSorter; -import com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker; -import com.alibaba.druid.pool.vendor.OracleExceptionSorter; -import com.alibaba.druid.pool.vendor.OracleValidConnectionChecker; -import com.alibaba.druid.pool.vendor.SybaseExceptionSorter; -import com.alibaba.druid.proxy.DruidDriver; -import com.alibaba.druid.proxy.jdbc.DataSourceProxyConfig; -import com.alibaba.druid.proxy.jdbc.TransactionInfo; -import com.alibaba.druid.sql.ast.SQLStatement; -import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; -import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; -import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; -import com.alibaba.druid.sql.parser.SQLParserUtils; -import com.alibaba.druid.sql.parser.SQLStatementParser; -import com.alibaba.druid.stat.DruidDataSourceStatManager; -import com.alibaba.druid.stat.JdbcDataSourceStat; -import com.alibaba.druid.stat.JdbcSqlStat; -import com.alibaba.druid.stat.JdbcSqlStatValue; -import com.alibaba.druid.support.logging.Log; -import com.alibaba.druid.support.logging.LogFactory; -import com.alibaba.druid.util.JMXUtils; -import com.alibaba.druid.util.JdbcConstants; -import com.alibaba.druid.util.JdbcUtils; -import com.alibaba.druid.util.StringUtils; -import com.alibaba.druid.util.Utils; -import com.alibaba.druid.wall.WallFilter; -import com.alibaba.druid.wall.WallProviderStatValue; - -/** - * @author ljw - * @author wenshao - */ -public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource { - - private final static Log LOG = LogFactory.getLog(DruidDataSource.class); - - private static final long serialVersionUID = 1L; - - // stats - private final AtomicLong recycleErrorCount = new AtomicLong(); - private long connectCount = 0L; - private long closeCount = 0L; - private final AtomicLong connectErrorCount = new AtomicLong(); - private long recycleCount = 0L; - private long removeAbandonedCount = 0L; - private long notEmptyWaitCount = 0L; - private long notEmptySignalCount = 0L; - private long notEmptyWaitNanos = 0L; - - private int activePeak = 0; - private long activePeakTime = 0; - private int poolingPeak = 0; - private long poolingPeakTime = 0; - - // store - private volatile DruidConnectionHolder[] connections; - private int poolingCount = 0; - private int activeCount = 0; - private long discardCount = 0; - private int notEmptyWaitThreadCount = 0; - private int notEmptyWaitThreadPeak = 0; - - // threads - private CreateConnectionThread createConnectionThread; - private DestroyConnectionThread destoryConnectionThread; - private LogStatsThread logStatsThread; - - private final CountDownLatch initedLatch = new CountDownLatch(2); - - private volatile boolean enable = true; - - private boolean resetStatEnable = true; - private final AtomicLong resetCount = new AtomicLong(); - - private String initStackTrace; - - private volatile boolean closed = false; - private long closeTimeMillis = -1L; - - protected JdbcDataSourceStat dataSourceStat; - - private boolean useGloalDataSourceStat = false; - - private boolean mbeanRegistered = false; - - public static ThreadLocal waitNanosLocal = new ThreadLocal(); - - private boolean logDiffrentThread = true; - - public DruidDataSource(){ - this(false); - } - - public DruidDataSource(boolean fairLock){ - super(fairLock); - - configFromPropety(System.getProperties()); - } - - public void configFromPropety(Properties properties) { - { - Boolean value = getBoolean(properties, "druid.testWhileIdle"); - if (value != null) { - this.setTestWhileIdle(value); - } - } - { - Boolean value = getBoolean(properties, "druid.testOnBorrow"); - if (value != null) { - this.setTestOnBorrow(value); - } - } - { - String property = properties.getProperty("druid.validationQuery"); - if (property != null && property.length() > 0) { - this.setValidationQuery(property); - } - } - { - Boolean value = getBoolean(properties, "druid.useGloalDataSourceStat"); - if (value != null) { - this.setUseGloalDataSourceStat(value); - } - } - { - String property = properties.getProperty("druid.filters"); - - if (property != null && property.length() > 0) { - try { - this.setFilters(property); - } catch (SQLException e) { - LOG.error("setFilters error", e); - } - } - } - { - String property = properties.getProperty(Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS); - if (property != null && property.length() > 0) { - try { - long value = Long.parseLong(property); - this.setTimeBetweenLogStatsMillis(value); - } catch (NumberFormatException e) { - LOG.error("illegal property '" + Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS + "'", e); - } - } - } - { - String property = properties.getProperty(Constants.DRUID_STAT_SQL_MAX_SIZE); - if (property != null && property.length() > 0) { - try { - int value = Integer.parseInt(property); - if (dataSourceStat != null) { - dataSourceStat.setMaxSqlSize(value); - } - } catch (NumberFormatException e) { - LOG.error("illegal property '" + Constants.DRUID_STAT_SQL_MAX_SIZE + "'", e); - } - } - } - { - Boolean value = getBoolean(properties, "druid.clearFiltersEnable"); - if (value != null) { - this.setClearFiltersEnable(value); - } - } - { - Boolean value = getBoolean(properties, "druid.resetStatEnable"); - if (value != null) { - this.setResetStatEnable(value); - } - } - } - - public boolean isUseGloalDataSourceStat() { - return useGloalDataSourceStat; - } - - public void setUseGloalDataSourceStat(boolean useGloalDataSourceStat) { - this.useGloalDataSourceStat = useGloalDataSourceStat; - } - - public String getInitStackTrace() { - return initStackTrace; - } - - public boolean isResetStatEnable() { - return resetStatEnable; - } - - public void setResetStatEnable(boolean resetStatEnable) { - this.resetStatEnable = resetStatEnable; - if (dataSourceStat != null) { - dataSourceStat.setResetStatEnable(resetStatEnable); - } - } - - public long getDiscardCount() { - return discardCount; - } - - public void restart() { - lock.lock(); - try { - if (LOG.isInfoEnabled()) { - LOG.info("{dataSource-" + this.getID() + "} restart"); - } - - this.close(); - this.resetStat(); - this.inited = false; - this.enable = true; - this.closed = false; - } finally { - lock.unlock(); - } - } - - public void resetStat() { - if (!isResetStatEnable()) { - return; - } - - lock.lock(); - try { - connectCount = 0; - closeCount = 0; - discardCount = 0; - recycleCount = 0; - createCount.set(0); - destroyCount.set(0); - removeAbandonedCount = 0; - notEmptyWaitCount = 0; - notEmptySignalCount = 0L; - notEmptyWaitNanos = 0; - - activePeak = 0; - activePeakTime = 0; - poolingPeak = 0; - createTimespan = 0; - lastError = null; - lastErrorTimeMillis = 0; - lastCreateError = null; - lastCreateErrorTimeMillis = 0; - } finally { - lock.unlock(); - } - - connectErrorCount.set(0); - errorCount.set(0); - commitCount.set(0); - rollbackCount.set(0); - startTransactionCount.set(0); - cachedPreparedStatementHitCount.set(0); - closedPreparedStatementCount.set(0); - preparedStatementCount.set(0); - transactionHistogram.reset(); - cachedPreparedStatementDeleteCount.set(0); - recycleErrorCount.set(0); - - resetCount.incrementAndGet(); - } - - public long getResetCount() { - return this.resetCount.get(); - } - - public boolean isEnable() { - return enable; - } - - public void setEnable(boolean enable) { - lock.lock(); - try { - this.enable = enable; - if (!enable) { - notEmpty.signalAll(); - notEmptySignalCount++; - } - } finally { - lock.unlock(); - } - } - - public void setPoolPreparedStatements(boolean value) { - if (this.poolPreparedStatements == value) { - return; - } - - this.poolPreparedStatements = value; - - if (!inited) { - return; - } - - if (LOG.isInfoEnabled()) { - LOG.info("set poolPreparedStatements " + this.poolPreparedStatements + " -> " + value); - } - - if (!value) { - lock.lock(); - try { - - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder connection = connections[i]; - - for (PreparedStatementHolder holder : connection.getStatementPool().getMap().values()) { - closePreapredStatement(holder); - } - - connection.getStatementPool().getMap().clear(); - } - } finally { - lock.unlock(); - } - } - } - - public void setMaxActive(int maxActive) { - if (this.maxActive == maxActive) { - return; - } - - if (maxActive == 0) { - throw new IllegalArgumentException("maxActive can't not set zero"); - } - - if (!inited) { - this.maxActive = maxActive; - return; - } - - if (maxActive < this.minIdle) { - throw new IllegalArgumentException("maxActive less than minIdle, " + maxActive + " < " + this.minIdle); - } - - if (LOG.isInfoEnabled()) { - LOG.info("maxActive changed : " + this.maxActive + " -> " + maxActive); - } - - lock.lock(); - try { - int allCount = this.poolingCount + this.activeCount; - - if (maxActive > allCount) { - this.connections = Arrays.copyOf(this.connections, maxActive); - } else { - this.connections = Arrays.copyOf(this.connections, allCount); - } - - this.maxActive = maxActive; - } finally { - lock.unlock(); - } - } - - @SuppressWarnings("rawtypes") - public void setConnectProperties(Properties properties) { - if (properties == null) { - properties = new Properties(); - } - - boolean equals; - if (properties.size() == this.connectProperties.size()) { - equals = true; - for (Map.Entry entry : properties.entrySet()) { - Object value = this.connectProperties.get(entry.getKey()); - Object entryValue = entry.getValue(); - if (value == null && entryValue != null) { - equals = false; - break; - } - - if (!value.equals(entry.getValue())) { - equals = false; - break; - } - } - } else { - equals = false; - } - - if (!equals) { - if (inited && LOG.isInfoEnabled()) { - LOG.info("connectProperties changed : " + this.connectProperties + " -> " + properties); - } - - configFromPropety(properties); - - for (Filter filter : this.filters) { - filter.configFromProperties(properties); - } - - if (exceptionSorter != null) { - exceptionSorter.configFromProperties(properties); - } - - if (validConnectionChecker != null) { - validConnectionChecker.configFromProperties(properties); - } - - if (statLogger != null) { - statLogger.configFromProperties(properties); - } - } - - this.connectProperties = properties; - } - - public boolean isInited() { - return this.inited; - } - - public void init() throws SQLException { - if (inited) { - return; - } - - final ReentrantLock lock = this.lock; - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - throw new SQLException("interrupt", e); - } - - boolean init = false; - try { - if (inited) { - return; - } - - init = true; - - initStackTrace = Utils.toString(Thread.currentThread().getStackTrace()); - - this.id = DruidDriver.createDataSourceId(); - if (this.id > 1) { - long delta = (this.id - 1) * 100000; - this.connectionIdSeed.addAndGet(delta); - this.statementIdSeed.addAndGet(delta); - this.resultSetIdSeed.addAndGet(delta); - this.transactionIdSeed.addAndGet(delta); - } - - if (this.dbType == null || this.dbType.length() == 0) { - this.dbType = JdbcUtils.getDbType(jdbcUrl, null); - } - - for (Filter filter : filters) { - filter.init(this); - } - - if (JdbcConstants.MYSQL.equals(this.dbType) || // - JdbcConstants.MARIADB.equals(this.dbType)) { - boolean cacheServerConfigurationSet = false; - if (this.connectProperties.containsKey("cacheServerConfiguration")) { - cacheServerConfigurationSet = true; - } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) { - cacheServerConfigurationSet = true; - } - if (cacheServerConfigurationSet) { - this.connectProperties.put("cacheServerConfiguration", "true"); - } - } - - if (maxActive <= 0) { - throw new IllegalArgumentException("illegal maxActive " + maxActive); - } - - if (maxActive < minIdle) { - throw new IllegalArgumentException("illegal maxActive " + maxActive); - } - - if (getInitialSize() > maxActive) { - throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActieve " - + maxActive); - } - - if (timeBetweenLogStatsMillis > 0 && useGloalDataSourceStat) { - throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGloalDataSourceStat=true"); - } - - if (this.driverClass != null) { - this.driverClass = driverClass.trim(); - } - - if (this.jdbcUrl != null) { - this.jdbcUrl = this.jdbcUrl.trim(); - initFromWrapDriverUrl(); - } - - initFromSPIServiceLoader(); - - if (this.driver == null) { - if (this.driverClass == null || this.driverClass.isEmpty()) { - this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); - } - - if (MockDriver.class.getName().equals(driverClass)) { - driver = MockDriver.instance; - } else { - driver = JdbcUtils.createDriver(driverClassLoader, driverClass); - } - } else { - if (this.driverClass == null) { - this.driverClass = driver.getClass().getName(); - } - } - - if (this.dbType == null || this.dbType.length() == 0) { - this.dbType = JdbcUtils.getDbType(jdbcUrl, driverClass.getClass().getName()); - } - - initCheck(); - - initExceptionSorter(); - initValidConnectionChecker(); - validationQueryCheck(); - - if (isUseGloalDataSourceStat()) { - dataSourceStat = JdbcDataSourceStat.getGlobal(); - if (dataSourceStat == null) { - dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType); - JdbcDataSourceStat.setGlobal(dataSourceStat); - } - if (dataSourceStat.getDbType() == null) { - dataSourceStat.setDbType(this.getDbType()); - } - } else { - dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties); - } - dataSourceStat.setResetStatEnable(this.resetStatEnable); - - connections = new DruidConnectionHolder[maxActive]; - - SQLException connectError = null; - - try { - // init connections - for (int i = 0, size = getInitialSize(); i < size; ++i) { - Connection conn = createPhysicalConnection(); - DruidConnectionHolder holder = new DruidConnectionHolder(this, conn); - connections[poolingCount++] = holder; - } - - if (poolingCount > 0) { - poolingPeak = poolingCount; - poolingPeakTime = System.currentTimeMillis(); - } - } catch (SQLException ex) { - LOG.error("init datasource error", ex); - connectError = ex; - } - - createAndLogThread(); - createAndStartCreatorThread(); - createAndStartDestroyThread(); - - initedLatch.await(); - - initedTime = new Date(); - registerMbean(); - - if (connectError != null && poolingCount == 0) { - throw connectError; - } - } catch (SQLException e) { - LOG.error("dataSource init error", e); - throw e; - } catch (InterruptedException e) { - throw new SQLException(e.getMessage(), e); - } finally { - inited = true; - lock.unlock(); - - if (init && LOG.isInfoEnabled()) { - LOG.info("{dataSource-" + this.getID() + "} inited"); - } - } - } - - private void createAndLogThread() { - if (this.timeBetweenLogStatsMillis <= 0) { - return; - } - - String threadName = "Druid-ConnectionPool-Log-" + System.identityHashCode(this); - logStatsThread = new LogStatsThread(threadName); - logStatsThread.start(); - - this.resetStatEnable = false; - } - - private void createAndStartDestroyThread() { - String threadName = "Druid-ConnectionPool-Destory-" + System.identityHashCode(this); - destoryConnectionThread = new DestroyConnectionThread(threadName); - destoryConnectionThread.start(); - } - - protected void createAndStartCreatorThread() { - String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this); - createConnectionThread = new CreateConnectionThread(threadName); - createConnectionThread.start(); - } - - /** - * load filters from SPI ServiceLoader - * - * @see ServiceLoader - */ - private void initFromSPIServiceLoader() { - - String property = System.getProperty("druid.load.spifilter.skip"); - if (property != null) { - return; - } - - ServiceLoader druidAutoFilterLoader = ServiceLoader.load(Filter.class); - - for (Filter autoFilter : druidAutoFilterLoader) { - AutoLoad autoLoad = autoFilter.getClass().getAnnotation(AutoLoad.class); - if (autoLoad != null && autoLoad.value()) { - if (LOG.isInfoEnabled()) { - LOG.info("load filter from spi :" + autoFilter.getClass().getName()); - } - addFilter(autoFilter); - } - } - } - - private void initFromWrapDriverUrl() throws SQLException { - if (!jdbcUrl.startsWith(DruidDriver.DEFAULT_PREFIX)) { - return; - } - - DataSourceProxyConfig config = DruidDriver.parseConfig(jdbcUrl, null); - this.driverClass = config.getRawDriverClassName(); - - LOG.error("error url : '" + jdbcUrl + "', it should be : '" + config.getRawUrl() + "'"); - - this.jdbcUrl = config.getRawUrl(); - if (this.name == null) { - this.name = config.getName(); - } - - for (Filter filter : config.getFilters()) { - addFilter(filter); - } - } - - /** - * 会去重复 - * - * @param filter - */ - private void addFilter(Filter filter) { - boolean exists = false; - for (Filter initedFilter : this.filters) { - if (initedFilter.getClass() == filter.getClass()) { - exists = true; - break; - } - } - - if (!exists) { - filter.init(this); - this.filters.add(filter); - } - - } - - private void validationQueryCheck() { - if (!(isTestOnBorrow() || isTestOnReturn() || isTestWhileIdle())) { - return; - } - - if (this.validConnectionChecker != null) { - return; - } - - if (this.getValidationQuery() != null && this.getValidationQuery().length() > 0) { - return; - } - - String errorMessage = ""; - - if (isTestOnBorrow()) { - errorMessage += "testOnBorrow is true, "; - } - - if (isTestOnReturn()) { - errorMessage += "testOnReturn is true, "; - } - - if (isTestWhileIdle()) { - errorMessage += "testWhileIdle is true, "; - } - - LOG.error(errorMessage + "validationQuery not set"); - } - - protected void initCheck() throws SQLException { - if (JdbcUtils.ORACLE.equals(this.dbType)) { - isOracle = true; - - if (driver.getMajorVersion() < 10) { - throw new SQLException("not support oracle driver " + driver.getMajorVersion() + "." - + driver.getMinorVersion()); - } - - if (driver.getMajorVersion() == 10 && isUseOracleImplicitCache()) { - this.getConnectProperties().setProperty("oracle.jdbc.FreeMemoryOnEnterImplicitCache", "true"); - } - - oracleValidationQueryCheck(); - } else if (JdbcUtils.DB2.equals(dbType)) { - db2ValidationQueryCheck(); - } - } - - private void oracleValidationQueryCheck() { - if (validationQuery == null) { - return; - } - if (validationQuery.length() == 0) { - return; - } - - SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); - List stmtList = sqlStmtParser.parseStatementList(); - - if (stmtList.size() != 1) { - return; - } - - SQLStatement stmt = stmtList.get(0); - if (!(stmt instanceof SQLSelectStatement)) { - return; - } - - SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); - if (query instanceof SQLSelectQueryBlock) { - if (((SQLSelectQueryBlock) query).getFrom() == null) { - LOG.error("invalid oracle validationQuery. " + validationQuery + ", may should be : " + validationQuery - + " FROM DUAL"); - } - } - } - - private void db2ValidationQueryCheck() { - if (validationQuery == null) { - return; - } - if (validationQuery.length() == 0) { - return; - } - - SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); - List stmtList = sqlStmtParser.parseStatementList(); - - if (stmtList.size() != 1) { - return; - } - - SQLStatement stmt = stmtList.get(0); - if (!(stmt instanceof SQLSelectStatement)) { - return; - } - - SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); - if (query instanceof SQLSelectQueryBlock) { - if (((SQLSelectQueryBlock) query).getFrom() == null) { - LOG.error("invalid db2 validationQuery. " + validationQuery + ", may should be : " + validationQuery - + " FROM SYSDUMMY"); - } - } - } - - private void initValidConnectionChecker() { - String realDriverClassName = driver.getClass().getName(); - if (realDriverClassName.equals("com.mysql.jdbc.Driver")) { - this.validConnectionChecker = new MySqlValidConnectionChecker(); - } else if (realDriverClassName.equals("oracle.jdbc.driver.OracleDriver")) { - this.validConnectionChecker = new OracleValidConnectionChecker(); - } else if (realDriverClassName.equals("com.microsoft.jdbc.sqlserver.SQLServerDriver")) { - this.validConnectionChecker = new MSSQLValidConnectionChecker(); - } - } - - private void initExceptionSorter() { - String realDriverClassName = driver.getClass().getName(); - if (realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER)) { - this.exceptionSorter = new MySqlExceptionSorter(); - } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)) { - 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(); - - } else if (realDriverClassName.equals("com.alibaba.druid.mock.MockDriver")) { - this.exceptionSorter = new MockExceptionSorter(); - } else if (realDriverClassName.contains("DB2")) { - this.exceptionSorter = new DB2ExceptionSorter(); - } - } - - @Override - public DruidPooledConnection getConnection() throws SQLException { - return getConnection(maxWait); - } - - public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException { - init(); - - if (filters.size() > 0) { - FilterChainImpl filterChain = new FilterChainImpl(this); - return filterChain.dataSource_connect(this, maxWaitMillis); - } else { - return getConnectionDirect(maxWaitMillis); - } - } - - @Override - public PooledConnection getPooledConnection() throws SQLException { - return getConnection(maxWait); - } - - @Override - public PooledConnection getPooledConnection(String user, String password) throws SQLException { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); - } - - public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException { - for (;;) { - DruidPooledConnection poolableConnection = getConnectionInternal(maxWaitMillis); - - if (isTestOnBorrow()) { - boolean validate = testConnectionInternal(poolableConnection.getConnection()); - if (!validate) { - if (LOG.isDebugEnabled()) { - LOG.debug("skip not validate connection."); - } - - Connection realConnection = poolableConnection.getConnection(); - discardConnection(realConnection); - continue; - } - } else { - Connection realConnection = poolableConnection.getConnection(); - if (realConnection.isClosed()) { - discardConnection(null); // 传入null,避免重复关闭 - continue; - } - - if (isTestWhileIdle()) { - final long currentTimeMillis = System.currentTimeMillis(); - final long lastActiveTimeMillis = poolableConnection.getConnectionHolder().getLastActiveTimeMillis(); - final long idleMillis = currentTimeMillis - lastActiveTimeMillis; - long timeBetweenEvictionRunsMillis = this.getTimeBetweenEvictionRunsMillis(); - if (timeBetweenEvictionRunsMillis <= 0) { - timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; - } - - if (idleMillis >= timeBetweenEvictionRunsMillis) { - boolean validate = testConnectionInternal(poolableConnection.getConnection()); - if (!validate) { - if (LOG.isDebugEnabled()) { - LOG.debug("skip not validate connection."); - } - - discardConnection(realConnection); - continue; - } - } - } - } - - if (isRemoveAbandoned()) { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - poolableConnection.setConnectStackTrace(stackTrace); - poolableConnection.setConnectedTimeNano(); - poolableConnection.setTraceEnable(true); - - synchronized (activeConnections) { - activeConnections.put(poolableConnection, PRESENT); - } - } - - if (!this.isDefaultAutoCommit()) { - poolableConnection.setAutoCommit(false); - } - - return poolableConnection; - } - } - - /** - * 抛弃连接,不进行回收,而是抛弃 - * - * @param realConnection - * @throws SQLException - */ - public void discardConnection(Connection realConnection) { - JdbcUtils.close(realConnection); - - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - LOG.error("interrupt"); - return; - } - try { - activeCount--; - discardCount++; - - if (activeCount <= 0) { - empty.signal(); - } - } finally { - lock.unlock(); - } - } - - private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException { - if (closed) { - connectErrorCount.incrementAndGet(); - throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis)); - } - - if (!enable) { - connectErrorCount.incrementAndGet(); - throw new DataSourceDisableException(); - } - - final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait); - final int maxWaitThreadCount = getMaxWaitThreadCount(); - - DruidConnectionHolder holder; - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - connectErrorCount.incrementAndGet(); - throw new SQLException("interrupt", e); - } - - try { - if (maxWaitThreadCount > 0) { - if (notEmptyWaitThreadCount >= maxWaitThreadCount) { - connectErrorCount.incrementAndGet(); - throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count " - + lock.getQueueLength()); - } - } - - connectCount++; - - if (maxWait > 0) { - holder = pollLast(nanos); - } else { - holder = takeLast(); - } - - if (holder != null) { - activeCount++; - if (activeCount > activePeak) { - activePeak = activeCount; - activePeakTime = System.currentTimeMillis(); - } - } - } catch (InterruptedException e) { - connectErrorCount.incrementAndGet(); - throw new SQLException(e.getMessage(), e); - } catch (SQLException e) { - connectErrorCount.incrementAndGet(); - throw e; - } finally { - lock.unlock(); - } - - if (holder == null) { - long waitNanos = waitNanosLocal.get(); - - StringBuilder buf = new StringBuilder(); - buf.append("wait millis ")// - .append(waitNanos / (1000 * 1000))// - .append(", active " + activeCount)// - ; - - List sqlList = this.getDataSourceStat().getRuningSqlList(); - for (int i = 0; i < sqlList.size(); ++i) { - if (i != 0) { - buf.append('\n'); - } else { - buf.append(", "); - } - JdbcSqlStatValue sql = sqlList.get(i); - buf.append("runningSqlCount "); - buf.append(sql.getRunningCount()); - buf.append(" : "); - buf.append(sql.getSql()); - } - - String errorMessage = buf.toString(); - - if (this.createError != null) { - throw new GetConnectionTimeoutException(errorMessage, createError); - } else { - throw new GetConnectionTimeoutException(errorMessage); - } - } - - holder.incrementUseCount(); - - DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder); - return poolalbeConnection; - } - - public void handleConnectionException(DruidPooledConnection pooledConnection, Throwable t) throws SQLException { - final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); - - errorCount.incrementAndGet(); - lastError = t; - lastErrorTimeMillis = System.currentTimeMillis(); - - if (t instanceof SQLException) { - SQLException sqlEx = (SQLException) t; - - // broadcastConnectionError - ConnectionEvent event = new ConnectionEvent(pooledConnection, sqlEx); - for (ConnectionEventListener eventListener : holder.getConnectionEventListeners()) { - eventListener.connectionErrorOccurred(event); - } - - // exceptionSorter.isExceptionFatal - if (exceptionSorter != null && exceptionSorter.isExceptionFatal(sqlEx)) { - if (pooledConnection.isTraceEnable()) { - synchronized (activeConnections) { - if (pooledConnection.isTraceEnable()) { - activeConnections.remove(pooledConnection); - pooledConnection.setTraceEnable(false); - } - } - } - this.discardConnection(holder.getConnection()); - holder.setDiscard(true); - pooledConnection.disable(t); - - LOG.error("discard connection", sqlEx); - } - - throw sqlEx; - } else { - throw new SQLException("Error", t); - } - } - - /** - * 回收连接 - */ - protected void recycle(DruidPooledConnection pooledConnection) throws SQLException { - final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); - - if (holder == null) { - LOG.warn("connectionHolder is null"); - return; - } - - if (logDiffrentThread && pooledConnection.getOwnerThread() != Thread.currentThread()) { - LOG.warn("get/close not same thread"); - } - - final Connection physicalConnection = holder.getConnection(); - - if (pooledConnection.isTraceEnable()) { - synchronized (activeConnections) { - if (pooledConnection.isTraceEnable()) { - Object oldInfo = activeConnections.remove(pooledConnection); - if (oldInfo == null) { - if (LOG.isWarnEnabled()) { - LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size()); - } - } - pooledConnection.setTraceEnable(false); - } - } - } - - final boolean isAutoCommit = holder.isUnderlyingAutoCommit(); - final boolean isReadOnly = holder.isUnderlyingReadOnly(); - final boolean testOnReturn = this.isTestOnReturn(); - - try { - // check need to rollback? - if ((!isAutoCommit) && (!isReadOnly)) { - pooledConnection.rollback(); - } - - // reset holder, restore default settings, clear warnings - holder.reset(); - - if (testOnReturn) { - boolean validate = testConnectionInternal(physicalConnection); - if (!validate) { - JdbcUtils.close(physicalConnection); - - destroyCount.incrementAndGet(); - - lock.lockInterruptibly(); - try { - activeCount--; - closeCount++; - } finally { - lock.unlock(); - } - return; - } - } - - if (!enable) { - discardConnection(holder.getConnection()); - return; - } - - final long lastActiveTimeMillis = System.currentTimeMillis(); - lock.lockInterruptibly(); - try { - activeCount--; - closeCount++; - - putLast(holder, lastActiveTimeMillis); - recycleCount++; - } finally { - lock.unlock(); - } - } catch (Throwable e) { - holder.clearStatementCache(); - - if (!holder.isDiscard()) { - this.discardConnection(physicalConnection); - holder.setDiscard(true); - } - - LOG.error("recyle error", e); - recycleErrorCount.incrementAndGet(); - } - } - - public long getRecycleErrorCount() { - return recycleErrorCount.get(); - } - - public void clearStatementCache() throws SQLException { - lock.lock(); - try { - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder conn = connections[i]; - conn.getStatementPool().clear(); - } - } finally { - lock.unlock(); - } - } - - /** - * close datasource - */ - public void close() { - lock.lock(); - try { - if (this.closed) { - return; - } - - if (!this.inited) { - return; - } - - if (logStatsThread != null) { - logStatsThread.interrupt(); - } - - if (createConnectionThread != null) { - createConnectionThread.interrupt(); - } - - if (destoryConnectionThread != null) { - destoryConnectionThread.interrupt(); - } - - for (int i = 0; i < poolingCount; ++i) { - try { - DruidConnectionHolder connHolder = connections[i]; - - for (PreparedStatementHolder stmtHolder : connHolder.getStatementPool().getMap().values()) { - connHolder.getStatementPool().closeRemovedStatement(stmtHolder); - } - connHolder.getStatementPool().getMap().clear(); - - Connection physicalConnection = connHolder.getConnection(); - physicalConnection.close(); - connections[i] = null; - destroyCount.incrementAndGet(); - } catch (Exception ex) { - LOG.warn("close connection error", ex); - } - } - poolingCount = 0; - unregisterMbean(); - - enable = false; - notEmpty.signalAll(); - notEmptySignalCount++; - - this.closed = true; - this.closeTimeMillis = System.currentTimeMillis(); - - for (Filter filter : filters) { - filter.destory(); - } - } finally { - lock.unlock(); - } - - if (LOG.isInfoEnabled()) { - LOG.info("{dataSource-" + this.getID() + "} closed"); - } - } - - public void registerMbean() { - if (!mbeanRegistered) { - AccessController.doPrivileged(new PrivilegedAction() { - - @Override - public Object run() { - ObjectName objectName = DruidDataSourceStatManager.addDataSource(DruidDataSource.this, - DruidDataSource.this.name); - DruidDataSource.this.setObjectName(objectName); - DruidDataSource.this.mbeanRegistered = true; - return null; - } - }); - } - } - - public void unregisterMbean() { - if (mbeanRegistered) { - AccessController.doPrivileged(new PrivilegedAction() { - - @Override - public Object run() { - DruidDataSourceStatManager.removeDataSource(DruidDataSource.this); - DruidDataSource.this.mbeanRegistered = false; - return null; - } - }); - } - } - - public boolean isMbeanRegistered() { - return mbeanRegistered; - } - - void putLast(DruidConnectionHolder e, long lastActiveTimeMillis) { - e.setLastActiveTimeMillis(lastActiveTimeMillis); - connections[poolingCount++] = e; - - if (poolingCount > poolingPeak) { - poolingPeak = poolingCount; - poolingPeakTime = lastActiveTimeMillis; - } - - notEmpty.signal(); - notEmptySignalCount++; - } - - DruidConnectionHolder takeLast() throws InterruptedException, SQLException { - try { - while (poolingCount == 0) { - empty.signal(); // send signal to CreateThread create connection - notEmptyWaitThreadCount++; - if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { - notEmptyWaitThreadPeak = notEmptyWaitThreadCount; - } - try { - notEmpty.await(); // signal by recycle or creator - } finally { - notEmptyWaitThreadCount--; - } - notEmptyWaitCount++; - - if (!enable) { - connectErrorCount.incrementAndGet(); - throw new DataSourceDisableException(); - } - } - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - notEmptySignalCount++; - throw ie; - } - - poolingCount--; - DruidConnectionHolder last = connections[poolingCount]; - connections[poolingCount] = null; - - return last; - } - - DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException { - long estimate = nanos; - - for (;;) { - if (poolingCount == 0) { - empty.signal(); // send signal to CreateThread create connection - - if (estimate <= 0) { - waitNanosLocal.set(nanos - estimate); - return null; - } - - notEmptyWaitThreadCount++; - if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { - notEmptyWaitThreadPeak = notEmptyWaitThreadCount; - } - - try { - long startEstimate = estimate; - estimate = notEmpty.awaitNanos(estimate); // signal by - // recycle or - // creator - notEmptyWaitCount++; - notEmptyWaitNanos += (startEstimate - estimate); - - if (!enable) { - connectErrorCount.incrementAndGet(); - throw new DataSourceDisableException(); - } - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - notEmptySignalCount++; - throw ie; - } finally { - notEmptyWaitThreadCount--; - } - - if (poolingCount == 0) { - if (estimate > 0) { - continue; - } - - waitNanosLocal.set(nanos - estimate); - return null; - } - } - - poolingCount--; - DruidConnectionHolder last = connections[poolingCount]; - connections[poolingCount] = null; - - return last; - } - } - - @Override - public Connection getConnection(String username, String password) throws SQLException { - if (!StringUtils.equals(username, this.username)) { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); - } - - if (!StringUtils.equals(password, this.password)) { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); - } - - return getConnection(); - } - - public long getCreateCount() { - return createCount.get(); - } - - public long getDestroyCount() { - return destroyCount.get(); - } - - public long getConnectCount() { - lock.lock(); - try { - return connectCount; - } finally { - lock.unlock(); - } - } - - public long getCloseCount() { - return closeCount; - } - - public long getConnectErrorCount() { - return connectErrorCount.get(); - } - - @Override - public int getPoolingCount() { - lock.lock(); - try { - return poolingCount; - } finally { - lock.unlock(); - } - } - - public int getPoolingPeak() { - lock.lock(); - try { - return poolingPeak; - } finally { - lock.unlock(); - } - } - - public Date getPoolingPeakTime() { - if (poolingPeakTime <= 0) { - return null; - } - - return new Date(poolingPeakTime); - } - - public long getRecycleCount() { - return recycleCount; - } - - public int getActiveCount() { - lock.lock(); - try { - return activeCount; - } finally { - lock.unlock(); - } - } - - public void logStats() { - final DruidDataSourceStatLogger statLogger = this.statLogger; - if (statLogger == null) { - return; - } - - DruidDataSourceStatValue statValue = getStatValueAndReset(); - - statLogger.log(statValue); - } - - public DruidDataSourceStatValue getStatValueAndReset() { - DruidDataSourceStatValue value = new DruidDataSourceStatValue(); - - lock.lock(); - try { - value.setPoolingCount(this.poolingCount); - value.setPoolingPeak(this.poolingPeak); - value.setPoolingPeakTime(this.poolingPeakTime); - - value.setActiveCount(this.activeCount); - value.setActivePeak(this.activePeak); - value.setActivePeakTime(this.activePeakTime); - - value.setConnectCount(this.connectCount); - value.setCloseCount(this.closeCount); - value.setWaitThreadCount(lock.getWaitQueueLength(notEmpty)); - value.setNotEmptyWaitCount(this.notEmptyWaitCount); - value.setNotEmptyWaitNanos(this.notEmptyWaitNanos); - - // reset - this.poolingPeak = 0; - this.poolingPeakTime = 0; - this.activePeak = 0; - this.activePeakTime = 0; - this.connectCount = 0; - this.closeCount = 0; - - this.notEmptyWaitCount = 0; - this.notEmptyWaitNanos = 0; - } finally { - lock.unlock(); - } - - value.setName(this.getName()); - value.setDbType(this.getDbType()); - value.setDriverClassName(this.getDriverClassName()); - - value.setUrl(this.getUrl()); - value.setUserName(this.getUsername()); - value.setFilterClassNames(this.getFilterClassNames()); - - value.setInitialSize(this.getInitialSize()); - value.setMinIdle(this.getMinIdle()); - value.setMaxActive(this.getMaxActive()); - - value.setQueryTimeout(this.getQueryTimeout()); - value.setTransactionQueryTimeout(this.getTransactionQueryTimeout()); - value.setLoginTimeout(this.getLoginTimeout()); - value.setValidConnectionCheckerClassName(this.getValidConnectionCheckerClassName()); - value.setExceptionSorterClassName(this.getExceptionSorterClassName()); - - value.setTestOnBorrow(this.isTestOnBorrow()); - value.setTestOnReturn(this.isTestOnReturn()); - value.setTestWhileIdle(this.isTestWhileIdle()); - - value.setDefaultAutoCommit(this.isDefaultAutoCommit()); - - if (defaultReadOnly != null) { - value.setDefaultReadOnly(defaultReadOnly); - } - value.setDefaultTransactionIsolation(this.getDefaultTransactionIsolation()); - - value.setLogicConnectErrorCount(connectErrorCount.getAndSet(0)); - - value.setPhysicalConnectCount(createCount.getAndSet(0)); - value.setPhysicalCloseCount(destroyCount.getAndSet(0)); - value.setPhysicalConnectErrorCount(createErrorCount.getAndSet(0)); - - value.setExecuteCount(this.executeCount.getAndSet(0)); - value.setErrorCount(errorCount.getAndSet(0)); - value.setCommitCount(commitCount.getAndSet(0)); - value.setRollbackCount(rollbackCount.getAndSet(0)); - - value.setPstmtCacheHitCount(cachedPreparedStatementHitCount.getAndSet(0)); - value.setPstmtCacheMissCount(cachedPreparedStatementMissCount.getAndSet(0)); - - value.setStartTransactionCount(startTransactionCount.getAndSet(0)); - value.setTransactionHistogram(this.getTransactionHistogram().toArrayAndReset()); - - value.setConnectionHoldTimeHistogram(this.getDataSourceStat().getConnectionHoldHistogram().toArrayAndReset()); - value.removeAbandoned = this.isRemoveAbandoned(); - value.setClobOpenCount(this.getDataSourceStat().getClobOpenCountAndReset()); - value.setBlobOpenCount(this.getDataSourceStat().getBlobOpenCountAndReset()); - - value.setSqlSkipCount(this.getDataSourceStat().getSkipSqlCountAndReset()); - value.setSqlList(this.getDataSourceStat().getSqlStatMapAndReset()); - - return value; - } - - public long getRemoveAbandonedCount() { - return removeAbandonedCount; - } - - public class CreateConnectionThread extends Thread { - - public CreateConnectionThread(String name){ - super(name); - this.setDaemon(true); - } - - public void run() { - initedLatch.countDown(); - - int errorCount = 0; - for (;;) { - // addLast - try { - lock.lockInterruptibly(); - } catch (InterruptedException e2) { - break; - } - - try { - // 必须存在线程等待,才创建连接 - if (poolingCount >= notEmptyWaitThreadCount) { - empty.await(); - } - - // 防止创建超过maxActive数量的连接 - if (activeCount + poolingCount >= maxActive) { - empty.await(); - continue; - } - - } catch (InterruptedException e) { - lastCreateError = e; - lastErrorTimeMillis = System.currentTimeMillis(); - break; - } finally { - lock.unlock(); - } - - Connection connection = null; - - try { - connection = createPhysicalConnection(); - } catch (SQLException e) { - LOG.error("create connection error", e); - - errorCount++; - - if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { - if (breakAfterAcquireFailure) { - break; - } - - try { - Thread.sleep(timeBetweenConnectErrorMillis); - } catch (InterruptedException interruptEx) { - break; - } - } - } catch (RuntimeException e) { - LOG.error("create connection error", e); - continue; - } catch (Error e) { - LOG.error("create connection error", e); - break; - } - - if (connection == null) { - continue; - } - - DruidConnectionHolder holder = null; - try { - holder = new DruidConnectionHolder(DruidDataSource.this, connection); - } catch (SQLException ex) { - LOG.error("create connection holder error", ex); - break; - } - - lock.lock(); - try { - connections[poolingCount++] = holder; - - if (poolingCount > poolingPeak) { - poolingPeak = poolingCount; - poolingPeakTime = System.currentTimeMillis(); - } - - errorCount = 0; // reset errorCount - - notEmpty.signal(); - notEmptySignalCount++; - } finally { - lock.unlock(); - } - } - } - } - - public class DestroyConnectionThread extends Thread { - - public DestroyConnectionThread(String name){ - super(name); - this.setDaemon(true); - } - - public void run() { - initedLatch.countDown(); - - for (;;) { - // 从前面开始删除 - try { - if (closed) { - break; - } - - if (timeBetweenEvictionRunsMillis > 0) { - Thread.sleep(timeBetweenEvictionRunsMillis); - } else { - Thread.sleep(1000); // - } - - if (Thread.interrupted()) { - break; - } - - shrink(true); - - if (isRemoveAbandoned()) { - removeAbandoned(); - } - } catch (InterruptedException e) { - break; - } - } - } - - } - - public class LogStatsThread extends Thread { - - public LogStatsThread(String name){ - super(name); - this.setDaemon(true); - } - - public void run() { - try { - for (;;) { - try { - logStats(); - } catch (Exception e) { - LOG.error("logStats error", e); - } - - Thread.sleep(timeBetweenLogStatsMillis); - } - } catch (InterruptedException e) { - // skip - } - } - } - - public int removeAbandoned() { - int removeCount = 0; - - long currrentNanos = System.nanoTime(); - - List abandonedList = new ArrayList(); - - synchronized (activeConnections) { - Iterator iter = activeConnections.keySet().iterator(); - - for (; iter.hasNext();) { - DruidPooledConnection pooledConnection = iter.next(); - - if (pooledConnection.isRunning()) { - continue; - } - - long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000); - - if (timeMillis >= removeAbandonedTimeoutMillis) { - iter.remove(); - pooledConnection.setTraceEnable(false); - abandonedList.add(pooledConnection); - } - } - } - - if (abandonedList.size() > 0) { - for (DruidPooledConnection pooledConnection : abandonedList) { - JdbcUtils.close(pooledConnection); - pooledConnection.abandond(); - removeAbandonedCount++; - removeCount++; - - if (isLogAbandoned()) { - StringBuilder buf = new StringBuilder(); - buf.append("abandon connection, open stackTrace\n"); - - StackTraceElement[] trace = pooledConnection.getConnectStackTrace(); - for (int i = 0; i < trace.length; i++) { - buf.append("\tat "); - buf.append(trace[i].toString()); - buf.append("\n"); - } - - LOG.error(buf.toString()); - } - } - } - - return removeCount; - } - - /** Instance key */ - protected String instanceKey = null; - - public Reference getReference() throws NamingException { - final String className = getClass().getName(); - final String factoryName = className + "Factory"; // XXX: not robust - Reference ref = new Reference(className, factoryName, null); - ref.add(new StringRefAddr("instanceKey", instanceKey)); - ref.add(new StringRefAddr("url", this.getUrl())); - ref.add(new StringRefAddr("username", this.getUsername())); - ref.add(new StringRefAddr("password", this.getPassword())); - // TODO ADD OTHER PROPERTIES - return ref; - } - - @Override - public List getFilterClassNames() { - List names = new ArrayList(); - for (Filter filter : filters) { - names.add(filter.getClass().getName()); - } - return names; - } - - public int getRawDriverMajorVersion() { - int version = -1; - if (this.driver != null) { - version = driver.getMajorVersion(); - } - return version; - } - - public int getRawDriverMinorVersion() { - int version = -1; - if (this.driver != null) { - version = driver.getMinorVersion(); - } - return version; - } - - public String getProperties() { - Properties properties = new Properties(); - properties.putAll(connectProperties); - if (properties.containsKey("password")) { - properties.put("password", "******"); - } - return properties.toString(); - } - - @Override - public void shrink() { - shrink(false); - } - - public void shrink(boolean checkTime) { - final List evictList = new ArrayList(); - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - return; - } - - try { - final int checkCount = poolingCount - minIdle; - final long currentTimeMillis = System.currentTimeMillis(); - for (int i = 0; i < checkCount; ++i) { - DruidConnectionHolder connection = connections[i]; - - if (checkTime) { - long idleMillis = currentTimeMillis - connection.getLastActiveTimeMillis(); - if (idleMillis >= minEvictableIdleTimeMillis) { - evictList.add(connection); - } else { - break; - } - } else { - evictList.add(connection); - } - } - - int removeCount = evictList.size(); - if (removeCount > 0) { - System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount); - Arrays.fill(connections, poolingCount - removeCount, poolingCount, null); - poolingCount -= removeCount; - } - } finally { - lock.unlock(); - } - - for (DruidConnectionHolder item : evictList) { - Connection connection = item.getConnection(); - JdbcUtils.close(connection); - destroyCount.incrementAndGet(); - } - } - - public int getWaitThreadCount() { - lock.lock(); - try { - return lock.getWaitQueueLength(notEmpty); - } finally { - lock.unlock(); - } - } - - public long getNotEmptyWaitCount() { - return notEmptyWaitCount; - } - - public int getNotEmptyWaitThreadCount() { - lock.lock(); - try { - return notEmptyWaitThreadCount; - } finally { - lock.unlock(); - } - } - - public int getNotEmptyWaitThreadPeak() { - lock.lock(); - try { - return notEmptyWaitThreadPeak; - } finally { - lock.unlock(); - } - } - - public long getNotEmptySignalCount() { - return notEmptySignalCount; - } - - public long getNotEmptyWaitMillis() { - return notEmptyWaitNanos / (1000 * 1000); - } - - public long getNotEmptyWaitNanos() { - return notEmptyWaitNanos; - } - - public int getLockQueueLength() { - return lock.getQueueLength(); - } - - public int getActivePeak() { - return activePeak; - } - - public Date getActivePeakTime() { - if (activePeakTime <= 0) { - return null; - } - - return new Date(activePeakTime); - } - - public String dump() { - lock.lock(); - try { - return this.toString(); - } finally { - lock.unlock(); - } - } - - public long getErrorCount() { - return this.errorCount.get(); - } - - public String toString() { - StringBuilder buf = new StringBuilder(); - - buf.append("{"); - - buf.append("\n\tCreateTime:\""); - buf.append(Utils.toString(getCreatedTime())); - buf.append("\""); - - buf.append(",\n\tActiveCount:"); - buf.append(getActiveCount()); - - buf.append(",\n\tPoolingCount:"); - buf.append(getPoolingCount()); - - buf.append(",\n\tCreateCount:"); - buf.append(getCreateCount()); - - buf.append(",\n\tDestroyCount:"); - buf.append(getDestroyCount()); - - buf.append(",\n\tCloseCount:"); - buf.append(getCloseCount()); - - buf.append(",\n\tConnectCount:"); - buf.append(getConnectCount()); - - buf.append(",\n\tConnections:["); - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder conn = connections[i]; - if (conn != null) { - if (i != 0) { - buf.append(","); - } - buf.append("\n\t\t"); - buf.append(conn.toString()); - } - } - buf.append("\n\t]"); - - buf.append("\n}"); - - if (this.isPoolPreparedStatements()) { - buf.append("\n\n["); - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder conn = connections[i]; - if (conn != null) { - if (i != 0) { - buf.append(","); - } - buf.append("\n\t{\n\tID:"); - buf.append(System.identityHashCode(conn.getConnection())); - PreparedStatementPool pool = conn.getStatementPool(); - - buf.append(", \n\tpoolStatements:["); - - int entryIndex = 0; - try { - for (Map.Entry entry : pool.getMap().entrySet()) { - if (entryIndex != 0) { - buf.append(","); - } - buf.append("\n\t\t{hitCount:"); - buf.append(entry.getValue().getHitCount()); - buf.append(",sql:\""); - buf.append(entry.getKey().getSql()); - buf.append("\""); - buf.append("\t}"); - - entryIndex++; - } - } catch (ConcurrentModificationException e) { - // skip .. - } - - buf.append("\n\t\t]"); - - buf.append("\n\t}"); - } - } - buf.append("\n]"); - } - - return buf.toString(); - } - - public List> getPoolingConnectionInfo() { - List> list = new ArrayList>(); - lock.lock(); - try { - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder connHolder = connections[i]; - Connection conn = connHolder.getConnection(); - - Map map = new LinkedHashMap(); - map.put("id", System.identityHashCode(conn)); - map.put("useCount", connHolder.getUseCount()); - if (connHolder.getLastActiveTimeMillis() > 0) { - map.put("lastActiveTime", new Date(connHolder.getLastActiveTimeMillis())); - } - map.put("connectTime", new Date(connHolder.getTimeMillis())); - map.put("holdability", connHolder.getUnderlyingHoldability()); - map.put("transactionIsolation", connHolder.getUnderlyingTransactionIsolation()); - map.put("autoCommit", connHolder.isUnderlyingAutoCommit()); - map.put("readoOnly", connHolder.isUnderlyingReadOnly()); - - if (connHolder.isPoolPreparedStatements()) { - List> stmtCache = new ArrayList>(); - PreparedStatementPool stmtPool = connHolder.getStatementPool(); - for (PreparedStatementHolder stmtHolder : stmtPool.getMap().values()) { - Map stmtInfo = new LinkedHashMap(); - - stmtInfo.put("sql", stmtHolder.getKey().getSql()); - stmtInfo.put("defaultRowPretch", stmtHolder.getDefaultRowPretch()); - stmtInfo.put("rowPrefetch", stmtHolder.getRowPrefetch()); - stmtInfo.put("hitCount", stmtHolder.getHitCount()); - - stmtCache.add(stmtInfo); - } - - map.put("pscache", stmtCache); - } - - list.add(map); - } - } finally { - lock.unlock(); - } - return list; - } - - public void logTransaction(TransactionInfo info) { - long transactionMillis = info.getEndTimeMillis() - info.getStartTimeMillis(); - if (transactionThresholdMillis > 0 && transactionMillis > transactionThresholdMillis) { - StringBuilder buf = new StringBuilder(); - buf.append("long time transaction, take "); - buf.append(transactionMillis); - buf.append(" ms : "); - for (String sql : info.getSqlList()) { - buf.append(sql); - buf.append(";"); - } - LOG.error(buf.toString(), new TransactionTimeoutException()); - } - } - - @Override - public String getVersion() { - return VERSION.getVersionNumber(); - } - - @Override - public JdbcDataSourceStat getDataSourceStat() { - return dataSourceStat; - } - - public Object clone() throws CloneNotSupportedException { - return cloneDruidDataSource(); - } - - public DruidDataSource cloneDruidDataSource() { - DruidDataSource x = new DruidDataSource(); - - cloneTo(x); - - return x; - } - - public Map getStatDataForMBean() { - try { - Map map = new HashMap(); - - // 0 - 4 - map.put("Name", this.getName()); - map.put("URL", this.getUrl()); - map.put("CreateCount", this.getCreateCount()); - map.put("DestroyCount", this.getDestroyCount()); - map.put("ConnectCount", this.getConnectCount()); - - // 5 - 9 - map.put("CloseCount", this.getCloseCount()); - map.put("ActiveCount", this.getActivePeak()); - map.put("PoolingCount", this.getPoolingCount()); - map.put("LockQueueLength", this.getLockQueueLength()); - map.put("WaitThreadCount", this.getNotEmptyWaitThreadPeak()); - - // 10 - 14 - map.put("InitialSize", this.getInitialSize()); - map.put("MaxActive", this.getMaxActive()); - map.put("MinIdle", this.getMinIdle()); - map.put("PoolPreparedStatements", this.isPoolPreparedStatements()); - map.put("TestOnBorrow", this.isTestOnBorrow()); - - // 15 - 19 - map.put("TestOnReturn", this.isTestOnReturn()); - map.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis()); - map.put("ConnectErrorCount", this.getConnectErrorCount()); - map.put("CreateTimespanMillis", this.getCreateTimespanMillis()); - map.put("DbType", this.getDbType()); - - // 20 - 24 - map.put("ValidationQuery", this.getValidationQuery()); - map.put("ValidationQueryTimeout", this.getValidationQueryTimeout()); - map.put("DriverClassName", this.getDriverClassName()); - map.put("Username", this.getUsername()); - map.put("RemoveAbandonedCount", this.getRemoveAbandonedCount()); - - // 25 - 29 - map.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); - map.put("NotEmptyWaitNanos", this.getNotEmptyWaitNanos()); - map.put("ErrorCount", this.getErrorCount()); - map.put("ReusePreparedStatementCount", this.getCachedPreparedStatementHitCount()); - map.put("StartTransactionCount", this.getStartTransactionCount()); - - // 30 - 34 - map.put("CommitCount", this.getCommitCount()); - map.put("RollbackCount", this.getRollbackCount()); - map.put("LastError", JMXUtils.getErrorCompositeData(this.getLastError())); - map.put("LastCreateError", JMXUtils.getErrorCompositeData(this.getLastCreateError())); - map.put("PreparedStatementCacheDeleteCount", this.getCachedPreparedStatementDeleteCount()); - - // 35 - 39 - map.put("PreparedStatementCacheAccessCount", this.getCachedPreparedStatementAccessCount()); - map.put("PreparedStatementCacheMissCount", this.getCachedPreparedStatementMissCount()); - map.put("PreparedStatementCacheHitCount", this.getCachedPreparedStatementHitCount()); - map.put("PreparedStatementCacheCurrentCount", this.getCachedPreparedStatementCount()); - map.put("Version", this.getVersion()); - - // 40 - - map.put("LastErrorTime", this.getLastErrorTime()); - map.put("LastCreateErrorTime", this.getLastCreateErrorTime()); - map.put("CreateErrorCount", this.getCreateErrorCount()); - map.put("DiscardCount", this.getDiscardCount()); - - return map; - } catch (JMException ex) { - throw new IllegalStateException("getStatData error", ex); - } - } - - public Map getStatData() { - final int activeCount; - final int activePeak; - final Date activePeakTime; - - final int poolingCount; - final int poolingPeak; - final Date poolingPeakTime; - - final long connectCount; - final long closeCount; - - lock.lock(); - try { - poolingCount = this.poolingCount; - poolingPeak = this.poolingPeak; - poolingPeakTime = this.getPoolingPeakTime(); - - activeCount = this.activeCount; - activePeak = this.activePeak; - activePeakTime = this.getActivePeakTime(); - - connectCount = this.connectCount; - closeCount = this.closeCount; - } finally { - lock.unlock(); - } - Map dataMap = new LinkedHashMap(); - - dataMap.put("Identity", System.identityHashCode(this)); - dataMap.put("Name", this.getName()); - dataMap.put("DbType", this.getDbType()); - dataMap.put("DriverClassName", this.getDriverClassName()); - - dataMap.put("URL", this.getUrl()); - dataMap.put("UserName", this.getUsername()); - dataMap.put("FilterClassNames", this.getFilterClassNames()); - - dataMap.put("WaitThreadCount", this.getWaitThreadCount()); - dataMap.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); - dataMap.put("NotEmptyWaitMillis", this.getNotEmptyWaitMillis()); - - dataMap.put("PoolingCount", poolingCount); - dataMap.put("PoolingPeak", poolingPeak); - dataMap.put("PoolingPeakTime", poolingPeakTime); - - dataMap.put("ActiveCount", activeCount); - dataMap.put("ActivePeak", activePeak); - dataMap.put("ActivePeakTime", activePeakTime); - - dataMap.put("InitialSize", this.getInitialSize()); - dataMap.put("MinIdle", this.getMinIdle()); - dataMap.put("MaxActive", this.getMaxActive()); - - dataMap.put("QueryTimeout", this.getQueryTimeout()); - dataMap.put("TransactionQueryTimeout", this.getTransactionQueryTimeout()); - dataMap.put("LoginTimeout", this.getLoginTimeout()); - dataMap.put("ValidConnectionCheckerClassName", this.getValidConnectionCheckerClassName()); - dataMap.put("ExceptionSorterClassName", this.getExceptionSorterClassName()); - - dataMap.put("TestOnBorrow", this.isTestOnBorrow()); - dataMap.put("TestOnReturn", this.isTestOnReturn()); - dataMap.put("TestWhileIdle", this.isTestWhileIdle()); - - dataMap.put("DefaultAutoCommit", this.isDefaultAutoCommit()); - dataMap.put("DefaultReadOnly", this.getDefaultReadOnly()); - dataMap.put("DefaultTransactionIsolation", this.getDefaultTransactionIsolation()); - - dataMap.put("LogicConnectCount", connectCount); - dataMap.put("LogicCloseCount", closeCount); - dataMap.put("LogicConnectErrorCount", this.getConnectErrorCount()); - - dataMap.put("PhysicalConnectCount", this.getCreateCount()); - dataMap.put("PhysicalCloseCount", this.getDestroyCount()); - dataMap.put("PhysicalConnectErrorCount", this.getCreateErrorCount()); - - dataMap.put("ExecuteCount", this.getExecuteCount()); - dataMap.put("ErrorCount", this.getErrorCount()); - dataMap.put("CommitCount", this.getCommitCount()); - dataMap.put("RollbackCount", this.getRollbackCount()); - - dataMap.put("PSCacheAccessCount", this.getCachedPreparedStatementAccessCount()); - dataMap.put("PSCacheHitCount", this.getCachedPreparedStatementHitCount()); - dataMap.put("PSCacheMissCount", this.getCachedPreparedStatementMissCount()); - - dataMap.put("StartTransactionCount", this.getStartTransactionCount()); - dataMap.put("TransactionHistogram", this.getTransactionHistogramValues()); - - dataMap.put("ConnectionHoldTimeHistogram", this.getDataSourceStat().getConnectionHoldHistogram().toArray()); - dataMap.put("RemoveAbandoned", this.isRemoveAbandoned()); - dataMap.put("ClobOpenCount", this.getDataSourceStat().getClobOpenCount()); - dataMap.put("BlobOpenCount", this.getDataSourceStat().getBlobOpenCount()); - - return dataMap; - } - - public JdbcSqlStat getSqlStat(int sqlId) { - return this.getDataSourceStat().getSqlStat(sqlId); - } - - public JdbcSqlStat getSqlStat(long sqlId) { - return this.getDataSourceStat().getSqlStat(sqlId); - } - - public Map getSqlStatMap() { - return this.getDataSourceStat().getSqlStatMap(); - } - - public Map getWallStatMap() { - WallProviderStatValue wallStatValue = getWallStatValue(false); - - if (wallStatValue != null) { - return wallStatValue.toMap(); - } - - return null; - } - - public WallProviderStatValue getWallStatValue(boolean reset) { - for (Filter filter : this.filters) { - if (filter instanceof WallFilter) { - WallFilter wallFilter = (WallFilter) filter; - return wallFilter.getProvider().getStatValue(reset); - } - } - - return null; - } - - public Lock getLock() { - return lock; - } - - @Override - public boolean isWrapperFor(Class iface) { - for (Filter filter : this.filters) { - if (filter.isWrapperFor(iface)) { - return true; - } - } - - if (this.statLogger != null - && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { - return true; - } - - return super.isWrapperFor(iface); - } - - @SuppressWarnings("unchecked") - public T unwrap(Class iface) { - for (Filter filter : this.filters) { - if (filter.isWrapperFor(iface)) { - return (T) filter; - } - } - - if (this.statLogger != null - && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { - return (T) statLogger; - } - - return super.unwrap(iface); - } - - public boolean isLogDiffrentThread() { - return logDiffrentThread; - } - - public void setLogDiffrentThread(boolean logDiffrentThread) { - this.logDiffrentThread = logDiffrentThread; - } -} +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.pool; + +import static com.alibaba.druid.util.Utils.getBoolean; + +import java.io.Closeable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.sql.Connection; +import java.sql.SQLException; +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.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import javax.management.JMException; +import javax.management.ObjectName; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; +import javax.sql.ConnectionEvent; +import javax.sql.ConnectionEventListener; +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; + +import com.alibaba.druid.Constants; +import com.alibaba.druid.TransactionTimeoutException; +import com.alibaba.druid.VERSION; +import com.alibaba.druid.filter.AutoLoad; +import com.alibaba.druid.filter.Filter; +import com.alibaba.druid.filter.FilterChainImpl; +import com.alibaba.druid.mock.MockDriver; +import com.alibaba.druid.pool.DruidPooledPreparedStatement.PreparedStatementKey; +import com.alibaba.druid.pool.vendor.DB2ExceptionSorter; +import com.alibaba.druid.pool.vendor.InformixExceptionSorter; +import com.alibaba.druid.pool.vendor.MSSQLValidConnectionChecker; +import com.alibaba.druid.pool.vendor.MockExceptionSorter; +import com.alibaba.druid.pool.vendor.MySqlExceptionSorter; +import com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker; +import com.alibaba.druid.pool.vendor.OracleExceptionSorter; +import com.alibaba.druid.pool.vendor.OracleValidConnectionChecker; +import com.alibaba.druid.pool.vendor.SybaseExceptionSorter; +import com.alibaba.druid.proxy.DruidDriver; +import com.alibaba.druid.proxy.jdbc.DataSourceProxyConfig; +import com.alibaba.druid.proxy.jdbc.TransactionInfo; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.stat.DruidDataSourceStatManager; +import com.alibaba.druid.stat.JdbcDataSourceStat; +import com.alibaba.druid.stat.JdbcSqlStat; +import com.alibaba.druid.stat.JdbcSqlStatValue; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JMXUtils; +import com.alibaba.druid.util.JdbcConstants; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.StringUtils; +import com.alibaba.druid.util.Utils; +import com.alibaba.druid.wall.WallFilter; +import com.alibaba.druid.wall.WallProviderStatValue; + +/** + * @author ljw + * @author wenshao + */ +public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource { + + private final static Log LOG = LogFactory.getLog(DruidDataSource.class); + + private static final long serialVersionUID = 1L; + + // stats + private final AtomicLong recycleErrorCount = new AtomicLong(); + private long connectCount = 0L; + private long closeCount = 0L; + private final AtomicLong connectErrorCount = new AtomicLong(); + private long recycleCount = 0L; + private long removeAbandonedCount = 0L; + private long notEmptyWaitCount = 0L; + private long notEmptySignalCount = 0L; + private long notEmptyWaitNanos = 0L; + + private int activePeak = 0; + private long activePeakTime = 0; + private int poolingPeak = 0; + private long poolingPeakTime = 0; + + // store + private volatile DruidConnectionHolder[] connections; + private int poolingCount = 0; + private int activeCount = 0; + private long discardCount = 0; + private int notEmptyWaitThreadCount = 0; + private int notEmptyWaitThreadPeak = 0; + + // threads + private CreateConnectionThread createConnectionThread; + private DestroyConnectionThread destoryConnectionThread; + private LogStatsThread logStatsThread; + + private final CountDownLatch initedLatch = new CountDownLatch(2); + + private volatile boolean enable = true; + + private boolean resetStatEnable = true; + private final AtomicLong resetCount = new AtomicLong(); + + private String initStackTrace; + + private volatile boolean closed = false; + private long closeTimeMillis = -1L; + + protected JdbcDataSourceStat dataSourceStat; + + private boolean useGloalDataSourceStat = false; + + private boolean mbeanRegistered = false; + + public static ThreadLocal waitNanosLocal = new ThreadLocal(); + + private boolean logDiffrentThread = true; + + public DruidDataSource(){ + this(false); + } + + public DruidDataSource(boolean fairLock){ + super(fairLock); + + configFromPropety(System.getProperties()); + } + + public void configFromPropety(Properties properties) { + { + Boolean value = getBoolean(properties, "druid.testWhileIdle"); + if (value != null) { + this.setTestWhileIdle(value); + } + } + { + Boolean value = getBoolean(properties, "druid.testOnBorrow"); + if (value != null) { + this.setTestOnBorrow(value); + } + } + { + String property = properties.getProperty("druid.validationQuery"); + if (property != null && property.length() > 0) { + this.setValidationQuery(property); + } + } + { + Boolean value = getBoolean(properties, "druid.useGloalDataSourceStat"); + if (value != null) { + this.setUseGloalDataSourceStat(value); + } + } + { + String property = properties.getProperty("druid.filters"); + + if (property != null && property.length() > 0) { + try { + this.setFilters(property); + } catch (SQLException e) { + LOG.error("setFilters error", e); + } + } + } + { + String property = properties.getProperty(Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS); + if (property != null && property.length() > 0) { + try { + long value = Long.parseLong(property); + this.setTimeBetweenLogStatsMillis(value); + } catch (NumberFormatException e) { + LOG.error("illegal property '" + Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS + "'", e); + } + } + } + { + String property = properties.getProperty(Constants.DRUID_STAT_SQL_MAX_SIZE); + if (property != null && property.length() > 0) { + try { + int value = Integer.parseInt(property); + if (dataSourceStat != null) { + dataSourceStat.setMaxSqlSize(value); + } + } catch (NumberFormatException e) { + LOG.error("illegal property '" + Constants.DRUID_STAT_SQL_MAX_SIZE + "'", e); + } + } + } + { + Boolean value = getBoolean(properties, "druid.clearFiltersEnable"); + if (value != null) { + this.setClearFiltersEnable(value); + } + } + { + Boolean value = getBoolean(properties, "druid.resetStatEnable"); + if (value != null) { + this.setResetStatEnable(value); + } + } + } + + public boolean isUseGloalDataSourceStat() { + return useGloalDataSourceStat; + } + + public void setUseGloalDataSourceStat(boolean useGloalDataSourceStat) { + this.useGloalDataSourceStat = useGloalDataSourceStat; + } + + public String getInitStackTrace() { + return initStackTrace; + } + + public boolean isResetStatEnable() { + return resetStatEnable; + } + + public void setResetStatEnable(boolean resetStatEnable) { + this.resetStatEnable = resetStatEnable; + if (dataSourceStat != null) { + dataSourceStat.setResetStatEnable(resetStatEnable); + } + } + + public long getDiscardCount() { + return discardCount; + } + + public void restart() { + lock.lock(); + try { + if (LOG.isInfoEnabled()) { + LOG.info("{dataSource-" + this.getID() + "} restart"); + } + + this.close(); + this.resetStat(); + this.inited = false; + this.enable = true; + this.closed = false; + } finally { + lock.unlock(); + } + } + + public void resetStat() { + if (!isResetStatEnable()) { + return; + } + + lock.lock(); + try { + connectCount = 0; + closeCount = 0; + discardCount = 0; + recycleCount = 0; + createCount.set(0); + destroyCount.set(0); + removeAbandonedCount = 0; + notEmptyWaitCount = 0; + notEmptySignalCount = 0L; + notEmptyWaitNanos = 0; + + activePeak = 0; + activePeakTime = 0; + poolingPeak = 0; + createTimespan = 0; + lastError = null; + lastErrorTimeMillis = 0; + lastCreateError = null; + lastCreateErrorTimeMillis = 0; + } finally { + lock.unlock(); + } + + connectErrorCount.set(0); + errorCount.set(0); + commitCount.set(0); + rollbackCount.set(0); + startTransactionCount.set(0); + cachedPreparedStatementHitCount.set(0); + closedPreparedStatementCount.set(0); + preparedStatementCount.set(0); + transactionHistogram.reset(); + cachedPreparedStatementDeleteCount.set(0); + recycleErrorCount.set(0); + + resetCount.incrementAndGet(); + } + + public long getResetCount() { + return this.resetCount.get(); + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + lock.lock(); + try { + this.enable = enable; + if (!enable) { + notEmpty.signalAll(); + notEmptySignalCount++; + } + } finally { + lock.unlock(); + } + } + + public void setPoolPreparedStatements(boolean value) { + if (this.poolPreparedStatements == value) { + return; + } + + this.poolPreparedStatements = value; + + if (!inited) { + return; + } + + if (LOG.isInfoEnabled()) { + LOG.info("set poolPreparedStatements " + this.poolPreparedStatements + " -> " + value); + } + + if (!value) { + lock.lock(); + try { + + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder connection = connections[i]; + + for (PreparedStatementHolder holder : connection.getStatementPool().getMap().values()) { + closePreapredStatement(holder); + } + + connection.getStatementPool().getMap().clear(); + } + } finally { + lock.unlock(); + } + } + } + + public void setMaxActive(int maxActive) { + if (this.maxActive == maxActive) { + return; + } + + if (maxActive == 0) { + throw new IllegalArgumentException("maxActive can't not set zero"); + } + + if (!inited) { + this.maxActive = maxActive; + return; + } + + if (maxActive < this.minIdle) { + throw new IllegalArgumentException("maxActive less than minIdle, " + maxActive + " < " + this.minIdle); + } + + if (LOG.isInfoEnabled()) { + LOG.info("maxActive changed : " + this.maxActive + " -> " + maxActive); + } + + lock.lock(); + try { + int allCount = this.poolingCount + this.activeCount; + + if (maxActive > allCount) { + this.connections = Arrays.copyOf(this.connections, maxActive); + } else { + this.connections = Arrays.copyOf(this.connections, allCount); + } + + this.maxActive = maxActive; + } finally { + lock.unlock(); + } + } + + @SuppressWarnings("rawtypes") + public void setConnectProperties(Properties properties) { + if (properties == null) { + properties = new Properties(); + } + + boolean equals; + if (properties.size() == this.connectProperties.size()) { + equals = true; + for (Map.Entry entry : properties.entrySet()) { + Object value = this.connectProperties.get(entry.getKey()); + Object entryValue = entry.getValue(); + if (value == null && entryValue != null) { + equals = false; + break; + } + + if (!value.equals(entry.getValue())) { + equals = false; + break; + } + } + } else { + equals = false; + } + + if (!equals) { + if (inited && LOG.isInfoEnabled()) { + LOG.info("connectProperties changed : " + this.connectProperties + " -> " + properties); + } + + configFromPropety(properties); + + for (Filter filter : this.filters) { + filter.configFromProperties(properties); + } + + if (exceptionSorter != null) { + exceptionSorter.configFromProperties(properties); + } + + if (validConnectionChecker != null) { + validConnectionChecker.configFromProperties(properties); + } + + if (statLogger != null) { + statLogger.configFromProperties(properties); + } + } + + this.connectProperties = properties; + } + + public boolean isInited() { + return this.inited; + } + + public void init() throws SQLException { + if (inited) { + return; + } + + final ReentrantLock lock = this.lock; + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + throw new SQLException("interrupt", e); + } + + boolean init = false; + try { + if (inited) { + return; + } + + init = true; + + initStackTrace = Utils.toString(Thread.currentThread().getStackTrace()); + + this.id = DruidDriver.createDataSourceId(); + if (this.id > 1) { + long delta = (this.id - 1) * 100000; + this.connectionIdSeed.addAndGet(delta); + this.statementIdSeed.addAndGet(delta); + this.resultSetIdSeed.addAndGet(delta); + this.transactionIdSeed.addAndGet(delta); + } + + if (this.jdbcUrl != null) { + this.jdbcUrl = this.jdbcUrl.trim(); + initFromWrapDriverUrl(); + } + + if (this.dbType == null || this.dbType.length() == 0) { + this.dbType = JdbcUtils.getDbType(jdbcUrl, null); + } + + for (Filter filter : filters) { + filter.init(this); + } + + if (JdbcConstants.MYSQL.equals(this.dbType) || // + JdbcConstants.MARIADB.equals(this.dbType)) { + boolean cacheServerConfigurationSet = false; + if (this.connectProperties.containsKey("cacheServerConfiguration")) { + cacheServerConfigurationSet = true; + } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) { + cacheServerConfigurationSet = true; + } + if (cacheServerConfigurationSet) { + this.connectProperties.put("cacheServerConfiguration", "true"); + } + } + + if (maxActive <= 0) { + throw new IllegalArgumentException("illegal maxActive " + maxActive); + } + + if (maxActive < minIdle) { + throw new IllegalArgumentException("illegal maxActive " + maxActive); + } + + if (getInitialSize() > maxActive) { + throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActieve " + + maxActive); + } + + if (timeBetweenLogStatsMillis > 0 && useGloalDataSourceStat) { + throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGloalDataSourceStat=true"); + } + + if (this.driverClass != null) { + this.driverClass = driverClass.trim(); + } + + initFromSPIServiceLoader(); + + if (this.driver == null) { + if (this.driverClass == null || this.driverClass.isEmpty()) { + this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); + } + + if (MockDriver.class.getName().equals(driverClass)) { + driver = MockDriver.instance; + } else { + driver = JdbcUtils.createDriver(driverClassLoader, driverClass); + } + } else { + if (this.driverClass == null) { + this.driverClass = driver.getClass().getName(); + } + } + + initCheck(); + + initExceptionSorter(); + initValidConnectionChecker(); + validationQueryCheck(); + + if (isUseGloalDataSourceStat()) { + dataSourceStat = JdbcDataSourceStat.getGlobal(); + if (dataSourceStat == null) { + dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType); + JdbcDataSourceStat.setGlobal(dataSourceStat); + } + if (dataSourceStat.getDbType() == null) { + dataSourceStat.setDbType(this.getDbType()); + } + } else { + dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties); + } + dataSourceStat.setResetStatEnable(this.resetStatEnable); + + connections = new DruidConnectionHolder[maxActive]; + + SQLException connectError = null; + + try { + // init connections + for (int i = 0, size = getInitialSize(); i < size; ++i) { + Connection conn = createPhysicalConnection(); + DruidConnectionHolder holder = new DruidConnectionHolder(this, conn); + connections[poolingCount++] = holder; + } + + if (poolingCount > 0) { + poolingPeak = poolingCount; + poolingPeakTime = System.currentTimeMillis(); + } + } catch (SQLException ex) { + LOG.error("init datasource error", ex); + connectError = ex; + } + + createAndLogThread(); + createAndStartCreatorThread(); + createAndStartDestroyThread(); + + initedLatch.await(); + + initedTime = new Date(); + registerMbean(); + + if (connectError != null && poolingCount == 0) { + throw connectError; + } + } catch (SQLException e) { + LOG.error("dataSource init error", e); + throw e; + } catch (InterruptedException e) { + throw new SQLException(e.getMessage(), e); + } finally { + inited = true; + lock.unlock(); + + if (init && LOG.isInfoEnabled()) { + LOG.info("{dataSource-" + this.getID() + "} inited"); + } + } + } + + private void createAndLogThread() { + if (this.timeBetweenLogStatsMillis <= 0) { + return; + } + + String threadName = "Druid-ConnectionPool-Log-" + System.identityHashCode(this); + logStatsThread = new LogStatsThread(threadName); + logStatsThread.start(); + + this.resetStatEnable = false; + } + + private void createAndStartDestroyThread() { + String threadName = "Druid-ConnectionPool-Destory-" + System.identityHashCode(this); + destoryConnectionThread = new DestroyConnectionThread(threadName); + destoryConnectionThread.start(); + } + + protected void createAndStartCreatorThread() { + String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this); + createConnectionThread = new CreateConnectionThread(threadName); + createConnectionThread.start(); + } + + /** + * load filters from SPI ServiceLoader + * + * @see ServiceLoader + */ + private void initFromSPIServiceLoader() { + + String property = System.getProperty("druid.load.spifilter.skip"); + if (property != null) { + return; + } + + ServiceLoader druidAutoFilterLoader = ServiceLoader.load(Filter.class); + + for (Filter autoFilter : druidAutoFilterLoader) { + AutoLoad autoLoad = autoFilter.getClass().getAnnotation(AutoLoad.class); + if (autoLoad != null && autoLoad.value()) { + if (LOG.isInfoEnabled()) { + LOG.info("load filter from spi :" + autoFilter.getClass().getName()); + } + addFilter(autoFilter); + } + } + } + + private void initFromWrapDriverUrl() throws SQLException { + if (!jdbcUrl.startsWith(DruidDriver.DEFAULT_PREFIX)) { + return; + } + + DataSourceProxyConfig config = DruidDriver.parseConfig(jdbcUrl, null); + this.driverClass = config.getRawDriverClassName(); + + LOG.error("error url : '" + jdbcUrl + "', it should be : '" + config.getRawUrl() + "'"); + + this.jdbcUrl = config.getRawUrl(); + if (this.name == null) { + this.name = config.getName(); + } + + for (Filter filter : config.getFilters()) { + addFilter(filter); + } + } + + /** + * 会去重复 + * + * @param filter + */ + private void addFilter(Filter filter) { + boolean exists = false; + for (Filter initedFilter : this.filters) { + if (initedFilter.getClass() == filter.getClass()) { + exists = true; + break; + } + } + + if (!exists) { + filter.init(this); + this.filters.add(filter); + } + + } + + private void validationQueryCheck() { + if (!(isTestOnBorrow() || isTestOnReturn() || isTestWhileIdle())) { + return; + } + + if (this.validConnectionChecker != null) { + return; + } + + if (this.getValidationQuery() != null && this.getValidationQuery().length() > 0) { + return; + } + + String errorMessage = ""; + + if (isTestOnBorrow()) { + errorMessage += "testOnBorrow is true, "; + } + + if (isTestOnReturn()) { + errorMessage += "testOnReturn is true, "; + } + + if (isTestWhileIdle()) { + errorMessage += "testWhileIdle is true, "; + } + + LOG.error(errorMessage + "validationQuery not set"); + } + + protected void initCheck() throws SQLException { + if (JdbcUtils.ORACLE.equals(this.dbType)) { + isOracle = true; + + if (driver.getMajorVersion() < 10) { + throw new SQLException("not support oracle driver " + driver.getMajorVersion() + "." + + driver.getMinorVersion()); + } + + if (driver.getMajorVersion() == 10 && isUseOracleImplicitCache()) { + this.getConnectProperties().setProperty("oracle.jdbc.FreeMemoryOnEnterImplicitCache", "true"); + } + + oracleValidationQueryCheck(); + } else if (JdbcUtils.DB2.equals(dbType)) { + db2ValidationQueryCheck(); + } + } + + private void oracleValidationQueryCheck() { + if (validationQuery == null) { + return; + } + if (validationQuery.length() == 0) { + return; + } + + SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); + List stmtList = sqlStmtParser.parseStatementList(); + + if (stmtList.size() != 1) { + return; + } + + SQLStatement stmt = stmtList.get(0); + if (!(stmt instanceof SQLSelectStatement)) { + return; + } + + SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); + if (query instanceof SQLSelectQueryBlock) { + if (((SQLSelectQueryBlock) query).getFrom() == null) { + LOG.error("invalid oracle validationQuery. " + validationQuery + ", may should be : " + validationQuery + + " FROM DUAL"); + } + } + } + + private void db2ValidationQueryCheck() { + if (validationQuery == null) { + return; + } + if (validationQuery.length() == 0) { + return; + } + + SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); + List stmtList = sqlStmtParser.parseStatementList(); + + if (stmtList.size() != 1) { + return; + } + + SQLStatement stmt = stmtList.get(0); + if (!(stmt instanceof SQLSelectStatement)) { + return; + } + + SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); + if (query instanceof SQLSelectQueryBlock) { + if (((SQLSelectQueryBlock) query).getFrom() == null) { + LOG.error("invalid db2 validationQuery. " + validationQuery + ", may should be : " + validationQuery + + " FROM SYSDUMMY"); + } + } + } + + private void initValidConnectionChecker() { + String realDriverClassName = driver.getClass().getName(); + if (realDriverClassName.equals("com.mysql.jdbc.Driver")) { + this.validConnectionChecker = new MySqlValidConnectionChecker(); + } else if (realDriverClassName.equals("oracle.jdbc.driver.OracleDriver")) { + this.validConnectionChecker = new OracleValidConnectionChecker(); + } else if (realDriverClassName.equals("com.microsoft.jdbc.sqlserver.SQLServerDriver")) { + this.validConnectionChecker = new MSSQLValidConnectionChecker(); + } + } + + private void initExceptionSorter() { + String realDriverClassName = driver.getClass().getName(); + if (realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER)) { + this.exceptionSorter = new MySqlExceptionSorter(); + } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)) { + 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(); + + } else if (realDriverClassName.equals("com.alibaba.druid.mock.MockDriver")) { + this.exceptionSorter = new MockExceptionSorter(); + } else if (realDriverClassName.contains("DB2")) { + this.exceptionSorter = new DB2ExceptionSorter(); + } + } + + @Override + public DruidPooledConnection getConnection() throws SQLException { + return getConnection(maxWait); + } + + public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException { + init(); + + if (filters.size() > 0) { + FilterChainImpl filterChain = new FilterChainImpl(this); + return filterChain.dataSource_connect(this, maxWaitMillis); + } else { + return getConnectionDirect(maxWaitMillis); + } + } + + @Override + public PooledConnection getPooledConnection() throws SQLException { + return getConnection(maxWait); + } + + @Override + public PooledConnection getPooledConnection(String user, String password) throws SQLException { + throw new UnsupportedOperationException("Not supported by DruidDataSource"); + } + + public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException { + for (;;) { + DruidPooledConnection poolableConnection = getConnectionInternal(maxWaitMillis); + + if (isTestOnBorrow()) { + boolean validate = testConnectionInternal(poolableConnection.getConnection()); + if (!validate) { + if (LOG.isDebugEnabled()) { + LOG.debug("skip not validate connection."); + } + + Connection realConnection = poolableConnection.getConnection(); + discardConnection(realConnection); + continue; + } + } else { + Connection realConnection = poolableConnection.getConnection(); + if (realConnection.isClosed()) { + discardConnection(null); // 传入null,避免重复关闭 + continue; + } + + if (isTestWhileIdle()) { + final long currentTimeMillis = System.currentTimeMillis(); + final long lastActiveTimeMillis = poolableConnection.getConnectionHolder().getLastActiveTimeMillis(); + final long idleMillis = currentTimeMillis - lastActiveTimeMillis; + long timeBetweenEvictionRunsMillis = this.getTimeBetweenEvictionRunsMillis(); + if (timeBetweenEvictionRunsMillis <= 0) { + timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + } + + if (idleMillis >= timeBetweenEvictionRunsMillis) { + boolean validate = testConnectionInternal(poolableConnection.getConnection()); + if (!validate) { + if (LOG.isDebugEnabled()) { + LOG.debug("skip not validate connection."); + } + + discardConnection(realConnection); + continue; + } + } + } + } + + if (isRemoveAbandoned()) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + poolableConnection.setConnectStackTrace(stackTrace); + poolableConnection.setConnectedTimeNano(); + poolableConnection.setTraceEnable(true); + + synchronized (activeConnections) { + activeConnections.put(poolableConnection, PRESENT); + } + } + + if (!this.isDefaultAutoCommit()) { + poolableConnection.setAutoCommit(false); + } + + return poolableConnection; + } + } + + /** + * 抛弃连接,不进行回收,而是抛弃 + * + * @param realConnection + * @throws SQLException + */ + public void discardConnection(Connection realConnection) { + JdbcUtils.close(realConnection); + + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + LOG.error("interrupt"); + return; + } + try { + activeCount--; + discardCount++; + + if (activeCount <= 0) { + empty.signal(); + } + } finally { + lock.unlock(); + } + } + + private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException { + if (closed) { + connectErrorCount.incrementAndGet(); + throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis)); + } + + if (!enable) { + connectErrorCount.incrementAndGet(); + throw new DataSourceDisableException(); + } + + final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait); + final int maxWaitThreadCount = getMaxWaitThreadCount(); + + DruidConnectionHolder holder; + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + connectErrorCount.incrementAndGet(); + throw new SQLException("interrupt", e); + } + + try { + if (maxWaitThreadCount > 0) { + if (notEmptyWaitThreadCount >= maxWaitThreadCount) { + connectErrorCount.incrementAndGet(); + throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count " + + lock.getQueueLength()); + } + } + + connectCount++; + + if (maxWait > 0) { + holder = pollLast(nanos); + } else { + holder = takeLast(); + } + + if (holder != null) { + activeCount++; + if (activeCount > activePeak) { + activePeak = activeCount; + activePeakTime = System.currentTimeMillis(); + } + } + } catch (InterruptedException e) { + connectErrorCount.incrementAndGet(); + throw new SQLException(e.getMessage(), e); + } catch (SQLException e) { + connectErrorCount.incrementAndGet(); + throw e; + } finally { + lock.unlock(); + } + + if (holder == null) { + long waitNanos = waitNanosLocal.get(); + + StringBuilder buf = new StringBuilder(); + buf.append("wait millis ")// + .append(waitNanos / (1000 * 1000))// + .append(", active " + activeCount)// + ; + + List sqlList = this.getDataSourceStat().getRuningSqlList(); + for (int i = 0; i < sqlList.size(); ++i) { + if (i != 0) { + buf.append('\n'); + } else { + buf.append(", "); + } + JdbcSqlStatValue sql = sqlList.get(i); + buf.append("runningSqlCount "); + buf.append(sql.getRunningCount()); + buf.append(" : "); + buf.append(sql.getSql()); + } + + String errorMessage = buf.toString(); + + if (this.createError != null) { + throw new GetConnectionTimeoutException(errorMessage, createError); + } else { + throw new GetConnectionTimeoutException(errorMessage); + } + } + + holder.incrementUseCount(); + + DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder); + return poolalbeConnection; + } + + public void handleConnectionException(DruidPooledConnection pooledConnection, Throwable t) throws SQLException { + final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); + + errorCount.incrementAndGet(); + lastError = t; + lastErrorTimeMillis = System.currentTimeMillis(); + + if (t instanceof SQLException) { + SQLException sqlEx = (SQLException) t; + + // broadcastConnectionError + ConnectionEvent event = new ConnectionEvent(pooledConnection, sqlEx); + for (ConnectionEventListener eventListener : holder.getConnectionEventListeners()) { + eventListener.connectionErrorOccurred(event); + } + + // exceptionSorter.isExceptionFatal + if (exceptionSorter != null && exceptionSorter.isExceptionFatal(sqlEx)) { + if (pooledConnection.isTraceEnable()) { + synchronized (activeConnections) { + if (pooledConnection.isTraceEnable()) { + activeConnections.remove(pooledConnection); + pooledConnection.setTraceEnable(false); + } + } + } + this.discardConnection(holder.getConnection()); + holder.setDiscard(true); + pooledConnection.disable(t); + + LOG.error("discard connection", sqlEx); + } + + throw sqlEx; + } else { + throw new SQLException("Error", t); + } + } + + /** + * 回收连接 + */ + protected void recycle(DruidPooledConnection pooledConnection) throws SQLException { + final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); + + if (holder == null) { + LOG.warn("connectionHolder is null"); + return; + } + + if (logDiffrentThread && pooledConnection.getOwnerThread() != Thread.currentThread()) { + LOG.warn("get/close not same thread"); + } + + final Connection physicalConnection = holder.getConnection(); + + if (pooledConnection.isTraceEnable()) { + synchronized (activeConnections) { + if (pooledConnection.isTraceEnable()) { + Object oldInfo = activeConnections.remove(pooledConnection); + if (oldInfo == null) { + if (LOG.isWarnEnabled()) { + LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size()); + } + } + pooledConnection.setTraceEnable(false); + } + } + } + + final boolean isAutoCommit = holder.isUnderlyingAutoCommit(); + final boolean isReadOnly = holder.isUnderlyingReadOnly(); + final boolean testOnReturn = this.isTestOnReturn(); + + try { + // check need to rollback? + if ((!isAutoCommit) && (!isReadOnly)) { + pooledConnection.rollback(); + } + + // reset holder, restore default settings, clear warnings + holder.reset(); + + if (testOnReturn) { + boolean validate = testConnectionInternal(physicalConnection); + if (!validate) { + JdbcUtils.close(physicalConnection); + + destroyCount.incrementAndGet(); + + lock.lockInterruptibly(); + try { + activeCount--; + closeCount++; + } finally { + lock.unlock(); + } + return; + } + } + + if (!enable) { + discardConnection(holder.getConnection()); + return; + } + + final long lastActiveTimeMillis = System.currentTimeMillis(); + lock.lockInterruptibly(); + try { + activeCount--; + closeCount++; + + putLast(holder, lastActiveTimeMillis); + recycleCount++; + } finally { + lock.unlock(); + } + } catch (Throwable e) { + holder.clearStatementCache(); + + if (!holder.isDiscard()) { + this.discardConnection(physicalConnection); + holder.setDiscard(true); + } + + LOG.error("recyle error", e); + recycleErrorCount.incrementAndGet(); + } + } + + public long getRecycleErrorCount() { + return recycleErrorCount.get(); + } + + public void clearStatementCache() throws SQLException { + lock.lock(); + try { + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder conn = connections[i]; + conn.getStatementPool().clear(); + } + } finally { + lock.unlock(); + } + } + + /** + * close datasource + */ + public void close() { + lock.lock(); + try { + if (this.closed) { + return; + } + + if (!this.inited) { + return; + } + + if (logStatsThread != null) { + logStatsThread.interrupt(); + } + + if (createConnectionThread != null) { + createConnectionThread.interrupt(); + } + + if (destoryConnectionThread != null) { + destoryConnectionThread.interrupt(); + } + + for (int i = 0; i < poolingCount; ++i) { + try { + DruidConnectionHolder connHolder = connections[i]; + + for (PreparedStatementHolder stmtHolder : connHolder.getStatementPool().getMap().values()) { + connHolder.getStatementPool().closeRemovedStatement(stmtHolder); + } + connHolder.getStatementPool().getMap().clear(); + + Connection physicalConnection = connHolder.getConnection(); + physicalConnection.close(); + connections[i] = null; + destroyCount.incrementAndGet(); + } catch (Exception ex) { + LOG.warn("close connection error", ex); + } + } + poolingCount = 0; + unregisterMbean(); + + enable = false; + notEmpty.signalAll(); + notEmptySignalCount++; + + this.closed = true; + this.closeTimeMillis = System.currentTimeMillis(); + + for (Filter filter : filters) { + filter.destory(); + } + } finally { + lock.unlock(); + } + + if (LOG.isInfoEnabled()) { + LOG.info("{dataSource-" + this.getID() + "} closed"); + } + } + + public void registerMbean() { + if (!mbeanRegistered) { + AccessController.doPrivileged(new PrivilegedAction() { + + @Override + public Object run() { + ObjectName objectName = DruidDataSourceStatManager.addDataSource(DruidDataSource.this, + DruidDataSource.this.name); + DruidDataSource.this.setObjectName(objectName); + DruidDataSource.this.mbeanRegistered = true; + return null; + } + }); + } + } + + public void unregisterMbean() { + if (mbeanRegistered) { + AccessController.doPrivileged(new PrivilegedAction() { + + @Override + public Object run() { + DruidDataSourceStatManager.removeDataSource(DruidDataSource.this); + DruidDataSource.this.mbeanRegistered = false; + return null; + } + }); + } + } + + public boolean isMbeanRegistered() { + return mbeanRegistered; + } + + void putLast(DruidConnectionHolder e, long lastActiveTimeMillis) { + e.setLastActiveTimeMillis(lastActiveTimeMillis); + connections[poolingCount++] = e; + + if (poolingCount > poolingPeak) { + poolingPeak = poolingCount; + poolingPeakTime = lastActiveTimeMillis; + } + + notEmpty.signal(); + notEmptySignalCount++; + } + + DruidConnectionHolder takeLast() throws InterruptedException, SQLException { + try { + while (poolingCount == 0) { + empty.signal(); // send signal to CreateThread create connection + notEmptyWaitThreadCount++; + if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { + notEmptyWaitThreadPeak = notEmptyWaitThreadCount; + } + try { + notEmpty.await(); // signal by recycle or creator + } finally { + notEmptyWaitThreadCount--; + } + notEmptyWaitCount++; + + if (!enable) { + connectErrorCount.incrementAndGet(); + throw new DataSourceDisableException(); + } + } + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + notEmptySignalCount++; + throw ie; + } + + poolingCount--; + DruidConnectionHolder last = connections[poolingCount]; + connections[poolingCount] = null; + + return last; + } + + DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException { + long estimate = nanos; + + for (;;) { + if (poolingCount == 0) { + empty.signal(); // send signal to CreateThread create connection + + if (estimate <= 0) { + waitNanosLocal.set(nanos - estimate); + return null; + } + + notEmptyWaitThreadCount++; + if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { + notEmptyWaitThreadPeak = notEmptyWaitThreadCount; + } + + try { + long startEstimate = estimate; + estimate = notEmpty.awaitNanos(estimate); // signal by + // recycle or + // creator + notEmptyWaitCount++; + notEmptyWaitNanos += (startEstimate - estimate); + + if (!enable) { + connectErrorCount.incrementAndGet(); + throw new DataSourceDisableException(); + } + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + notEmptySignalCount++; + throw ie; + } finally { + notEmptyWaitThreadCount--; + } + + if (poolingCount == 0) { + if (estimate > 0) { + continue; + } + + waitNanosLocal.set(nanos - estimate); + return null; + } + } + + poolingCount--; + DruidConnectionHolder last = connections[poolingCount]; + connections[poolingCount] = null; + + return last; + } + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + if (!StringUtils.equals(username, this.username)) { + throw new UnsupportedOperationException("Not supported by DruidDataSource"); + } + + if (!StringUtils.equals(password, this.password)) { + throw new UnsupportedOperationException("Not supported by DruidDataSource"); + } + + return getConnection(); + } + + public long getCreateCount() { + return createCount.get(); + } + + public long getDestroyCount() { + return destroyCount.get(); + } + + public long getConnectCount() { + lock.lock(); + try { + return connectCount; + } finally { + lock.unlock(); + } + } + + public long getCloseCount() { + return closeCount; + } + + public long getConnectErrorCount() { + return connectErrorCount.get(); + } + + @Override + public int getPoolingCount() { + lock.lock(); + try { + return poolingCount; + } finally { + lock.unlock(); + } + } + + public int getPoolingPeak() { + lock.lock(); + try { + return poolingPeak; + } finally { + lock.unlock(); + } + } + + public Date getPoolingPeakTime() { + if (poolingPeakTime <= 0) { + return null; + } + + return new Date(poolingPeakTime); + } + + public long getRecycleCount() { + return recycleCount; + } + + public int getActiveCount() { + lock.lock(); + try { + return activeCount; + } finally { + lock.unlock(); + } + } + + public void logStats() { + final DruidDataSourceStatLogger statLogger = this.statLogger; + if (statLogger == null) { + return; + } + + DruidDataSourceStatValue statValue = getStatValueAndReset(); + + statLogger.log(statValue); + } + + public DruidDataSourceStatValue getStatValueAndReset() { + DruidDataSourceStatValue value = new DruidDataSourceStatValue(); + + lock.lock(); + try { + value.setPoolingCount(this.poolingCount); + value.setPoolingPeak(this.poolingPeak); + value.setPoolingPeakTime(this.poolingPeakTime); + + value.setActiveCount(this.activeCount); + value.setActivePeak(this.activePeak); + value.setActivePeakTime(this.activePeakTime); + + value.setConnectCount(this.connectCount); + value.setCloseCount(this.closeCount); + value.setWaitThreadCount(lock.getWaitQueueLength(notEmpty)); + value.setNotEmptyWaitCount(this.notEmptyWaitCount); + value.setNotEmptyWaitNanos(this.notEmptyWaitNanos); + + // reset + this.poolingPeak = 0; + this.poolingPeakTime = 0; + this.activePeak = 0; + this.activePeakTime = 0; + this.connectCount = 0; + this.closeCount = 0; + + this.notEmptyWaitCount = 0; + this.notEmptyWaitNanos = 0; + } finally { + lock.unlock(); + } + + value.setName(this.getName()); + value.setDbType(this.getDbType()); + value.setDriverClassName(this.getDriverClassName()); + + value.setUrl(this.getUrl()); + value.setUserName(this.getUsername()); + value.setFilterClassNames(this.getFilterClassNames()); + + value.setInitialSize(this.getInitialSize()); + value.setMinIdle(this.getMinIdle()); + value.setMaxActive(this.getMaxActive()); + + value.setQueryTimeout(this.getQueryTimeout()); + value.setTransactionQueryTimeout(this.getTransactionQueryTimeout()); + value.setLoginTimeout(this.getLoginTimeout()); + value.setValidConnectionCheckerClassName(this.getValidConnectionCheckerClassName()); + value.setExceptionSorterClassName(this.getExceptionSorterClassName()); + + value.setTestOnBorrow(this.isTestOnBorrow()); + value.setTestOnReturn(this.isTestOnReturn()); + value.setTestWhileIdle(this.isTestWhileIdle()); + + value.setDefaultAutoCommit(this.isDefaultAutoCommit()); + + if (defaultReadOnly != null) { + value.setDefaultReadOnly(defaultReadOnly); + } + value.setDefaultTransactionIsolation(this.getDefaultTransactionIsolation()); + + value.setLogicConnectErrorCount(connectErrorCount.getAndSet(0)); + + value.setPhysicalConnectCount(createCount.getAndSet(0)); + value.setPhysicalCloseCount(destroyCount.getAndSet(0)); + value.setPhysicalConnectErrorCount(createErrorCount.getAndSet(0)); + + value.setExecuteCount(this.executeCount.getAndSet(0)); + value.setErrorCount(errorCount.getAndSet(0)); + value.setCommitCount(commitCount.getAndSet(0)); + value.setRollbackCount(rollbackCount.getAndSet(0)); + + value.setPstmtCacheHitCount(cachedPreparedStatementHitCount.getAndSet(0)); + value.setPstmtCacheMissCount(cachedPreparedStatementMissCount.getAndSet(0)); + + value.setStartTransactionCount(startTransactionCount.getAndSet(0)); + value.setTransactionHistogram(this.getTransactionHistogram().toArrayAndReset()); + + value.setConnectionHoldTimeHistogram(this.getDataSourceStat().getConnectionHoldHistogram().toArrayAndReset()); + value.removeAbandoned = this.isRemoveAbandoned(); + value.setClobOpenCount(this.getDataSourceStat().getClobOpenCountAndReset()); + value.setBlobOpenCount(this.getDataSourceStat().getBlobOpenCountAndReset()); + + value.setSqlSkipCount(this.getDataSourceStat().getSkipSqlCountAndReset()); + value.setSqlList(this.getDataSourceStat().getSqlStatMapAndReset()); + + return value; + } + + public long getRemoveAbandonedCount() { + return removeAbandonedCount; + } + + public class CreateConnectionThread extends Thread { + + public CreateConnectionThread(String name){ + super(name); + this.setDaemon(true); + } + + public void run() { + initedLatch.countDown(); + + int errorCount = 0; + for (;;) { + // addLast + try { + lock.lockInterruptibly(); + } catch (InterruptedException e2) { + break; + } + + try { + // 必须存在线程等待,才创建连接 + if (poolingCount >= notEmptyWaitThreadCount) { + empty.await(); + } + + // 防止创建超过maxActive数量的连接 + if (activeCount + poolingCount >= maxActive) { + empty.await(); + continue; + } + + } catch (InterruptedException e) { + lastCreateError = e; + lastErrorTimeMillis = System.currentTimeMillis(); + break; + } finally { + lock.unlock(); + } + + Connection connection = null; + + try { + connection = createPhysicalConnection(); + } catch (SQLException e) { + LOG.error("create connection error", e); + + errorCount++; + + if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { + if (breakAfterAcquireFailure) { + break; + } + + try { + Thread.sleep(timeBetweenConnectErrorMillis); + } catch (InterruptedException interruptEx) { + break; + } + } + } catch (RuntimeException e) { + LOG.error("create connection error", e); + continue; + } catch (Error e) { + LOG.error("create connection error", e); + break; + } + + if (connection == null) { + continue; + } + + DruidConnectionHolder holder = null; + try { + holder = new DruidConnectionHolder(DruidDataSource.this, connection); + } catch (SQLException ex) { + LOG.error("create connection holder error", ex); + break; + } + + lock.lock(); + try { + connections[poolingCount++] = holder; + + if (poolingCount > poolingPeak) { + poolingPeak = poolingCount; + poolingPeakTime = System.currentTimeMillis(); + } + + errorCount = 0; // reset errorCount + + notEmpty.signal(); + notEmptySignalCount++; + } finally { + lock.unlock(); + } + } + } + } + + public class DestroyConnectionThread extends Thread { + + public DestroyConnectionThread(String name){ + super(name); + this.setDaemon(true); + } + + public void run() { + initedLatch.countDown(); + + for (;;) { + // 从前面开始删除 + try { + if (closed) { + break; + } + + if (timeBetweenEvictionRunsMillis > 0) { + Thread.sleep(timeBetweenEvictionRunsMillis); + } else { + Thread.sleep(1000); // + } + + if (Thread.interrupted()) { + break; + } + + shrink(true); + + if (isRemoveAbandoned()) { + removeAbandoned(); + } + } catch (InterruptedException e) { + break; + } + } + } + + } + + public class LogStatsThread extends Thread { + + public LogStatsThread(String name){ + super(name); + this.setDaemon(true); + } + + public void run() { + try { + for (;;) { + try { + logStats(); + } catch (Exception e) { + LOG.error("logStats error", e); + } + + Thread.sleep(timeBetweenLogStatsMillis); + } + } catch (InterruptedException e) { + // skip + } + } + } + + public int removeAbandoned() { + int removeCount = 0; + + long currrentNanos = System.nanoTime(); + + List abandonedList = new ArrayList(); + + synchronized (activeConnections) { + Iterator iter = activeConnections.keySet().iterator(); + + for (; iter.hasNext();) { + DruidPooledConnection pooledConnection = iter.next(); + + if (pooledConnection.isRunning()) { + continue; + } + + long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000); + + if (timeMillis >= removeAbandonedTimeoutMillis) { + iter.remove(); + pooledConnection.setTraceEnable(false); + abandonedList.add(pooledConnection); + } + } + } + + if (abandonedList.size() > 0) { + for (DruidPooledConnection pooledConnection : abandonedList) { + JdbcUtils.close(pooledConnection); + pooledConnection.abandond(); + removeAbandonedCount++; + removeCount++; + + if (isLogAbandoned()) { + StringBuilder buf = new StringBuilder(); + buf.append("abandon connection, open stackTrace\n"); + + StackTraceElement[] trace = pooledConnection.getConnectStackTrace(); + for (int i = 0; i < trace.length; i++) { + buf.append("\tat "); + buf.append(trace[i].toString()); + buf.append("\n"); + } + + LOG.error(buf.toString()); + } + } + } + + return removeCount; + } + + /** Instance key */ + protected String instanceKey = null; + + public Reference getReference() throws NamingException { + final String className = getClass().getName(); + final String factoryName = className + "Factory"; // XXX: not robust + Reference ref = new Reference(className, factoryName, null); + ref.add(new StringRefAddr("instanceKey", instanceKey)); + ref.add(new StringRefAddr("url", this.getUrl())); + ref.add(new StringRefAddr("username", this.getUsername())); + ref.add(new StringRefAddr("password", this.getPassword())); + // TODO ADD OTHER PROPERTIES + return ref; + } + + @Override + public List getFilterClassNames() { + List names = new ArrayList(); + for (Filter filter : filters) { + names.add(filter.getClass().getName()); + } + return names; + } + + public int getRawDriverMajorVersion() { + int version = -1; + if (this.driver != null) { + version = driver.getMajorVersion(); + } + return version; + } + + public int getRawDriverMinorVersion() { + int version = -1; + if (this.driver != null) { + version = driver.getMinorVersion(); + } + return version; + } + + public String getProperties() { + Properties properties = new Properties(); + properties.putAll(connectProperties); + if (properties.containsKey("password")) { + properties.put("password", "******"); + } + return properties.toString(); + } + + @Override + public void shrink() { + shrink(false); + } + + public void shrink(boolean checkTime) { + final List evictList = new ArrayList(); + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + return; + } + + try { + final int checkCount = poolingCount - minIdle; + final long currentTimeMillis = System.currentTimeMillis(); + for (int i = 0; i < checkCount; ++i) { + DruidConnectionHolder connection = connections[i]; + + if (checkTime) { + long idleMillis = currentTimeMillis - connection.getLastActiveTimeMillis(); + if (idleMillis >= minEvictableIdleTimeMillis) { + evictList.add(connection); + } else { + break; + } + } else { + evictList.add(connection); + } + } + + int removeCount = evictList.size(); + if (removeCount > 0) { + System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount); + Arrays.fill(connections, poolingCount - removeCount, poolingCount, null); + poolingCount -= removeCount; + } + } finally { + lock.unlock(); + } + + for (DruidConnectionHolder item : evictList) { + Connection connection = item.getConnection(); + JdbcUtils.close(connection); + destroyCount.incrementAndGet(); + } + } + + public int getWaitThreadCount() { + lock.lock(); + try { + return lock.getWaitQueueLength(notEmpty); + } finally { + lock.unlock(); + } + } + + public long getNotEmptyWaitCount() { + return notEmptyWaitCount; + } + + public int getNotEmptyWaitThreadCount() { + lock.lock(); + try { + return notEmptyWaitThreadCount; + } finally { + lock.unlock(); + } + } + + public int getNotEmptyWaitThreadPeak() { + lock.lock(); + try { + return notEmptyWaitThreadPeak; + } finally { + lock.unlock(); + } + } + + public long getNotEmptySignalCount() { + return notEmptySignalCount; + } + + public long getNotEmptyWaitMillis() { + return notEmptyWaitNanos / (1000 * 1000); + } + + public long getNotEmptyWaitNanos() { + return notEmptyWaitNanos; + } + + public int getLockQueueLength() { + return lock.getQueueLength(); + } + + public int getActivePeak() { + return activePeak; + } + + public Date getActivePeakTime() { + if (activePeakTime <= 0) { + return null; + } + + return new Date(activePeakTime); + } + + public String dump() { + lock.lock(); + try { + return this.toString(); + } finally { + lock.unlock(); + } + } + + public long getErrorCount() { + return this.errorCount.get(); + } + + public String toString() { + StringBuilder buf = new StringBuilder(); + + buf.append("{"); + + buf.append("\n\tCreateTime:\""); + buf.append(Utils.toString(getCreatedTime())); + buf.append("\""); + + buf.append(",\n\tActiveCount:"); + buf.append(getActiveCount()); + + buf.append(",\n\tPoolingCount:"); + buf.append(getPoolingCount()); + + buf.append(",\n\tCreateCount:"); + buf.append(getCreateCount()); + + buf.append(",\n\tDestroyCount:"); + buf.append(getDestroyCount()); + + buf.append(",\n\tCloseCount:"); + buf.append(getCloseCount()); + + buf.append(",\n\tConnectCount:"); + buf.append(getConnectCount()); + + buf.append(",\n\tConnections:["); + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder conn = connections[i]; + if (conn != null) { + if (i != 0) { + buf.append(","); + } + buf.append("\n\t\t"); + buf.append(conn.toString()); + } + } + buf.append("\n\t]"); + + buf.append("\n}"); + + if (this.isPoolPreparedStatements()) { + buf.append("\n\n["); + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder conn = connections[i]; + if (conn != null) { + if (i != 0) { + buf.append(","); + } + buf.append("\n\t{\n\tID:"); + buf.append(System.identityHashCode(conn.getConnection())); + PreparedStatementPool pool = conn.getStatementPool(); + + buf.append(", \n\tpoolStatements:["); + + int entryIndex = 0; + try { + for (Map.Entry entry : pool.getMap().entrySet()) { + if (entryIndex != 0) { + buf.append(","); + } + buf.append("\n\t\t{hitCount:"); + buf.append(entry.getValue().getHitCount()); + buf.append(",sql:\""); + buf.append(entry.getKey().getSql()); + buf.append("\""); + buf.append("\t}"); + + entryIndex++; + } + } catch (ConcurrentModificationException e) { + // skip .. + } + + buf.append("\n\t\t]"); + + buf.append("\n\t}"); + } + } + buf.append("\n]"); + } + + return buf.toString(); + } + + public List> getPoolingConnectionInfo() { + List> list = new ArrayList>(); + lock.lock(); + try { + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder connHolder = connections[i]; + Connection conn = connHolder.getConnection(); + + Map map = new LinkedHashMap(); + map.put("id", System.identityHashCode(conn)); + map.put("useCount", connHolder.getUseCount()); + if (connHolder.getLastActiveTimeMillis() > 0) { + map.put("lastActiveTime", new Date(connHolder.getLastActiveTimeMillis())); + } + map.put("connectTime", new Date(connHolder.getTimeMillis())); + map.put("holdability", connHolder.getUnderlyingHoldability()); + map.put("transactionIsolation", connHolder.getUnderlyingTransactionIsolation()); + map.put("autoCommit", connHolder.isUnderlyingAutoCommit()); + map.put("readoOnly", connHolder.isUnderlyingReadOnly()); + + if (connHolder.isPoolPreparedStatements()) { + List> stmtCache = new ArrayList>(); + PreparedStatementPool stmtPool = connHolder.getStatementPool(); + for (PreparedStatementHolder stmtHolder : stmtPool.getMap().values()) { + Map stmtInfo = new LinkedHashMap(); + + stmtInfo.put("sql", stmtHolder.getKey().getSql()); + stmtInfo.put("defaultRowPretch", stmtHolder.getDefaultRowPretch()); + stmtInfo.put("rowPrefetch", stmtHolder.getRowPrefetch()); + stmtInfo.put("hitCount", stmtHolder.getHitCount()); + + stmtCache.add(stmtInfo); + } + + map.put("pscache", stmtCache); + } + + list.add(map); + } + } finally { + lock.unlock(); + } + return list; + } + + public void logTransaction(TransactionInfo info) { + long transactionMillis = info.getEndTimeMillis() - info.getStartTimeMillis(); + if (transactionThresholdMillis > 0 && transactionMillis > transactionThresholdMillis) { + StringBuilder buf = new StringBuilder(); + buf.append("long time transaction, take "); + buf.append(transactionMillis); + buf.append(" ms : "); + for (String sql : info.getSqlList()) { + buf.append(sql); + buf.append(";"); + } + LOG.error(buf.toString(), new TransactionTimeoutException()); + } + } + + @Override + public String getVersion() { + return VERSION.getVersionNumber(); + } + + @Override + public JdbcDataSourceStat getDataSourceStat() { + return dataSourceStat; + } + + public Object clone() throws CloneNotSupportedException { + return cloneDruidDataSource(); + } + + public DruidDataSource cloneDruidDataSource() { + DruidDataSource x = new DruidDataSource(); + + cloneTo(x); + + return x; + } + + public Map getStatDataForMBean() { + try { + Map map = new HashMap(); + + // 0 - 4 + map.put("Name", this.getName()); + map.put("URL", this.getUrl()); + map.put("CreateCount", this.getCreateCount()); + map.put("DestroyCount", this.getDestroyCount()); + map.put("ConnectCount", this.getConnectCount()); + + // 5 - 9 + map.put("CloseCount", this.getCloseCount()); + map.put("ActiveCount", this.getActivePeak()); + map.put("PoolingCount", this.getPoolingCount()); + map.put("LockQueueLength", this.getLockQueueLength()); + map.put("WaitThreadCount", this.getNotEmptyWaitThreadPeak()); + + // 10 - 14 + map.put("InitialSize", this.getInitialSize()); + map.put("MaxActive", this.getMaxActive()); + map.put("MinIdle", this.getMinIdle()); + map.put("PoolPreparedStatements", this.isPoolPreparedStatements()); + map.put("TestOnBorrow", this.isTestOnBorrow()); + + // 15 - 19 + map.put("TestOnReturn", this.isTestOnReturn()); + map.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis()); + map.put("ConnectErrorCount", this.getConnectErrorCount()); + map.put("CreateTimespanMillis", this.getCreateTimespanMillis()); + map.put("DbType", this.getDbType()); + + // 20 - 24 + map.put("ValidationQuery", this.getValidationQuery()); + map.put("ValidationQueryTimeout", this.getValidationQueryTimeout()); + map.put("DriverClassName", this.getDriverClassName()); + map.put("Username", this.getUsername()); + map.put("RemoveAbandonedCount", this.getRemoveAbandonedCount()); + + // 25 - 29 + map.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); + map.put("NotEmptyWaitNanos", this.getNotEmptyWaitNanos()); + map.put("ErrorCount", this.getErrorCount()); + map.put("ReusePreparedStatementCount", this.getCachedPreparedStatementHitCount()); + map.put("StartTransactionCount", this.getStartTransactionCount()); + + // 30 - 34 + map.put("CommitCount", this.getCommitCount()); + map.put("RollbackCount", this.getRollbackCount()); + map.put("LastError", JMXUtils.getErrorCompositeData(this.getLastError())); + map.put("LastCreateError", JMXUtils.getErrorCompositeData(this.getLastCreateError())); + map.put("PreparedStatementCacheDeleteCount", this.getCachedPreparedStatementDeleteCount()); + + // 35 - 39 + map.put("PreparedStatementCacheAccessCount", this.getCachedPreparedStatementAccessCount()); + map.put("PreparedStatementCacheMissCount", this.getCachedPreparedStatementMissCount()); + map.put("PreparedStatementCacheHitCount", this.getCachedPreparedStatementHitCount()); + map.put("PreparedStatementCacheCurrentCount", this.getCachedPreparedStatementCount()); + map.put("Version", this.getVersion()); + + // 40 - + map.put("LastErrorTime", this.getLastErrorTime()); + map.put("LastCreateErrorTime", this.getLastCreateErrorTime()); + map.put("CreateErrorCount", this.getCreateErrorCount()); + map.put("DiscardCount", this.getDiscardCount()); + + return map; + } catch (JMException ex) { + throw new IllegalStateException("getStatData error", ex); + } + } + + public Map getStatData() { + final int activeCount; + final int activePeak; + final Date activePeakTime; + + final int poolingCount; + final int poolingPeak; + final Date poolingPeakTime; + + final long connectCount; + final long closeCount; + + lock.lock(); + try { + poolingCount = this.poolingCount; + poolingPeak = this.poolingPeak; + poolingPeakTime = this.getPoolingPeakTime(); + + activeCount = this.activeCount; + activePeak = this.activePeak; + activePeakTime = this.getActivePeakTime(); + + connectCount = this.connectCount; + closeCount = this.closeCount; + } finally { + lock.unlock(); + } + Map dataMap = new LinkedHashMap(); + + dataMap.put("Identity", System.identityHashCode(this)); + dataMap.put("Name", this.getName()); + dataMap.put("DbType", this.getDbType()); + dataMap.put("DriverClassName", this.getDriverClassName()); + + dataMap.put("URL", this.getUrl()); + dataMap.put("UserName", this.getUsername()); + dataMap.put("FilterClassNames", this.getFilterClassNames()); + + dataMap.put("WaitThreadCount", this.getWaitThreadCount()); + dataMap.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); + dataMap.put("NotEmptyWaitMillis", this.getNotEmptyWaitMillis()); + + dataMap.put("PoolingCount", poolingCount); + dataMap.put("PoolingPeak", poolingPeak); + dataMap.put("PoolingPeakTime", poolingPeakTime); + + dataMap.put("ActiveCount", activeCount); + dataMap.put("ActivePeak", activePeak); + dataMap.put("ActivePeakTime", activePeakTime); + + dataMap.put("InitialSize", this.getInitialSize()); + dataMap.put("MinIdle", this.getMinIdle()); + dataMap.put("MaxActive", this.getMaxActive()); + + dataMap.put("QueryTimeout", this.getQueryTimeout()); + dataMap.put("TransactionQueryTimeout", this.getTransactionQueryTimeout()); + dataMap.put("LoginTimeout", this.getLoginTimeout()); + dataMap.put("ValidConnectionCheckerClassName", this.getValidConnectionCheckerClassName()); + dataMap.put("ExceptionSorterClassName", this.getExceptionSorterClassName()); + + dataMap.put("TestOnBorrow", this.isTestOnBorrow()); + dataMap.put("TestOnReturn", this.isTestOnReturn()); + dataMap.put("TestWhileIdle", this.isTestWhileIdle()); + + dataMap.put("DefaultAutoCommit", this.isDefaultAutoCommit()); + dataMap.put("DefaultReadOnly", this.getDefaultReadOnly()); + dataMap.put("DefaultTransactionIsolation", this.getDefaultTransactionIsolation()); + + dataMap.put("LogicConnectCount", connectCount); + dataMap.put("LogicCloseCount", closeCount); + dataMap.put("LogicConnectErrorCount", this.getConnectErrorCount()); + + dataMap.put("PhysicalConnectCount", this.getCreateCount()); + dataMap.put("PhysicalCloseCount", this.getDestroyCount()); + dataMap.put("PhysicalConnectErrorCount", this.getCreateErrorCount()); + + dataMap.put("ExecuteCount", this.getExecuteCount()); + dataMap.put("ErrorCount", this.getErrorCount()); + dataMap.put("CommitCount", this.getCommitCount()); + dataMap.put("RollbackCount", this.getRollbackCount()); + + dataMap.put("PSCacheAccessCount", this.getCachedPreparedStatementAccessCount()); + dataMap.put("PSCacheHitCount", this.getCachedPreparedStatementHitCount()); + dataMap.put("PSCacheMissCount", this.getCachedPreparedStatementMissCount()); + + dataMap.put("StartTransactionCount", this.getStartTransactionCount()); + dataMap.put("TransactionHistogram", this.getTransactionHistogramValues()); + + dataMap.put("ConnectionHoldTimeHistogram", this.getDataSourceStat().getConnectionHoldHistogram().toArray()); + dataMap.put("RemoveAbandoned", this.isRemoveAbandoned()); + dataMap.put("ClobOpenCount", this.getDataSourceStat().getClobOpenCount()); + dataMap.put("BlobOpenCount", this.getDataSourceStat().getBlobOpenCount()); + + return dataMap; + } + + public JdbcSqlStat getSqlStat(int sqlId) { + return this.getDataSourceStat().getSqlStat(sqlId); + } + + public JdbcSqlStat getSqlStat(long sqlId) { + return this.getDataSourceStat().getSqlStat(sqlId); + } + + public Map getSqlStatMap() { + return this.getDataSourceStat().getSqlStatMap(); + } + + public Map getWallStatMap() { + WallProviderStatValue wallStatValue = getWallStatValue(false); + + if (wallStatValue != null) { + return wallStatValue.toMap(); + } + + return null; + } + + public WallProviderStatValue getWallStatValue(boolean reset) { + for (Filter filter : this.filters) { + if (filter instanceof WallFilter) { + WallFilter wallFilter = (WallFilter) filter; + return wallFilter.getProvider().getStatValue(reset); + } + } + + return null; + } + + public Lock getLock() { + return lock; + } + + @Override + public boolean isWrapperFor(Class iface) { + for (Filter filter : this.filters) { + if (filter.isWrapperFor(iface)) { + return true; + } + } + + if (this.statLogger != null + && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { + return true; + } + + return super.isWrapperFor(iface); + } + + @SuppressWarnings("unchecked") + public T unwrap(Class iface) { + for (Filter filter : this.filters) { + if (filter.isWrapperFor(iface)) { + return (T) filter; + } + } + + if (this.statLogger != null + && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { + return (T) statLogger; + } + + return super.unwrap(iface); + } + + public boolean isLogDiffrentThread() { + return logDiffrentThread; + } + + public void setLogDiffrentThread(boolean logDiffrentThread) { + this.logDiffrentThread = logDiffrentThread; + } +} From 2eacefe2f1869e7b5bec72e21495a9bbf5ff74d2 Mon Sep 17 00:00:00 2001 From: YANGLiiN Date: Thu, 5 Jun 2014 20:18:01 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=20#557=20=E6=B7=BB=E5=8A=A0=E5=AF=B9log4jd?= =?UTF-8?q?bc=E6=94=AF=E6=8C=81=EF=BC=8C=E6=96=B9=E4=BE=BF=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E6=89=93=E5=8D=B0mybatis=E5=8A=A8=E6=80=81SQL?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/alibaba/druid/util/JdbcConstants.java | 6 ++++ .../com/alibaba/druid/util/JdbcUtils.java | 31 ++++++------------- .../META-INF/druid-driver.properties | 5 +-- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/alibaba/druid/util/JdbcConstants.java b/src/main/java/com/alibaba/druid/util/JdbcConstants.java index f203e65ce2..0feecaad52 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcConstants.java +++ b/src/main/java/com/alibaba/druid/util/JdbcConstants.java @@ -59,4 +59,10 @@ public interface JdbcConstants { * 阿里云odps */ public static final String ODPS = "odps"; + + /** + * Log4JDBC + */ + public static final String LOG4JDBC = "log4jdbc"; + public static final String LOG4JDBC_DRIVER = "net.sf.log4jdbc.DriverSpy"; } diff --git a/src/main/java/com/alibaba/druid/util/JdbcUtils.java b/src/main/java/com/alibaba/druid/util/JdbcUtils.java index 4521d3f5e0..c5fad63161 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcUtils.java +++ b/src/main/java/com/alibaba/druid/util/JdbcUtils.java @@ -15,32 +15,17 @@ */ package com.alibaba.druid.util; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; + +import javax.sql.DataSource; import java.io.Closeable; import java.io.InputStream; import java.io.PrintStream; import java.net.URL; -import java.sql.Connection; +import java.sql.*; import java.sql.Date; -import java.sql.Driver; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Types; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Enumeration; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import javax.sql.DataSource; - -import com.alibaba.druid.support.logging.Log; -import com.alibaba.druid.support.logging.LogFactory; +import java.util.*; /** * @author wenshao @@ -351,6 +336,8 @@ public final class JdbcUtils implements JdbcConstants { return "org.apache.derby.jdbc.EmbeddedDriver"; } else if (rawUrl.startsWith("jdbc:mysql:")) { return MYSQL_DRIVER; + } else if (rawUrl.startsWith("jdbc:log4jdbc:")) { + return LOG4JDBC_DRIVER; } else if (rawUrl.startsWith("jdbc:mariadb:")) { return MARIADB_DRIVER; } else if (rawUrl.startsWith("jdbc:oracle:") // @@ -421,6 +408,8 @@ public final class JdbcUtils implements JdbcConstants { return DERBY; } else if (rawUrl.startsWith("jdbc:mysql:")) { return MYSQL; + } else if (rawUrl.startsWith("jdbc:log4jdbc:")) { + return LOG4JDBC; } else if (rawUrl.startsWith("jdbc:mariadb:")) { return MARIADB; } else if (rawUrl.startsWith("jdbc:oracle:")) { diff --git a/src/main/resources/META-INF/druid-driver.properties b/src/main/resources/META-INF/druid-driver.properties index 95f94a1c44..9b1fcc926c 100644 --- a/src/main/resources/META-INF/druid-driver.properties +++ b/src/main/resources/META-INF/druid-driver.properties @@ -1,6 +1,7 @@ jdbc:derby:=org.apache.derby.jdbc.EmbeddedDriver -jdbc:mysql:=com.mysql.jdbc.Driver -jdbc:oracle:=oracle.jdbc.driver.OracleDriver +jdbc:mysql:=com.mysql.jdbc.Driver +jdbc:log4jdbc:=net.sf.log4jdbc.DriverSpy +jdbc:oracle:=oracle.jdbc.driver.OracleDriver jdbc:microsoft:=com.microsoft.jdbc.sqlserver.SQLServerDriver jdbc:jtds:=net.sourceforge.jtds.jdbc.Driver jdbc:postgresql:=org.postgresql.Driver From 9a253ff54b518f65320402aebc7c1a475a548cfd Mon Sep 17 00:00:00 2001 From: yakolee Date: Thu, 12 Jun 2014 13:40:52 +0800 Subject: [PATCH 6/6] update import in JdbcUtils --- .../com/alibaba/druid/util/JdbcUtils.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/alibaba/druid/util/JdbcUtils.java b/src/main/java/com/alibaba/druid/util/JdbcUtils.java index c5fad63161..765a2062b2 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcUtils.java +++ b/src/main/java/com/alibaba/druid/util/JdbcUtils.java @@ -15,17 +15,32 @@ */ package com.alibaba.druid.util; -import com.alibaba.druid.support.logging.Log; -import com.alibaba.druid.support.logging.LogFactory; - -import javax.sql.DataSource; import java.io.Closeable; import java.io.InputStream; import java.io.PrintStream; import java.net.URL; -import java.sql.*; +import java.sql.Connection; import java.sql.Date; -import java.util.*; +import java.sql.Driver; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.sql.DataSource; + +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; /** * @author wenshao