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.support.logging.Log;
  import com.alibaba.druid.support.logging.LogFactory;
  import com.alibaba.druid.util.DruidDataSourceUtils;
  import com.alibaba.druid.util.JMXUtils;
  
  import javax.management.JMException;
  import javax.management.MBeanServer;
  import javax.management.ObjectName;
  import javax.management.openmbean.*;
  
  import java.lang.management.ManagementFactory;
  import java.lang.reflect.Method;
  import java.util.*;
  import java.util.concurrent.atomic.AtomicLong;
  import java.util.concurrent.locks.Lock;
  import java.util.concurrent.locks.ReentrantLock;
  
  @SuppressWarnings("rawtypes")
  public class DruidDataSourceStatManager implements DruidDataSourceStatManagerMBean {
      private static final Lock staticLock = new ReentrantLock();
  
      public static final String SYS_PROP_INSTANCES = "druid.dataSources";
      public static final String SYS_PROP_REGISTER_SYS_PROPERTY = "druid.registerToSysProperty";
  
      private static final Log LOG = LogFactory.getLog(DruidDataSourceStatManager.class);
  
      private static final DruidDataSourceStatManager instance = new DruidDataSourceStatManager();
  
      private final AtomicLong resetCount = new AtomicLong();
  
      // global instances
      private static volatile Map dataSources;
  
      private static final String MBEAN_NAME = "com.alibaba.druid:type=DruidDataSourceStat";
  
      public static DruidDataSourceStatManager getInstance() {
          return instance;
      }
  
      public static void clear() {
          staticLock.lock();
          try {
              Map<Object, ObjectName> dataSources = getInstances();
  
              MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
              for (Object item : dataSources.entrySet()) {
                  Map.Entry entry = (Map.Entry) item;
                  ObjectName objectName = (ObjectName) entry.getValue();
  
                  if (objectName == null) {
                      continue;
                  }
  
                  try {
                      mbeanServer.unregisterMBean(objectName);
                  } catch (JMException e) {
                      LOG.error(e.getMessage(), e);
                  }
              }
          } finally {
              staticLock.unlock();
          }
      }
  
      @SuppressWarnings("unchecked")
      public static Map<Object, ObjectName> getInstances() {
          Map<Object, ObjectName> tmp = dataSources;
          if (tmp == null) {
              staticLock.lock();
              try {
                  if (isRegisterToSystemProperty()) {
                      dataSources = getInstances0();
                  } else {
                      tmp = dataSources;
                      if (null == tmp) {
                          dataSources = tmp = Collections.synchronizedMap(new IdentityHashMap<Object, ObjectName>());
                      }
                  }
              } finally {
                  staticLock.unlock();
              }
          }
  
          return dataSources;
      }
  
      public static boolean isRegisterToSystemProperty() {
          String value = System.getProperty(SYS_PROP_REGISTER_SYS_PROPERTY);
          return "true".equals(value);
      }
  
      @SuppressWarnings("unchecked")
      static Map<Object, ObjectName> getInstances0() {
          Properties properties = System.getProperties();
          Map<Object, ObjectName> instances = (Map<Object, ObjectName>) properties.get(SYS_PROP_INSTANCES);
  
          if (instances == null) {
              synchronized (properties) {
                  instances = (Map<Object, ObjectName>) properties.get(SYS_PROP_INSTANCES);
  
                  if (instances == null) {
                      instances = Collections.synchronizedMap(new IdentityHashMap<Object, ObjectName>());
                      properties.put(SYS_PROP_INSTANCES, instances);
                  }
              }
          }
  
          return instances;
      }
  
      public static synchronized ObjectName addDataSource(Object dataSource, String name) {
          final Map<Object, ObjectName> instances = getInstances();
  
          MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
          synchronized (instances) {
              if (instances.size() == 0) {
                  try {
                      ObjectName objectName = new ObjectName(MBEAN_NAME);
                      if (!mbeanServer.isRegistered(objectName)) {
                          mbeanServer.registerMBean(instance, objectName);
                      }
                  } catch (JMException ex) {
                      LOG.error("register mbean error", ex);
                  }
  
                  DruidStatService.registerMBean();
              }
          }
  
          ObjectName objectName = null;
          if (name != null) {
              try {
                  objectName = new ObjectName("com.alibaba.druid:type=DruidDataSource,id=" + name);
                  mbeanServer.registerMBean(dataSource, objectName);
              } catch (Throwable ex) {
                  LOG.error("register mbean error", ex);
                  objectName = null;
              }
          }
  
          if (objectName == null) {
              try {
                  int id = System.identityHashCode(dataSource);
                  objectName = new ObjectName("com.alibaba.druid:type=DruidDataSource,id=" + id);
                  mbeanServer.registerMBean(dataSource, objectName);
              } catch (Throwable ex) {
                  LOG.error("register mbean error", ex);
                  objectName = null;
              }
          }
  
          instances.put(dataSource, objectName);
          return objectName;
      }
  
      public static synchronized void removeDataSource(Object dataSource) {
          Map<Object, ObjectName> instances = getInstances();
  
          ObjectName objectName = (ObjectName) instances.remove(dataSource);
  
          if (objectName == null) {
              objectName = DruidDataSourceUtils.getObjectName(dataSource);
          }
  
          if (objectName == null) {
              LOG.error("unregister mbean failed. url " + DruidDataSourceUtils.getUrl(dataSource));
              return;
          }
  
          MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
  
          try {
              mbeanServer.unregisterMBean(objectName);
          } catch (Throwable ex) {
              LOG.error("unregister mbean error", ex);
          }
  
          if (instances.size() == 0) {
              try {
                  mbeanServer.unregisterMBean(new ObjectName(MBEAN_NAME));
              } catch (Throwable ex) {
                  LOG.error("unregister mbean error", ex);
              }
  
              DruidStatService.unregisterMBean();
          }
      }
  
      @SuppressWarnings("unchecked")
      public static Set<DataSourceMonitorable> getDruidDataSourceInstances() {
          getInstances();
          return dataSources.keySet();
      }
  
      public void reset() {
          Map<Object, ObjectName> dataSources = getInstances();
  
          for (Object item : dataSources.keySet()) {
              try {
                  Method method = item.getClass().getMethod("resetStat");
                  method.invoke(item);
              } catch (Exception e) {
                  LOG.error("resetStat error", e);
              }
          }
  
          resetCount.incrementAndGet();
      }
  
      public void logAndResetDataSource() {
          Map<Object, ObjectName> dataSources = getInstances();
  
          for (Object item : dataSources.keySet()) {
              try {
                  Method method = item.getClass().getMethod("logStats");
                  method.invoke(item);
              } catch (Exception e) {
                  LOG.error("resetStat error", e);
              }
          }
  
          resetCount.incrementAndGet();
      }
  
      public long getResetCount() {
          return resetCount.get();
      }
  
      public TabularData getDataSourceList() throws JMException {
          CompositeType rowType = getDruidDataSourceCompositeType();
          String[] indexNames = rowType.keySet().toArray(new String[rowType.keySet().size()]);
  
          TabularType tabularType = new TabularType("DruidDataSourceStat", "DruidDataSourceStat", rowType, indexNames);
          TabularData data = new TabularDataSupport(tabularType);
  
          final Set<Object> dataSources = getInstances().keySet();
          for (Object dataSource : dataSources) {
              data.put(getCompositeData(dataSource));
          }
  
          return data;
      }
  
      public CompositeDataSupport getCompositeData(Object dataSource) throws JMException {
          CompositeType rowType = getDruidDataSourceCompositeType();
  
          Map<String, Object> map = DruidDataSourceUtils.getStatDataForMBean(dataSource);
  
          return new CompositeDataSupport(rowType, map);
      }
  
      private static CompositeType COMPOSITE_TYPE;
  
      public static CompositeType getDruidDataSourceCompositeType() throws JMException {
          if (COMPOSITE_TYPE != null) {
              return COMPOSITE_TYPE;
          }
  
          OpenType<?>[] indexTypes = new OpenType<?>[]{
                  // 0 - 4
                  SimpleType.STRING, //
                  SimpleType.STRING, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
  
                  // 5 - 9
                  SimpleType.LONG, //
                  SimpleType.INTEGER, //
                  SimpleType.INTEGER, //
                  SimpleType.INTEGER, //
                  SimpleType.INTEGER, //
  
                  // 10 - 14
                  SimpleType.INTEGER, //
                  SimpleType.INTEGER, //
                  SimpleType.INTEGER, //
                  SimpleType.BOOLEAN, //
                  SimpleType.BOOLEAN, //
  
                  // 15 - 19
                  SimpleType.BOOLEAN, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.STRING, //
  
                  // 20 - 24
                  SimpleType.STRING, //
                  SimpleType.INTEGER, //
                  SimpleType.STRING, //
                  SimpleType.STRING, //
                  SimpleType.LONG, //
  
                  // 25 - 29
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
  
                  // 30 - 34
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  JMXUtils.getThrowableCompositeType(), //
                  JMXUtils.getThrowableCompositeType(), //
                  SimpleType.LONG, //
  
                  // 35 - 39
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.STRING, //
  
                  // 40 - 44
                  SimpleType.DATE, //
                  SimpleType.DATE, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
                  SimpleType.LONG, //
  
                  // 45
                  SimpleType.LONG, //
                  SimpleType.STRING, //
                  //
          };
  
          String[] indexNames = {
                  // 0 - 4
                  "Name", //
                  "URL", //
                  "CreateCount", //
                  "DestroyCount", //
                  "ConnectCount", //
  
                  // 5 - 9
                  "CloseCount", //
                  "ActiveCount", //
                  "PoolingCount", //
                  "LockQueueLength", //
                  "WaitThreadCount", //
  
                  // 10 - 14
                  "InitialSize", //
                  "MaxActive", //
                  "MinIdle", //
                  "PoolPreparedStatements", //
                  "TestOnBorrow", //
  
                  // 15 - 19
                  "TestOnReturn", //
                  "MinEvictableIdleTimeMillis", //
                  "ConnectErrorCount", //
                  "CreateTimespanMillis", //
                  "DbType", //
  
                  // 20 - 24
                  "ValidationQuery", //
                  "ValidationQueryTimeout", //
                  "DriverClassName", //
                  "Username", //
                  "RemoveAbandonedCount", //
  
                  // 25 - 29
                  "NotEmptyWaitCount", //
                  "NotEmptyWaitNanos", //
                  "ErrorCount", //
                  "ReusePreparedStatementCount", //
                  "StartTransactionCount", //
  
                  // 30 - 34
                  "CommitCount", //
                  "RollbackCount", //
                  "LastError", //
                  "LastCreateError", //
                  "PreparedStatementCacheDeleteCount", //
  
                  // 35 - 39
                  "PreparedStatementCacheAccessCount", //
                  "PreparedStatementCacheMissCount", //
                  "PreparedStatementCacheHitCount", //
                  "PreparedStatementCacheCurrentCount", //
                  "Version",
  
                  // 40 - 44
                  "LastErrorTime",
                  "LastCreateErrorTime",
                  "CreateErrorCount",
                  "DiscardCount",
                  "ExecuteQueryCount",
  
                  // 45 -
                  "ExecuteUpdateCount",
                  "InitStackTrace",
          };
  
          String[] indexDescriptions = indexNames;
          COMPOSITE_TYPE = new CompositeType("DataSourceStatistic",
                  "DataSource Statistic",
                  indexNames,
                  indexDescriptions,
                  indexTypes);
  
          return COMPOSITE_TYPE;
      }
  }
  