/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.tools.pulse.internal.data;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.rmi.ssl.SslRMIClientSocketFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.apache.geode.tools.pulse.internal.data.JmxManagerFinder.JmxManagerInfo;

/**
 * Class JMXDataUpdater Class used for creating JMX connection and getting all the required MBeans
 *
 * @since GemFire version 7.0.Beta 2012-09-23
 */
public class JMXDataUpdater implements IClusterUpdater, NotificationListener {

  private static final Logger logger = LogManager.getLogger();
  private final ResourceBundle resourceBundle;
  private final Repository repository;

  private JMXConnector conn = null;
  private MBeanServerConnection mbs = null;
  private final String serverName;
  private final String port;
  private Boolean isAddedNotiListner = false;
  private final Cluster cluster;

  // MBean object names
  private ObjectName MBEAN_OBJECT_NAME_SYSTEM_DISTRIBUTED;
  private ObjectName MBEAN_OBJECT_NAME_REGION_DISTRIBUTED;
  private ObjectName MBEAN_OBJECT_NAME_MEMBER;
  private ObjectName MBEAN_OBJECT_NAME_STATEMENT_DISTRIBUTED;

  private Set<ObjectName> systemMBeans = null;

  private final String[] opSignature =
      {String.class.getName(), String.class.getName(), int.class.getName()};

  private final ObjectMapper mapper = new ObjectMapper();

  /**
   * constructor used for creating JMX connection
   */
  public JMXDataUpdater(String server, String port, Cluster cluster, ResourceBundle resourceBundle,
      Repository repository) {
    serverName = server;
    this.port = port;
    this.cluster = cluster;
    this.resourceBundle = resourceBundle;
    this.repository = repository;

    try {
      // Initialize MBean object names
      MBEAN_OBJECT_NAME_SYSTEM_DISTRIBUTED =
          new ObjectName(PulseConstants.OBJECT_NAME_SYSTEM_DISTRIBUTED);
      MBEAN_OBJECT_NAME_REGION_DISTRIBUTED =
          new ObjectName(PulseConstants.OBJECT_NAME_REGION_DISTRIBUTED);
      MBEAN_OBJECT_NAME_MEMBER = new ObjectName(PulseConstants.OBJECT_NAME_MEMBER);
      MBEAN_OBJECT_NAME_STATEMENT_DISTRIBUTED =
          new ObjectName(PulseConstants.OBJECT_NAME_STATEMENT_DISTRIBUTED);

    } catch (MalformedObjectNameException | NullPointerException e) {
      logger.fatal(e);
    }
  }

  private JmxManagerInfo getManagerInfoFromLocator(Repository repository) {

    try {
      String locatorHost = repository.getHost();
      int locatorPort = Integer.parseInt(repository.getPort());

      logger.info("{}={} & {}={}", resourceBundle.getString("LOG_MSG_HOST"), locatorHost,
          resourceBundle.getString("LOG_MSG_PORT"), locatorPort);

      InetAddress inetAddr = InetAddress.getByName(locatorHost);

      if ((inetAddr instanceof Inet4Address) || (inetAddr instanceof Inet6Address)) {

        if (inetAddr instanceof Inet4Address) {
          logger.info("{}: {}", resourceBundle.getString("LOG_MSG_LOCATOR_IPV4_ADDRESS"), inetAddr);
        } else {
          logger.info("{}: {}", resourceBundle.getString("LOG_MSG_LOCATOR_IPV6_ADDRESS"), inetAddr);
        }

        JmxManagerInfo jmxManagerInfo = JmxManagerFinder.askLocatorForJmxManager(inetAddr,
            locatorPort, 15000, repository.isUseSSLLocator());

        if (jmxManagerInfo.port == 0) {
          logger.info(resourceBundle.getString("LOG_MSG_LOCATOR_COULD_NOT_FIND_MANAGER"));
        }
        return jmxManagerInfo;
      } else {
        // Locator has Invalid locator Address
        logger.info(resourceBundle.getString("LOG_MSG_LOCATOR_BAD_ADDRESS"));
        cluster
            .setConnectionErrorMsg(resourceBundle.getString("LOG_MSG_JMX_CONNECTION_BAD_ADDRESS"));
        // update message to display on UI
        cluster
            .setConnectionErrorMsg(resourceBundle.getString("LOG_MSG_JMX_CONNECTION_BAD_ADDRESS"));
        return null;
      }
    } catch (IOException e) {
      StringWriter swBuffer = new StringWriter();
      PrintWriter prtWriter = new PrintWriter(swBuffer);
      e.printStackTrace(prtWriter);
      logger.fatal("Exception Details : {}\n", swBuffer);
    }
    return null;
  }

