content: /*
   * Copyright 1999-2018 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.stat;
  
  import com.alibaba.druid.proxy.DruidDriver;
  import com.alibaba.druid.proxy.jdbc.DataSourceProxyImpl;
  
  import javax.management.JMException;
  import javax.management.openmbean.*;
  
  import java.util.Map;
  import java.util.Set;
  import java.util.concurrent.ConcurrentMap;
  import java.util.concurrent.atomic.AtomicLong;
  
  public final class JdbcStatManager implements JdbcStatManagerMBean {
      private final AtomicLong sqlIdSeed = new AtomicLong(1000);
  
      private static final JdbcStatManager instance = new JdbcStatManager();
  
      private final JdbcConnectionStat connectionStat = new JdbcConnectionStat();
      private final JdbcResultSetStat resultSetStat = new JdbcResultSetStat();
      private final JdbcStatementStat statementStat = new JdbcStatementStat();
  
      private final AtomicLong resetCount = new AtomicLong();
  
      public final ThreadLocal<JdbcStatContext> contextLocal = new ThreadLocal<JdbcStatContext>();
  
      private JdbcStatManager() {
      }
  
      public JdbcStatContext getStatContext() {
          return contextLocal.get();
      }
  
      public void setStatContext(JdbcStatContext context) {
          contextLocal.set(context);
      }
  
      public JdbcStatContext createStatContext() {
          return new JdbcStatContext();
      }
  
      public long generateSqlId() {
          return sqlIdSeed.incrementAndGet();
      }
  
      public static JdbcStatManager getInstance() {
          return instance;
      }
  
      public JdbcStatementStat getStatementStat() {
          return statementStat;
      }
  
      public JdbcResultSetStat getResultSetStat() {
          return resultSetStat;
      }
  
      public JdbcConnectionStat getConnectionStat() {
          return connectionStat;
      }
  
      private static CompositeType COMPOSITE_TYPE;
  
      public static CompositeType getDataSourceCompositeType() throws JMException {
          if (COMPOSITE_TYPE != null) {
              return COMPOSITE_TYPE;
          }
  
          OpenType<?>[] indexTypes = new OpenType<?>[]{
                  // 0 - 4
                  SimpleType.LONG, //
                  SimpleType.STRING, //
                  SimpleType.STRING, //
                  new ArrayType<SimpleType<String>>(SimpleType.STRING, false), //
                  SimpleType.DATE, //
  
                  // 5 - 9
                  SimpleType.STRING, //
                  SimpleType.STRING, //
                  SimpleType.INTEGER, //
                  SimpleType.INTEGER, //
                  SimpleType.STRING, //
  
                  // 10 - 14
                  SimpleType.LONG, //
                  SimpleType.INTEGER, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
  
                  // 14 - 19
                  SimpleType.DATE, //
                  SimpleType.LONG, //
                  SimpleType.DATE, //
                  SimpleType.STRING, //
                  SimpleType.STRING, //
  
                  // 20 - 24
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.INTEGER, //
  
                  // 25 - 29
                  SimpleType.INTEGER, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.DATE, //
                  SimpleType.STRING, //
  
                  // 30 - 34
                  SimpleType.STRING, //
                  SimpleType.LONG, //
                  SimpleType.INTEGER, //
                  SimpleType.DATE, //
                  SimpleType.LONG, //
  
                  // 35 - 39
                  SimpleType.LONG, //
                  SimpleType.INTEGER, //
                  SimpleType.INTEGER, //
                  SimpleType.LONG, //
                  SimpleType.DATE,
  
                  // 40 - 44
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.DATE, //
                  SimpleType.STRING, //
                  SimpleType.STRING,
  
                  // 45 - 49
                  SimpleType.LONG, //
                  SimpleType.STRING, //
                  SimpleType.STRING, //
                  SimpleType.LONG, //
                  SimpleType.INTEGER, //
  
                  // 50 - 54
                  SimpleType.LONG, //
                  SimpleType.DATE, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  new ArrayType<Long>(SimpleType.LONG, true), //
  
                  // 55 -
                  new ArrayType<Long>(SimpleType.LONG, true)
                  //
                  //
          };
  
          String[] indexNames = {
                  // 0 - 4
                  "ID",
                  "URL",
                  "Name",
                  "FilterClasses",
                  "CreatedTime", //
  
                  // 5 - 9
                  "RawUrl",
                  "RawDriverClassName",
                  "RawDriverMajorVersion",
                  "RawDriverMinorVersion",
                  "Properties",
  
                  // 10 - 14
                  "ConnectionActiveCount",
                  "ConnectionActiveCountMax",
                  "ConnectionCloseCount",
                  "ConnectionCommitCount",
                  "ConnectionRollbackCount",
  
                  // 15 - 19
                  "ConnectionConnectLastTime",
                  "ConnectionConnectErrorCount",
                  "ConnectionConnectErrorLastTime",
                  "ConnectionConnectErrorLastMessage",
                  "ConnectionConnectErrorLastStackTrace",
  
                  // 20 - 24
                  "StatementCreateCount",
                  "StatementPrepareCount",
                  "StatementPreCallCount",
                  "StatementExecuteCount",
                  "StatementRunningCount",
  
                  // 25 - 29
                  "StatementConcurrentMax",
                  "StatementCloseCount",
                  "StatementErrorCount",
                  "StatementLastErrorTime",
                  "StatementLastErrorMessage",
  
                  // 30 - 34
                  "StatementLastErrorStackTrace", "StatementExecuteMillisTotal", "ConnectionConnectingCount",
                  "StatementExecuteLastTime",
                  "ResultSetCloseCount",
  
                  // 35 -39
                  "ResultSetOpenCount",
                  "ResultSetOpenningCount",
                  "ResultSetOpenningMax",
                  "ResultSetFetchRowCount",
                  "ResultSetLastOpenTime",
  
                  // 40 - 44
                  "ResultSetErrorCount",
                  "ResultSetOpenningMillisTotal",
                  "ResultSetLastErrorTime",
                  "ResultSetLastErrorMessage",
                  "ResultSetLastErrorStackTrace",
  
                  // 45 - 49
                  "ConnectionConnectCount",
                  "ConnectionErrorLastMessage",
                  "ConnectionErrorLastStackTrace",
                  "ConnectionConnectMillisTotal",
                  "ConnectionConnectingCountMax",
  
                  // 50 - 54
                  "ConnectionConnectMillisMax", //
                  "ConnectionErrorLastTime", //
                  "ConnectionAliveMillisMax", //
                  "ConnectionAliveMillisMin", //
                  "ConnectionHistogram", //
  
                  // 55 -
                  "StatementHistogram",
                  //
          };
  
          COMPOSITE_TYPE = new CompositeType("DataSourceStatistic", "DataSource Statistic", indexNames,
                  indexNames, indexTypes);
  
          return COMPOSITE_TYPE;
      }
  
      @Override
      public TabularData getDataSourceList() throws JMException {
          CompositeType rowType = getDataSourceCompositeType();
          String[] indexNames = rowType.keySet().toArray(new String[rowType.keySet().size()]);
  
          TabularType tabularType = new TabularType("DataSourceStat", "DataSourceStat", rowType, indexNames);
          TabularData data = new TabularDataSupport(tabularType);
  
          {
              final ConcurrentMap<String, DataSourceProxyImpl> dataSources = DruidDriver.getProxyDataSources();
              for (DataSourceProxyImpl dataSource : dataSources.values()) {
                  data.put(dataSource.getCompositeData());
              }
          }
  
          final Set<DataSourceMonitorable> dataSources = DruidDataSourceStatManager.getDruidDataSourceInstances();
          for (DataSourceMonitorable dataSource : dataSources) {
              data.put(dataSource.getCompositeData());
          }
  
          return data;
      }
  
      @Override
      public TabularData getSqlList() throws JMException {
          CompositeType rowType = JdbcSqlStat.getCompositeType();
          String[] indexNames = rowType.keySet().toArray(new String[rowType.keySet().size()]);
  
          TabularType tabularType = new TabularType("SqlListStatistic", "SqlListStatistic", rowType, indexNames);
          TabularData data = new TabularDataSupport(tabularType);
  
          JdbcDataSourceStat globalStat = JdbcDataSourceStat.getGlobal();
          if (globalStat != null) {
              Map<String, JdbcSqlStat> statMap = globalStat.getSqlStatMap();
              for (Map.Entry<String, JdbcSqlStat> entry : statMap.entrySet()) {
                  if (entry.getValue().getExecuteCount() == 0 && entry.getValue().getRunningCount() == 0) {
                      continue;
                  }
  
                  Map<String, Object> map = entry.getValue().getData();
                  map.put("URL", globalStat.getUrl());
                  data.put(new CompositeDataSupport(JdbcSqlStat.getCompositeType(), map));
              }
          }
  
          for (DataSourceProxyImpl dataSource : DruidDriver.getProxyDataSources().values()) {
              JdbcDataSourceStat druidDataSourceStat = dataSource.getDataSourceStat();
  
              if (druidDataSourceStat == globalStat) {
                  continue;
              }
  
              Map<String, JdbcSqlStat> statMap = druidDataSourceStat.getSqlStatMap();
              for (Map.Entry<String, JdbcSqlStat> entry : statMap.entrySet()) {
                  if (entry.getValue().getExecuteCount() == 0 && entry.getValue().getRunningCount() == 0) {
                      continue;
                  }
  
                  Map<String, Object> map = entry.getValue().getData();
                  map.put("URL", dataSource.getUrl());
                  data.put(new CompositeDataSupport(JdbcSqlStat.getCompositeType(), map));
              }
          }
  
          for (DataSourceMonitorable dataSource : DruidDataSourceStatManager.getDruidDataSourceInstances()) {
              JdbcDataSourceStat druidDataSourceStat = dataSource.getDataSourceStat();
  
              if (druidDataSourceStat == globalStat) {
                  continue;
              }
  
              Map<String, JdbcSqlStat> statMap = druidDataSourceStat.getSqlStatMap();
              for (Map.Entry<String, JdbcSqlStat> entry : statMap.entrySet()) {
                  if (entry.getValue().getExecuteCount() == 0 && entry.getValue().getRunningCount() == 0) {
                      continue;
                  }
  
                  Map<String, Object> map = entry.getValue().getData();
                  map.put("URL", dataSource.getUrl());
                  data.put(new CompositeDataSupport(JdbcSqlStat.getCompositeType(), map));
              }
          }
  
          return data;
      }
  
      public TabularData getConnectionList() throws JMException {
          CompositeType rowType = JdbcConnectionStat.Entry.getCompositeType();
          String[] indexNames = rowType.keySet().toArray(new String[rowType.keySet().size()]);
  
          TabularType tabularType = new TabularType("ConnectionList", "ConnectionList", rowType, indexNames);
          TabularData data = new TabularDataSupport(tabularType);
  
          final ConcurrentMap<String, DataSourceProxyImpl> dataSources = DruidDriver.getProxyDataSources();
          for (DataSourceProxyImpl dataSource : dataSources.values()) {
              JdbcDataSourceStat dataSourceStat = dataSource.getDataSourceStat();
              ConcurrentMap<Long, JdbcConnectionStat.Entry> connections = dataSourceStat.getConnections();
              for (Map.Entry<Long, JdbcConnectionStat.Entry> entry : connections.entrySet()) {
                  data.put(entry.getValue().getCompositeData());
              }
          }
  
          for (DataSourceMonitorable instance : DruidDataSourceStatManager.getDruidDataSourceInstances()) {
              JdbcDataSourceStat dataSourceStat = instance.getDataSourceStat();
              ConcurrentMap<Long, JdbcConnectionStat.Entry> connections = dataSourceStat.getConnections();
              for (Map.Entry<Long, JdbcConnectionStat.Entry> entry : connections.entrySet()) {
                  data.put(entry.getValue().getCompositeData());
              }
          }
  
          return data;
      }
  
      @Override
      public void reset() {
          resetCount.incrementAndGet();
  
          connectionStat.reset();
          statementStat.reset();
          resultSetStat.reset();
  
          final ConcurrentMap<String, DataSourceProxyImpl> dataSources = DruidDriver.getProxyDataSources();
          for (DataSourceProxyImpl dataSource : dataSources.values()) {
              dataSource.getDataSourceStat().reset();
          }
  
          for (DataSourceMonitorable instance : DruidDataSourceStatManager.getDruidDataSourceInstances()) {
              instance.getDataSourceStat().reset();
          }
      }
  
      @Override
      public long getResetCount() {
          return resetCount.get();
      }
  }
  