Package com.alibaba.druid.pool

Source Code of com.alibaba.druid.pool.DruidDataSource$ActiveConnectionTraceInfo

/*
* 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 java.io.Closeable;
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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;

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 com.alibaba.druid.TransactionTimeoutException;
import com.alibaba.druid.VERSION;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.logging.Log;
import com.alibaba.druid.logging.LogFactory;
import com.alibaba.druid.mock.MockDriver;
import com.alibaba.druid.pool.DruidPooledPreparedStatement.PreparedStatementKey;
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.stat.DruidDataSourceStatManager;
import com.alibaba.druid.stat.JdbcDataSourceStat;
import com.alibaba.druid.util.IOUtils;
import com.alibaba.druid.util.JdbcUtils;

/**
* @author ljw<ljw2083@alibaba-inc.com>
* @author wenshao<szujobs@hotmail.com>
*/
public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable {

    private final static Log        LOG                     = LogFactory.getLog(DruidDataSource.class);

    private static final long       serialVersionUID        = 1L;

    private final Condition         notEmpty                = lock.newCondition();
    private final Condition         empty                   = lock.newCondition();

    // stats
    private long                    connectCount            = 0L;
    private long                    closeCount              = 0L;
    private long                    connectErrorCount       = 0L;
    private long                    recycleCount            = 0L;
    private long                    createConnectionCount   = 0L;
    private long                    destroyCount            = 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 ConnectionHolder[]      connections;
    private int                     poolingCount            = 0;
    private int                     activeCount             = 0;
    private int                     notEmptyWaitThreadCount = 0;
    private int                     notEmptyWaitThreadPeak  = 0;

    // threads
    private CreateConnectionThread  createConnectionThread;
    private DestroyConnectionThread destoryConnectionThread;

    private final CountDownLatch    initedLatch             = new CountDownLatch(2);

    private boolean                 enable                  = true;

    private boolean                 resetStatEnable         = true;

    private String                  initStackTrace;

    private boolean                 closed                  = false;

    private JdbcDataSourceStat      dataSourceStat;

    public DruidDataSource(){
    }

    public String getInitStackTrace() {
        return initStackTrace;
    }

    public boolean isResetStatEnable() {
        return resetStatEnable;
    }

    public void setResetStatEnable(boolean resetStatEnable) {
        this.resetStatEnable = resetStatEnable;
    }

    public void restart() {
        lock.lock();
        try {
            this.close();
            this.resetStat();
            this.inited = false;
            this.enable = true;
        } finally {
            lock.unlock();
        }
    }

    public void resetStat() {
        if (!resetStatEnable) {
            return;
        }

        lock.lock();
        try {
            connectCount = 0;
            closeCount = 0;
            connectErrorCount = 0;
            recycleCount = 0;
            createConnectionCount = 0;
            destroyCount = 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();
        }

        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);
    }

    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) {
        lock.lock();
        try {
            if (this.poolPreparedStatements && (!value)) {
                for (int i = 0; i < poolingCount; ++i) {
                    ConnectionHolder connection = connections[i];

                    for (PreparedStatementHolder holder : connection.getStatementPool().getMap().values()) {
                        closePreapredStatement(holder);
                        decrementCachedPreparedStatementCount();
                    }

                    connection.getStatementPool().getMap().clear();
                }
            }
            super.setPoolPreparedStatements(value);
        } finally {
            lock.unlock();
        }
    }

    public boolean isInited() {
        return this.inited;
    }

    public void init() throws SQLException {
        if (inited) {
            return;
        }

        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }

        try {
            if (inited) {
                return;
            }

            initStackTrace = IOUtils.toString(Thread.currentThread().getStackTrace());

            this.id = DruidDriver.createDataSourceId();

            if (maxActive <= 0) {
                throw new IllegalArgumentException("illegal maxActive " + maxActive);
            }

            if (maxActive < minIdle) {
                throw new IllegalArgumentException("illegal maxActive " + maxActive);
            }

            if (maxIdle <= 0 || maxIdle < minIdle) {
                throw new IllegalArgumentException("illegal maxPoolSize");
            }

            if (getInitialSize() > maxActive) {
                throw new IllegalArgumentException("illegal initialSize");
            }

            if (this.driverClass != null) {
                this.driverClass = driverClass.trim();
            }

            if (this.jdbcUrl != null) {
                this.jdbcUrl = this.jdbcUrl.trim();

                if (jdbcUrl.startsWith(DruidDriver.DEFAULT_PREFIX)) {
                    DataSourceProxyConfig config = DruidDriver.parseConfig(jdbcUrl, null);
                    this.driverClass = config.getRawDriverClassName();
                    this.jdbcUrl = config.getRawUrl();
                    if (this.name == null) {
                        this.name = config.getName();
                    }
                    this.filters.addAll(config.getFilters());
                }
            }

            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(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());
            }
           
            if ("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");
                }
            }

            String realDriverClassName = driver.getClass().getName();
            if (realDriverClassName.equals("com.mysql.jdbc.Driver")) {
                this.validConnectionChecker = new MySqlValidConnectionChecker();
                this.exceptionSorter = new MySqlExceptionSorter();

            } else if (realDriverClassName.equals("oracle.jdbc.driver.OracleDriver")) {
                this.validConnectionChecker = new OracleValidConnectionChecker();
                this.exceptionSorter = new OracleExceptionSorter();

            } else if (realDriverClassName.equals("com.microsoft.jdbc.sqlserver.SQLServerDriver")) {
                this.validConnectionChecker = new MSSQLValidConnectionChecker();

            } 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();
            }
           
            dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl);
           
            {
                String property = System.getProperty("druid.filters");
                if (property != null && property.length() > 0) {
                    this.setFilters(property);
                }
            }

            for (Filter filter : filters) {
                filter.init(this);
            }

            initConnectionFactory();

            connections = new ConnectionHolder[maxActive];

            SQLException connectError = null;

            try {
                // 初始化连接
                for (int i = 0, size = getInitialSize(); i < size; ++i) {
                    Connection conn = connectionFactory.createConnection();
                    conn.setAutoCommit(true);
                    connections[poolingCount++] = new ConnectionHolder(this, conn);
                }
            } catch (SQLException ex) {
                LOG.error("init datasource error", ex);
                connectError = ex;
            }

            createConnectionThread = new CreateConnectionThread("Druid-ConnectionPool-Create");
            createConnectionThread.setDaemon(true);
            destoryConnectionThread = new DestroyConnectionThread("Druid-ConnectionPool-Destory");
            destoryConnectionThread.setDaemon(true);

            createConnectionThread.start();
            destoryConnectionThread.start();

            initedLatch.await();

            createdTime = new Date();
            DruidDataSourceStatManager.add(this);

            if (connectError != null && poolingCount == 0) {
                throw connectError;
            }
        } catch (InterruptedException e) {
            throw new SQLException(e.getMessage(), e);
        } finally {
            inited = true;

            lock.unlock();
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        return getConnection(maxWait);
    }

    public Connection getConnection(long maxWaitMillis) throws SQLException {
        init();

        final int maxWaitThreadCount = getMaxWaitThreadCount();
        if (maxWaitThreadCount > 0) {
            if (notEmptyWaitThreadCount > maxWaitThreadCount) {
                lock.lock();
                try {
                    connectErrorCount++;
                } finally {
                    lock.unlock();
                }
                throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
                                       + lock.getQueueLength());
            }
        }

        for (;;) {
            DruidPooledConnection poolalbeConnection = getConnectionInternal(maxWaitMillis);

            if (isTestOnBorrow()) {
                boolean validate = testConnectionInternal(poolalbeConnection.getConnection());
                if (!validate) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("skip not validate connection.");
                    }

                    Connection realConnection = poolalbeConnection.getConnection();
                    discardConnection(realConnection);
                    continue;
                }
            } else {
                Connection realConnection = poolalbeConnection.getConnection();
                if (realConnection.isClosed()) {
                    discardConnection(null); // 传入null,避免重复关闭
                    continue;
                }

                if (isTestWhileIdle()) {
                    long idleMillis = System.currentTimeMillis()
                                      - poolalbeConnection.getConnectionHolder().getLastActiveTimeMillis();
                    if (idleMillis >= this.getTimeBetweenEvictionRunsMillis()) {
                        boolean validate = testConnectionInternal(poolalbeConnection.getConnection());
                        if (!validate) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("skip not validate connection.");
                            }

                            discardConnection(realConnection);
                            continue;
                        }
                    }
                }
            }

            if (isRemoveAbandoned()) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                activeConnections.put(poolalbeConnection,
                                      new ActiveConnectionTraceInfo(poolalbeConnection, System.currentTimeMillis(),
                                                                    stackTrace));
                poolalbeConnection.setTraceEnable(true);
            }

            if (!this.isDefaultAutoCommit()) {
                poolalbeConnection.setAutoCommit(false);
            }

            return poolalbeConnection;
        }
    }

    /**
     * 抛弃连接,不进行回收,而是抛弃
     *
     * @param realConnection
     * @throws SQLException
     */
    private void discardConnection(Connection realConnection) throws SQLException {
        JdbcUtils.close(realConnection);

        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }

        try {
            activeCount--;
        } finally {
            lock.unlock();
        }
    }

    private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
        DruidPooledConnection poolalbeConnection;

        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }

        try {
            if (!enable) {
                connectErrorCount++;
                throw new DataSourceDisableException();
            }

            connectCount++;

            ConnectionHolder holder;

            if (maxWait > 0) {
                holder = pollLast(maxWait, TimeUnit.MILLISECONDS);
            } else {
                holder = takeLast();
            }

            if (holder == null) {
                throw new SQLException("can not get connection");
            }

            holder.incrementUseCount();
            activeCount++;
            if (activeCount > activePeak) {
                activePeak = activeCount;
                activePeakTime = System.currentTimeMillis();
            }

            poolalbeConnection = new DruidPooledConnection(holder);
        } catch (InterruptedException e) {
            connectErrorCount++;
            throw new SQLException(e.getMessage(), e);
        } catch (SQLException e) {
            connectErrorCount++;
            throw e;
        } finally {
            lock.unlock();
        }
        return poolalbeConnection;
    }

    public void handleConnectionException(DruidPooledConnection pooledConnection, Throwable t) throws SQLException {
        final ConnectionHolder 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()) {
                    activeConnections.remove(pooledConnection);
                }
                this.discardConnection(holder.getConnection());
                pooledConnection.disable();
            }

            throw sqlEx;
        } else {
            throw new SQLException("Error", t);
        }
    }

    /**
     * 回收连接
     */
    protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
        final Connection conn = pooledConnection.getConnection();
        final ConnectionHolder holder = pooledConnection.getConnectionHolder();

        assert holder != null;

        if (pooledConnection.isTraceEnable()) {
            ActiveConnectionTraceInfo oldInfo = activeConnections.remove(pooledConnection);
            if (oldInfo == null) {
                LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size());
            }
        }

        try {
            // 第一步,检查连接是否关闭
            if (conn == null) {
                lock.lockInterruptibly();
                try {
                    activeCount--;
                    closeCount++;
                } finally {
                    lock.unlock();
                }
                return;
            }

            final boolean isAutoCommit = holder.isUnderlyingAutoCommit();
            final boolean isReadOnly = holder.isUnderlyingReadOnly();

            // check need to rollback?
            if ((!isAutoCommit) && (!isReadOnly)) {
                pooledConnection.rollback();
            }

            // reset holder, restore default settings, clear warnings
            holder.reset();

            if (isTestOnReturn()) {
                boolean validate = testConnectionInternal(conn);
                if (!validate) {
                    JdbcUtils.close(conn);

                    lock.lockInterruptibly();
                    try {
                        destroyCount++;
                        activeCount--;
                        closeCount++;
                    } finally {
                        lock.unlock();
                    }
                    return;
                }
            }

            boolean neadDestory = false;
            lock.lockInterruptibly();
            try {
                if (holder.getModCount() == this.modCount) {
                    activeCount--;
                    closeCount++;

                    // 第六步,加入队列中(putLast)
                    putLast(holder);
                    recycleCount++;
                } else {
                    destroyCount++;
                    activeCount--;
                    closeCount++;
                    neadDestory = true;
                }
            } finally {
                lock.unlock();
            }

            if (neadDestory) {
                JdbcUtils.close(conn);
            }
        } catch (Throwable e) {
            JdbcUtils.close(conn);

            try {
                lock.lockInterruptibly();
            } catch (InterruptedException interruptEx) {
                throw new SQLException("interrupt", interruptEx);
            }

            try {
                activeCount--;
                closeCount++;
            } finally {
                lock.unlock();
            }

            throw new SQLException("recyle error", e);
        }
    }

    public void clearStatementCache() throws SQLException {
        lock.lock();
        try {
            for (int i = 0; i < poolingCount; ++i) {
                ConnectionHolder 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 (createConnectionThread != null) {
                createConnectionThread.interrupt();
            }

            if (destoryConnectionThread != null) {
                destoryConnectionThread.interrupt();
            }

            for (int i = 0; i < poolingCount; ++i) {
                try {
                    JdbcUtils.close(connections[i].getConnection());
                    connections[i] = null;
                    destroyCount++;
                } catch (Exception ex) {
                    LOG.warn("close connection error", ex);
                }
            }
            poolingCount = 0;
            DruidDataSourceStatManager.remove(this);

            enable = false;
            notEmpty.signalAll();
            notEmptySignalCount++;

            this.closed = true;

            for (Filter filter : filters) {
                filter.destory();
            }
        } finally {
            lock.unlock();
        }
    }

    void incrementCreateCount() {
        createConnectionCount++;
    }

    void putLast(ConnectionHolder e) throws SQLException {
        if (!enable) {
            discardConnection(e.getConnection());
            return;
        }

        e.setLastActiveTimeMillis(System.currentTimeMillis());
        connections[poolingCount++] = e;

        notEmpty.signal();
        notEmptySignalCount++;
    }

    ConnectionHolder 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++;
                    throw new DataSourceDisableException();
                }
            }
        } catch (InterruptedException ie) {
            notEmpty.signal(); // propagate to non-interrupted thread
            notEmptySignalCount++;
            throw ie;
        }

        poolingCount--;
        ConnectionHolder last = connections[poolingCount];
        connections[poolingCount] = null;

        return last;
    }

    ConnectionHolder pollLast(long timeout, TimeUnit unit) throws InterruptedException, SQLException {
        long estimate = unit.toNanos(timeout);

        for (;;) {
            if (poolingCount == 0) {
                empty.signal(); // send signal to CreateThread create connection

                if (estimate <= 0) {
                    throw new GetConnectionTimeoutException();
                }

                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++;
                        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;
                    }

                    if (createError != null) {
                        throw new GetConnectionTimeoutException(createError);
                    } else {
                        throw new GetConnectionTimeoutException();
                    }
                }
            }

            poolingCount--;
            ConnectionHolder last = connections[poolingCount];
            connections[poolingCount] = null;

            return last;
        }
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        throw new UnsupportedOperationException("Not supported by DruidDataSource");
    }

    public long getCreateCount() {
        return createConnectionCount;
    }

    public long getDestroyCount() {
        return destroyCount;
    }

    public long getConnectCount() {
        return connectCount;
    }

    public long getCloseCount() {
        return closeCount;
    }

    public long getConnectErrorCount() {
        return connectErrorCount;
    }

    @Override
    public int getPoolingCount() {
        lock.lock();
        try {
            return poolingCount;
        } finally {
            lock.unlock();
        }
    }

    public int getPoolingPeak() {
        return poolingPeak;
    }

    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 boolean isBusy() {
        lock.lock();
        try {
            return this.inited && this.activeCount == maxActive && this.poolingCount == 0;
        } finally {
            lock.unlock();
        }
    }

    public long getRemoveAbandonedCount() {
        return removeAbandonedCount;
    }

    public class CreateConnectionThread extends Thread {

        public CreateConnectionThread(String name){
            super(name);
        }

        public void run() {
            initedLatch.countDown();

            int errorCount = 0;
            for (;;) {
                // addLast
                try {
                    lock.lockInterruptibly();
                } catch (InterruptedException e2) {
                    break;
                }

                try {
                    // 必须存在线程等待,才创建连接
                    if (poolingCount >= notEmptyWaitThreadCount) {
                        empty.await();
                        continue;
                    }

                    // 防止创建超过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 = connectionFactory.createConnection();
                } 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;
                }

                lock.lock();
                try {
                    connections[poolingCount++] = new ConnectionHolder(DruidDataSource.this, connection);

                    if (poolingCount > poolingPeak) {
                        poolingPeak = poolingCount;
                        poolingPeakTime = System.currentTimeMillis();
                    }

                    errorCount = 0; // reset errorCount

                    notEmpty.signal();
                    notEmptySignalCount++;
                } catch (SQLException ex) {
                    LOG.error("create connection holder error", ex);
                    break;
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    public class DestroyConnectionThread extends Thread {

        public DestroyConnectionThread(String name){
            super(name);
        }

        public void run() {
            initedLatch.countDown();

            for (;;) {
                // 从前面开始删除
                try {
                    if (timeBetweenEvictionRunsMillis > 0) {
                        Thread.sleep(timeBetweenEvictionRunsMillis);
                    } else {
                        Thread.sleep(1000); //
                    }

                    if (Thread.interrupted()) {
                        break;
                    }

                    shrink(true);

                    if (isRemoveAbandoned()) {
                        removeAbandoned();
                    }
                } catch (InterruptedException e) {
                    break;
                }
            }
        }

    }

    public int removeAbandoned() {
        int removeCount = 0;

        Iterator<Map.Entry<DruidPooledConnection, ActiveConnectionTraceInfo>> iter = activeConnections.entrySet().iterator();

        long currentMillis = System.currentTimeMillis();

        List<DruidPooledConnection> abondonedList = new ArrayList<DruidPooledConnection>();

        for (; iter.hasNext();) {
            Map.Entry<DruidPooledConnection, ActiveConnectionTraceInfo> entry = iter.next();
            ActiveConnectionTraceInfo activeInfo = entry.getValue();
            long timeMillis = currentMillis - activeInfo.getConnectTime();

            if (timeMillis >= removeAbandonedTimeoutMillis) {
                DruidPooledConnection pooledConnection = entry.getKey();
                JdbcUtils.close(pooledConnection);
                removeAbandonedCount++;
                removeCount++;
                abondonedList.add(pooledConnection);

                if (isLogAbandoned()) {
                    StringBuilder buf = new StringBuilder();
                    buf.append("abandon connection, open stackTrace\n");

                    StackTraceElement[] trace = activeInfo.getStackTrace();
                    for (int i = 0; i < trace.length; i++) {
                        buf.append("\tat ");
                        buf.append(trace[i].toString());
                        buf.append("\n");
                    }

                    LOG.error(buf.toString());
                }
            }
        }

        // multi-check dup close
        for (DruidPooledConnection conn : abondonedList) {
            activeConnections.remove(conn);
        }

        return removeCount;
    }

    public DataSourceProxyConfig getConfig() {
        return null;
    }

    /** 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));
        return ref;
    }

    static class ActiveConnectionTraceInfo {

        private final DruidPooledConnection connection;
        private final long                  connectTime;
        private final StackTraceElement[]   stackTrace;

        public ActiveConnectionTraceInfo(DruidPooledConnection connection, long connectTime,
                                         StackTraceElement[] stackTrace){
            super();
            this.connection = connection;
            this.connectTime = connectTime;
            this.stackTrace = stackTrace;
        }

        public DruidPooledConnection getConnection() {
            return connection;
        }

        public long getConnectTime() {
            return connectTime;
        }

        public StackTraceElement[] getStackTrace() {
            return stackTrace;
        }
    }

    @Override
    public List<String> getFilterClassNames() {
        List<String> names = new ArrayList<String>();
        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() {
        if (this.connectionProperties == null) {
            return null;
        }

        Properties properties = new Properties(this.connectionProperties);
        if (properties.contains("password")) {
            properties.put("password", "******");
        }
        return properties.toString();
    }

    @Override
    public void shrink() {
        shrink(false);
    }

    public void shrink(boolean checkTime) {
        final List<ConnectionHolder> evictList = new ArrayList<ConnectionHolder>();
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            return;
        }

        try {
            final int checkCount = poolingCount - minIdle;
            for (int i = 0; i < checkCount; ++i) {
                ConnectionHolder connection = connections[i];

                if (checkTime) {
                    long idleMillis = System.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 (ConnectionHolder item : evictList) {
            Connection connection = item.getConnection();
            JdbcUtils.close(connection);
            destroyCount++;
        }
    }

    public int getWaitThreadCount() {
        lock.lock();
        try {
            return lock.getWaitQueueLength(notEmpty);
        } finally {
            lock.unlock();
        }
    }

    public long getNotEmptyWaitCount() {
        return notEmptyWaitCount;
    }

    public int getNotEmptyWaitThreadCount() {
        return notEmptyWaitThreadCount;
    }

    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(IOUtils.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) {
            ConnectionHolder 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) {
                ConnectionHolder 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();

                    if (pool != null) {
                        buf.append(", \n\tpoolStatements:[");

                        int entryIndex = 0;
                        try {
                            for (Map.Entry<PreparedStatementKey, PreparedStatementHolder> 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}");
                            }
                        } catch (ConcurrentModificationException e) {
                            // skip ..
                        }

                        buf.append("\n\t\t]");
                    }

                    buf.append("\n\t}");
                }
            }
            buf.append("\n]");
        }

        return buf.toString();
    }

    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.MajorVersion + "." + VERSION.MinorVersion + "." + VERSION.RevisionVersion;
    }

    @Override
    public JdbcDataSourceStat getDataSourceStat() {
        return dataSourceStat;
    }

}
TOP

Related Classes of com.alibaba.druid.pool.DruidDataSource$ActiveConnectionTraceInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.