  /**
   * Get the jmx connection
   */
  @Override
  public JMXConnector connect(Object credentials) {
    try {

      String jmxSerURL = "";

      logger.info("{}:{}", resourceBundle.getString("LOG_MSG_USE_LOCATOR_VALUE"),
          repository.getJmxUseLocator());

      if (repository.getJmxUseLocator()) {
        JmxManagerInfo jmxManagerInfo = getManagerInfoFromLocator(repository);

        if (jmxManagerInfo.port == 0) {
          logger.info(resourceBundle.getString("LOG_MSG_LOCATOR_COULD_NOT_FIND_MANAGER"));
        } else {
          logger.info("{}: {}={} & {}={}, {}",
              resourceBundle.getString("LOG_MSG_LOCATOR_FOUND_MANAGER"),
              resourceBundle.getString("LOG_MSG_HOST"), jmxManagerInfo.host,
              resourceBundle.getString("LOG_MSG_PORT"), jmxManagerInfo.port,
              (jmxManagerInfo.ssl ? resourceBundle.getString("LOG_MSG_WITH_SSL")
                  : resourceBundle.getString("LOG_MSG_WITHOUT_SSL")));

          jmxSerURL =
              formJMXServiceURLString(jmxManagerInfo.host, String.valueOf(jmxManagerInfo.port));
        }
      } else {
        logger.info("{}={} & {}={}", resourceBundle.getString("LOG_MSG_HOST"), serverName,
            resourceBundle.getString("LOG_MSG_PORT"), port);
        jmxSerURL = formJMXServiceURLString(serverName, port);
      }

      if (StringUtils.isNotBlank(jmxSerURL)) {
        JMXServiceURL url = new JMXServiceURL(jmxSerURL);
        Map<String, Object> env = new HashMap<>();
        env.put(JMXConnector.CREDENTIALS, credentials);

        Properties originalProperties = System.getProperties();
        try {
          Properties updatedProperties = new Properties(originalProperties);
          if (repository.isUseSSLManager()) {
            for (String sslProperty : repository.getJavaSslProperties().stringPropertyNames()) {
              updatedProperties.setProperty(sslProperty,
                  repository.getJavaSslProperties().getProperty(sslProperty));
            }

            System.setProperties(updatedProperties);
            env.put("com.sun.jndi.rmi.factory.socket", new SslRMIClientSocketFactory());
          }
          logger.info("Connecting to jmxURL : {}", jmxSerURL);
          conn = JMXConnectorFactory.connect(url, env);
          mbs = conn.getMBeanServerConnection();
          cluster.setConnectedFlag(true);
        } finally {
          System.setProperties(originalProperties);
        }
      }
    } catch (Exception e) {
      cluster.setConnectedFlag(false);
      cluster.setConnectionErrorMsg(e.getMessage());
      logger.fatal(e.getMessage(), e);
      if (conn != null) {
        try {
          conn.close();
        } catch (Exception e1) {
          logger.fatal(e1.getMessage(), e1);
        }
        conn = null;
      }
    }
    return conn;
  }

  private String formJMXServiceURLString(String host, String port) throws UnknownHostException {
    /*
     * String jmxSerURL = "service:jmx:rmi://" + serverName + "/jndi/rmi://" + serverName + ":" +
     * port + "/jmxrmi";
     */
    String jmxSerURL = "";
    if (host.equalsIgnoreCase("localhost")) {
      // Create jmx service url for 'localhost'
      jmxSerURL = "service:jmx:rmi://" + host + "/jndi/rmi://" + host + ":" + port + "/jmxrmi";
    } else {
      InetAddress inetAddr = InetAddress.getByName(host);
      if (inetAddr instanceof Inet4Address) {
        // Create jmx service url for IPv4 address
        jmxSerURL = "service:jmx:rmi://" + host + "/jndi/rmi://" + host + ":" + port + "/jmxrmi";
      } else if (inetAddr instanceof Inet6Address) {
        // Create jmx service url for IPv6 address
        jmxSerURL =
            "service:jmx:rmi://[" + host + "]/jndi/rmi://[" + host + "]:" + port + "/jmxrmi";
      }
    }

    return jmxSerURL;
  }

  /**
   * function used for updating Cluster Data.
   */
  @Override
  public boolean updateData() {
    try {
      if (conn == null) {
        return false;
      }

      // deleted Members
      cluster.getDeletedMembers().clear();
      for (Entry<String, Cluster.Member> memberSet : cluster.getMembersHMap().entrySet()) {
        cluster.getDeletedMembers().add(memberSet.getKey());
      }

      // Deleted Regions
      cluster.getDeletedRegions().clear();
      for (Cluster.Region region : cluster.getClusterRegions().values()) {
        cluster.getDeletedRegions().add(region.getFullPath());
      }

      systemMBeans = mbs.queryNames(MBEAN_OBJECT_NAME_SYSTEM_DISTRIBUTED, null);
      for (ObjectName sysMBean : systemMBeans) {
        updateClusterSystem(sysMBean);
      }

      // Cluster Regions/Tables
      Set<ObjectName> regionMBeans =
          mbs.queryNames(MBEAN_OBJECT_NAME_REGION_DISTRIBUTED, null);

      // For Gemfire
      for (ObjectName regMBean : regionMBeans) {
        updateClusterRegion(regMBean);
      }

      // Remove deleted regions from cluster's regions list
      for (String s : cluster.getDeletedRegions()) {
        cluster.removeClusterRegion(s);
      }

      List<ObjectName> serviceMBeans = new ArrayList<>();
      List<ObjectName> nonServiceMBeans = new ArrayList<>();

      Set<ObjectName> memberMBeans = mbs.queryNames(MBEAN_OBJECT_NAME_MEMBER, null);
      for (ObjectName mBean : memberMBeans) {
        String service = mBean.getKeyProperty(PulseConstants.MBEAN_KEY_PROPERTY_SERVICE);
        if (service == null) {
          nonServiceMBeans.add(mBean);
        } else {
          serviceMBeans.add(mBean);
        }
      }

      // Make sure that we process 'pure' members first. This ensures that various structures in
      // Cluster are set up correctly since they are keyed on the 'host' attribute which does not
      // necessarily appear in other MBeans. This avoids the possibility of having a 'null' host
      // icon appear in the Pulse UI.
      for (ObjectName mBean : nonServiceMBeans) {
        updateClusterMember(mBean);
      }

      for (ObjectName serviceMBean : serviceMBeans) {
        String service = serviceMBean.getKeyProperty(PulseConstants.MBEAN_KEY_PROPERTY_SERVICE);
        switch (service) {
          case PulseConstants.MBEAN_KEY_PROPERTY_SERVICE_VALUE_REGION:
            updateMemberRegion(serviceMBean);
            break;
          case PulseConstants.MBEAN_KEY_PROPERTY_SERVICE_VALUE_CACHESERVER:
            updateMemberClient(serviceMBean);
            break;
          case PulseConstants.MBEAN_KEY_PROPERTY_SERVICE_VALUE_GATEWAYRECEIVER:
            updateGatewayReceiver(serviceMBean);
            break;
          case PulseConstants.MBEAN_KEY_PROPERTY_SERVICE_VALUE_GATEWAYSENDER:
            updateGatewaySender(serviceMBean);
            break;
          case PulseConstants.MBEAN_KEY_PROPERTY_SERVICE_VALUE_ASYNCEVENTQUEUE:
            updateAsyncEventQueue(serviceMBean);
            break;
          case PulseConstants.MBEAN_KEY_PROPERTY_SERVICE_VALUE_LOCATOR:
            updateClusterMember(serviceMBean);
            break;
        }
      }

      // Cluster Query Statistics
      Set<ObjectName> statementObjectNames =
          mbs.queryNames(MBEAN_OBJECT_NAME_STATEMENT_DISTRIBUTED, null);
      for (ObjectName stmtObjectName : statementObjectNames) {
        updateClusterStatement(stmtObjectName);
      }
    } catch (IOException ioe) {
      logger.fatal(ioe.getMessage(), ioe);
      mbs = null;
      if (conn != null) {
        try {
          conn.close();
        } catch (IOException e1) {
          logger.fatal(e1.getMessage(), e1);
        }
        conn = null;
        cluster.setConnectedFlag(false);
        cluster.setConnectionErrorMsg(ioe.getMessage());
      }

      return false;
    }

    // If there were members deleted, remove them from the membersList &
    // physicalToMember.
    for (String memberKey : cluster.getDeletedMembers()) {
      if (cluster.getMembersHMap().containsKey(memberKey)) {
        Cluster.Member member = cluster.getMembersHMap().get(memberKey);
        List<Cluster.Member> memberArrList = cluster.getPhysicalToMember().get(member.getHost());
        if (memberArrList != null) {
          if (memberArrList.contains(member)) {
            String host = member.getHost();
            cluster.getPhysicalToMember().get(member.getHost()).remove(member);

            if (cluster.getPhysicalToMember().get(member.getHost()).size() == 0) {
              cluster.getPhysicalToMember().remove(host);
            }
          }
        }
        cluster.getMembersHMap().remove(memberKey);
      }

    }

    return true;
  }

  /**
   * function used to get attribute values of Cluster System and map them to cluster vo
   *
   * @param mbeanName Cluster System MBean
   */
  private void updateClusterSystem(ObjectName mbeanName) throws IOException {
    try {
      if (!isAddedNotiListner) {
        mbs.addNotificationListener(mbeanName, this, null, new Object());
        isAddedNotiListner = true;
      }

      String[] serverCnt = (String[]) (mbs.invoke(mbeanName,
          PulseConstants.MBEAN_OPERATION_LISTSERVERS, null, null));
      cluster.setServerCount(serverCnt.length);

      TabularData table = (TabularData) (mbs.invoke(mbeanName,
          PulseConstants.MBEAN_OPERATION_VIEWREMOTECLUSTERSTATUS, null, null));

      @SuppressWarnings("unchecked")
      Collection<CompositeData> rows = (Collection<CompositeData>) table.values();
      cluster.getWanInformationObject().clear();
      for (CompositeData row : rows) {
        final Object key = row.get("key");
        final Object value = row.get("value");
        cluster.getWanInformationObject().put((String) key, (Boolean) value);
      }

      AttributeList attributeList =
          mbs.getAttributes(mbeanName, PulseConstants.CLUSTER_MBEAN_ATTRIBUTES);

      for (Object o : attributeList) {

        Attribute attribute = (Attribute) o;
        String name = attribute.getName();
        switch (name) {
          case PulseConstants.MBEAN_ATTRIBUTE_MEMBERCOUNT:
            cluster.setMemberCount(getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_NUMCLIENTS:
            cluster.setClientConnectionCount(
                getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_DISTRIBUTEDSYSTEMID:
            cluster.setClusterId(getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_LOCATORCOUNT:
            cluster.setLocatorCount(getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_NUMRUNNIGFUNCTION:
            try {
              cluster.setRunningFunctionCount(
                  getIntegerAttribute(attribute.getValue(), attribute.getName()));
            } catch (Exception e) {
              cluster.setRunningFunctionCount(0);
              continue;
            }
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_REGISTEREDCQCOUNT:
            cluster
                .setRegisteredCQCount(getLongAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_NUMSUBSCRIPTIONS:
            cluster.setSubscriptionCount(
                getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_NUMTXNCOMMITTED:
            cluster.setTxnCommittedCount(
                getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_NUMTXNROLLBACK:
            cluster.setTxnRollbackCount(
                getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_TOTALHEAPSIZE:
            cluster.setTotalHeapSize(getLongAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_USEDHEAPSIZE:
            try {
              cluster.setUsedHeapSize(getLongAttribute(attribute.getValue(), attribute.getName()));
            } catch (Exception e) {
              cluster.setUsedHeapSize(0);
              continue;
            }
            cluster.getMemoryUsageTrend().add(cluster.getUsedHeapSize());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_TOTALREGIONENTRYCOUNT:
            cluster.setTotalRegionEntryCount(
                getLongAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_CURRENTENTRYCOUNT:
            cluster.setCurrentQueryCount(
                getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_TOTALDISKUSAGE:
            try {
              cluster
                  .setTotalBytesOnDisk(getLongAttribute(attribute.getValue(), attribute.getName()));
            } catch (Exception e) {
              cluster.setTotalBytesOnDisk(0);
              continue;
            }
            cluster.getTotalBytesOnDiskTrend().add(cluster.getTotalBytesOnDisk());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_DISKWRITESRATE:
            cluster
                .setDiskWritesRate(getDoubleAttribute(attribute.getValue(), attribute.getName()));
            cluster.getThroughoutWritesTrend().add(cluster.getDiskWritesRate());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_AVERAGEWRITES:
            try {
              cluster.setWritePerSec(getDoubleAttribute(attribute.getValue(), attribute.getName()));
            } catch (Exception e) {
              cluster.setWritePerSec(0);
              continue;
            }
            cluster.getWritePerSecTrend().add(cluster.getWritePerSec());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_AVERAGEREADS:
            try {
              cluster.setReadPerSec(getDoubleAttribute(attribute.getValue(), attribute.getName()));
            } catch (Exception e) {
              cluster.setReadPerSec(0);
              continue;
            }
            cluster.getReadPerSecTrend().add(cluster.getReadPerSec());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_QUERYREQUESTRATE:
            cluster.setQueriesPerSec(getDoubleAttribute(attribute.getValue(), attribute.getName()));
            cluster.getQueriesPerSecTrend().add(cluster.getQueriesPerSec());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_DISKREADSRATE:
            cluster.setDiskReadsRate(getDoubleAttribute(attribute.getValue(), attribute.getName()));
            cluster.getThroughoutReadsTrend().add(cluster.getDiskReadsRate());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_JVMPAUSES:
            long trendVal = determineCurrentJVMPauses(PulseConstants.JVM_PAUSES_TYPE_CLUSTER, "",
                getLongAttribute(attribute.getValue(), attribute.getName()));
            cluster.setGarbageCollectionCount(trendVal);
            cluster.getGarbageCollectionTrend().add(cluster.getGarbageCollectionCount());
            break;
          case PulseConstants.MBEAN_ATTRIBUTE_TOTALREGIONCOUNT:
            cluster.setTotalRegionCount(
                getIntegerAttribute(attribute.getValue(), attribute.getName()));
            break;
        }
      }

    } catch (InstanceNotFoundException | ReflectionException | MBeanException infe) {
      logger.warn(infe);
    }
  }

  /**
   * function used to get attribute values of Gateway Receiver and map them to GatewayReceiver inner
   * class object
   *
   * @return GatewayReceiver object
   */
  private Cluster.GatewayReceiver initGatewayReceiver(ObjectName mbeanName)
      throws InstanceNotFoundException, ReflectionException, IOException {

    Cluster.GatewayReceiver gatewayReceiver = new Cluster.GatewayReceiver();

    AttributeList attributeList =
        mbs.getAttributes(mbeanName, PulseConstants.GATEWAY_MBEAN_ATTRIBUTES);

    for (Object o : attributeList) {
      Attribute attribute = (Attribute) o;

      switch (attribute.getName()) {
        case PulseConstants.MBEAN_ATTRIBUTE_PORT:
          gatewayReceiver
              .setListeningPort(getIntegerAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_EVENTRECEIVEDDATE:
          gatewayReceiver
              .setLinkThroughput(getDoubleAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AVEARGEBATCHPROCESSINGTIME:
          gatewayReceiver
              .setAvgBatchProcessingTime(
                  getLongAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_RUNNING:
          gatewayReceiver.setStatus(getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
      }
    }
    return gatewayReceiver;
  }

  /**
   * function used to get attribute values of Gateway Sender and map them to GatewaySender inner
   * class object
   */
  private Cluster.GatewaySender initGatewaySender(ObjectName mbeanName)
      throws InstanceNotFoundException, ReflectionException, IOException {

    Cluster.GatewaySender gatewaySender = new Cluster.GatewaySender();
    AttributeList attributeList =
        mbs.getAttributes(mbeanName, PulseConstants.GATEWAYSENDER_MBEAN_ATTRIBUTES);

    for (Object o : attributeList) {
      Attribute attribute = (Attribute) o;
      String name = attribute.getName();
      switch (name) {
        case PulseConstants.MBEAN_ATTRIBUTE_EVENTRECEIVEDDATE:
          gatewaySender
              .setLinkThroughput(getDoubleAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_BATCHSIZE:
          gatewaySender
              .setBatchSize(getIntegerAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_SENDERID:
          gatewaySender.setId(getStringAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_EVENTQUEUESIZE:
          gatewaySender
              .setQueueSize(getIntegerAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_RUNNING:
          gatewaySender.setStatus(getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_PRIMARY:
          gatewaySender.setPrimary(getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_PERSISTENCEENABLED:
          gatewaySender.setPersistenceEnabled(
              getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_PARALLEL:
          gatewaySender
              .setSenderType(getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_REMOTE_DS_ID:
          gatewaySender
              .setRemoteDSId(getIntegerAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_EVENTS_EXCEEDING_ALERT_THRESHOLD:
          gatewaySender.setEventsExceedingAlertThreshold(
              getIntegerAttribute(attribute.getValue(), attribute.getName()));
          break;
      }
    }
    return gatewaySender;
  }

  /**
   * function used for getting list of Gateway Senders from mBean for giving member and update the
   * list of gateway senders for respective member object
   */
  private void updateGatewaySender(ObjectName mbeanName) throws IOException {

    try {
      String memberName = mbeanName.getKeyProperty(PulseConstants.MBEAN_KEY_PROPERTY_MEMBER);

      if (cluster.getMembersHMap().containsKey(memberName)) {
        Cluster.Member existingMember = cluster.getMembersHMap().get(memberName);
        Cluster.GatewaySender gatewaySender = initGatewaySender(mbeanName);
        for (Iterator<Cluster.GatewaySender> it =
            existingMember.getGatewaySenderList().iterator(); it.hasNext();) {
          Cluster.GatewaySender exisGatewaySender = it.next();
          if ((exisGatewaySender.getId()).equals(gatewaySender.getId())) {
            it.remove();
            break;
          }
        }

        // Add gateway sender
        existingMember.getGatewaySenderList().add(gatewaySender);

      } else {
        Cluster.Member member = new Cluster.Member();
        member.setName(memberName);
        member.setId(memberName);
        Cluster.GatewaySender gatewaySender = initGatewaySender(mbeanName);
        member.getGatewaySenderList().add(gatewaySender);
        cluster.getMembersHMap().put(memberName, member);
      }
    } catch (InstanceNotFoundException | ReflectionException infe) {
      logger.warn(infe);
    }
  }

  /**
   * function used to get attribute values of Async Event Queue and map them to Async Event Queue
   * inner class object
   */
  private Cluster.AsyncEventQueue initAsyncEventQueue(ObjectName mbeanName)
      throws InstanceNotFoundException, ReflectionException, IOException {

    Cluster.AsyncEventQueue asyncEventQueue = new Cluster.AsyncEventQueue();
    AttributeList attributeList =
        mbs.getAttributes(mbeanName, PulseConstants.ASYNC_EVENT_QUEUE_MBEAN_ATTRIBUTES);

    for (Object o : attributeList) {
      Attribute attribute = (Attribute) o;
      String name = attribute.getName();
      switch (name) {
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_ASYNCEVENTID:
          asyncEventQueue.setId(getStringAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_ASYNC_EVENT_LISTENER:
          asyncEventQueue
              .setAsyncEventListener(getStringAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_BATCH_CONFLATION_ENABLED:
          asyncEventQueue.setBatchConflationEnabled(
              getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_BATCH_TIME_INTERVAL:
          asyncEventQueue
              .setBatchTimeInterval(getLongAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_BATCH_SIZE:
          asyncEventQueue
              .setBatchSize(getIntegerAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_EVENT_QUEUE_SIZE:
          asyncEventQueue
              .setEventQueueSize(getIntegerAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_PARALLEL:
          asyncEventQueue
              .setParallel(getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
        case PulseConstants.MBEAN_ATTRIBUTE_AEQ_PRIMARY:
          asyncEventQueue
              .setPrimary(getBooleanAttribute(attribute.getValue(), attribute.getName()));
          break;
      }
    }
    return asyncEventQueue;
  }

  /**
   * function used for getting list of Gateway Senders from mBean for giving member and update the
   * list of gateway senders for respective member object
   */
  private void updateAsyncEventQueue(ObjectName mbeanName) throws IOException {
    try {
      String memberName = mbeanName.getKeyProperty(PulseConstants.MBEAN_KEY_PROPERTY_MEMBER);

      if (cluster.getMembersHMap().containsKey(memberName)) {
        Cluster.Member existingMember = cluster.getMembersHMap().get(memberName);
        Cluster.AsyncEventQueue asyncQ = initAsyncEventQueue(mbeanName);
        for (Iterator<Cluster.AsyncEventQueue> it =
            existingMember.getAsyncEventQueueList().iterator(); it.hasNext();) {
          Cluster.AsyncEventQueue exisAsyncEventQueue = it.next();
          if ((exisAsyncEventQueue.getId()).equals(asyncQ.getId())) {
            it.remove();
            break;
          }
        }

        // Add async event queue
        existingMember.getAsyncEventQueueList().add(asyncQ);
      } else {
        Cluster.Member member = new Cluster.Member();
        member.setName(memberName);
        member.setId(memberName);

        Cluster.AsyncEventQueue asyncQ = initAsyncEventQueue(mbeanName);
        member.getAsyncEventQueueList().add(asyncQ);

        cluster.getMembersHMap().put(memberName, member);
      }
    } catch (InstanceNotFoundException | ReflectionException infe) {
      logger.warn(infe);
    }
  }

  /**
   * function used for getting a Gateway Receiver from mBean for giving member and update the
   * gateway receiver for respective member object
   */
  private void updateGatewayReceiver(ObjectName mbeanName) throws IOException {

    try {
      String memberName = mbeanName.getKeyProperty(PulseConstants.MBEAN_KEY_PROPERTY_MEMBER);

      if (cluster.getMembersHMap().containsKey(memberName)) {
        Cluster.Member existingMember = cluster.getMembersHMap().get(memberName);
        Cluster.GatewayReceiver gatewayReceiver = initGatewayReceiver(mbeanName);
        existingMember.setGatewayReceiver(gatewayReceiver);
      } else {
        Cluster.Member member = new Cluster.Member();
        member.setName(memberName);
        member.setId(memberName);
        Cluster.GatewayReceiver gatewayReceiver = initGatewayReceiver(mbeanName);
        member.setGatewayReceiver(gatewayReceiver);
        cluster.getMembersHMap().put(memberName, member);
      }
    } catch (InstanceNotFoundException | ReflectionException infe) {
      logger.warn(infe);
    }
  }

  /**
   * function used for getting member clients from mbean and update the clients information in
   * member object's client arraylist
   */
  private void updateMemberClient(ObjectName mbeanName) throws IOException {

    try {
      String memberName = mbeanName.getKeyProperty(PulseConstants.MBEAN_KEY_PROPERTY_MEMBER);

      if (cluster.getMembersHMap().containsKey(memberName)) {
        Cluster.Member existingMember = cluster.getMembersHMap().get(memberName);
