All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.admin.jmx.internal.MemberInfoWithStatsMBean Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.admin.jmx.internal;

import java.net.InetAddress;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicLong;

import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.ReflectionException;

import mx4j.AbstractDynamicMBean;

import com.gemstone.gemfire.admin.AdminDistributedSystem;
import com.gemstone.gemfire.admin.AdminException;
import com.gemstone.gemfire.admin.CacheVm;
import com.gemstone.gemfire.admin.ConfigurationParameter;
import com.gemstone.gemfire.admin.GemFireMemberStatus;
import com.gemstone.gemfire.admin.RegionSubRegionSnapshot;
import com.gemstone.gemfire.admin.StatisticResource;
import com.gemstone.gemfire.admin.SystemMember;
import com.gemstone.gemfire.admin.SystemMemberCacheServer;
import com.gemstone.gemfire.admin.jmx.Agent;
import com.gemstone.gemfire.cache.InterestPolicy;
import com.gemstone.gemfire.cache.SubscriptionAttributes;
import com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.GemFireVersion;
import com.gemstone.gemfire.internal.admin.remote.ClientHealthStats;
import com.gemstone.gemfire.internal.cache.GatewayEndpointStatus;
import com.gemstone.gemfire.internal.cache.GatewayHubStatus;
import com.gemstone.gemfire.internal.cache.GatewayStatus;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;

/**
 * This class uses the JMX Attributes/Operations that use (return/throw) 
 * GemFire types. This is the single MBean accessible with ObjectName string
 * {@link MemberInfoWithStatsMBean#MBEAN_NAME}}. This MBean can be used to 
 * retrieve the all member details as plain java types.
 * 
 * This MBean also acts as a Notification Hub for all the Notifications that are 
 * defined for Admin Distributed System. 
 * 
 * @author abhishek
 * 
 * @since 6.5
 */
public class MemberInfoWithStatsMBean extends AbstractDynamicMBean 
                                 implements NotificationEmitter {
  /* constants defining max no of attributes/operations/notifications */
  private static final int MAX_ATTRIBUTES_COUNT    = 3;
  private static final int MAX_OPERATIONS_COUNT    = 3;
  private static final int MAX_NOTIFICATIONS_COUNT = 9;
  
  private static final String NOT_AVAILABLE_STR = "N/A";
  private static final String NOT_AVAILABLE = null;
  private static final Number NOT_AVAILABLE_NUMBER = null;

  /* String constant used for a region that is used on admin side just as a root 
   * for rootRegions defined on the member */
  private static final String PLACE_HOLDER_ROOT_REGION = "/Root/";

  /* String that are used to form QueryExp/ObjectName for querying MBeanServer */
  private static final String REGION_QUERY_EXPRESSION = "*GemFire.Cache*:*,owner={0},type=Region";  
  private static final String STATS_QUERY_EXPRESSION  = "*GemFire.Statistic*:*,source={0},name={1}";
  
  /** mbean name string for this MBean */
  /*default*/static final String  MBEAN_NAME = "GemFire:type=MemberInfoWithStatsMBean";

  /** ObjectName handle for this MBean */
  private ObjectName objectName;
  
  /** version of the GemFire Enterprise system that is running */
  private String                        version;
  private int                           refreshInterval;
  private String                        id;
  
  private Agent                         agent;
  private AdminDistributedSystemJmxImpl adminDSJmx;
  private LogWriterI18n                 logWriter;
  
  private NotificationForwarder         forwarder;
  private boolean                       isInitialized;//needs synchronization?

  /**
   * Default Constructor
   * 
   * @param agent Admin Agent instance
   * @throws OperationsException if ObjectName can't be formed for this MBean
   * @throws MBeanRegistrationException 
   * @throws AdminException 
   */
  MemberInfoWithStatsMBean(Agent agent) throws OperationsException, MBeanRegistrationException, AdminException {
    this.agent           = agent;
    this.logWriter       = this.agent.getLogWriter().convertToLogWriterI18n();
    this.objectName      = ObjectName.getInstance(MBEAN_NAME);
    this.version         = GemFireVersion.getGemFireVersion();
    this.refreshInterval = -1;
    this.id              = NOT_AVAILABLE_STR;
    this.forwarder       = new NotificationForwarder(agent.getMBeanServer(), this.logWriter);
  }

  /**
   * Returns attributes defined for this MBean as an array of 
   * MBeanAttributeInfo objects.
   * 
   * @return attributes defined as an array of MBeanAttributeInfo objects.
   */
  @Override
  protected MBeanAttributeInfo[] createMBeanAttributeInfo() {
    MBeanAttributeInfo[] attributesInfo = new MBeanAttributeInfo[MAX_ATTRIBUTES_COUNT];
    
    /* First letter in attribute name has to be 'V' so that getVersion is 
     * called. With 'v' it looks for getversion, same for others */
    attributesInfo[0] = new MBeanAttributeInfo("Version", 
                                               String.class.getName(), 
                                               "GemFire Enterprise Version", 
                                               true,  /*readable*/ 
                                               false, /*writable*/ 
                                               false);/*has getter with name like 'is****'*/
    
    attributesInfo[1] = new MBeanAttributeInfo("RefreshInterval", 
                                               String.class.getName(), 
                                               "The interval (in seconds) between auto-polling for updating member & statistics resources. If this is '-1', it means the this MBean has not yet been initialized. First call to getMembers operation will initialize this MBean.", 
                                               true,  /*readable*/ 
                                               false, /*writable*/ 
                                               false);/*has getter with name like 'is****'*/
    
    attributesInfo[2] = new MBeanAttributeInfo("Id", 
                                               String.class.getName(), 
                                               "Identifier of the GemFire Enterprise. If this is 'N/A', it means the this MBean has not yet been initialized. First call to getMembers operation will initialize this MBean.", 
                                               true,  /*readable*/ 
                                               false, /*writable*/ 
                                               false);/*has getter with name like 'is****'*/   
    
    
    return attributesInfo;
  }
  
  /**
   * Returns operations defined for this MBean as an array of 
   * MBeanOperationInfo objects.
   * 
   * @return operations defined as an array of MBeanOperationInfo objects. 
   */
  @Override
  protected MBeanOperationInfo[] createMBeanOperationInfo() {
    MBeanOperationInfo[] operationsInfo = new MBeanOperationInfo[MAX_OPERATIONS_COUNT];
    
    operationsInfo[0] = new MBeanOperationInfo("getMembers", 
                                               "Returns ids as strings for all the members - Application Peers & Cache Servers.", 
                                               new MBeanParameterInfo[] {}, 
                                               String[].class.getName(), 
                                               MBeanOperationInfo.ACTION_INFO);
    
    MBeanParameterInfo[] getMemberDetailsArgs = new MBeanParameterInfo[1];
    getMemberDetailsArgs[0] = new MBeanParameterInfo("memberId", String.class.getName(), "Id of the member for all the details are to be retrieved.");
    operationsInfo[1] = new MBeanOperationInfo("getMemberDetails", 
                                                "Returns details for a given member", 
                                                getMemberDetailsArgs, 
                                                Map.class.getName(), 
                                                MBeanOperationInfo.ACTION_INFO);
    
    /* For retrieving ObjectNames of existing Region MBeans, MBeanServerConnection.queryMBeans(), could be called */
    MBeanParameterInfo[] getRegionSnapArgs = new MBeanParameterInfo[1];
    getRegionSnapArgs[0] = new MBeanParameterInfo("memberId", String.class.getName(), "Id of the member on which we want to discover all the region MBean.");
    operationsInfo[2] = new MBeanOperationInfo("getRegions", 
                                                "Returns a java.util.Map of details of regions on a member", 
                                                getRegionSnapArgs, 
                                                Map.class.getName(), 
                                                MBeanOperationInfo.ACTION_INFO);

    
    return operationsInfo;
  }

  /**
   * Returns notifications defined for this MBean as an array of
   * MBeanNotificationInfo objects.
   * 
   * @return notification definitions as an array of MBeanNotificationInfo
   *         objects.
   */
  @Override
  protected MBeanNotificationInfo[] createMBeanNotificationInfo() {
    MBeanNotificationInfo[] notificationsInfo = new MBeanNotificationInfo[MAX_NOTIFICATIONS_COUNT];
    
    String[] notificationTypes = new String[] {AdminDistributedSystemJmxImpl.NOTIF_MEMBER_JOINED};
    notificationsInfo[0] = new MBeanNotificationInfo(notificationTypes,
                                                    Notification.class.getName(), 
                                                    "A GemFire manager, cache, or other member has joined this distributed system.");
    
    notificationTypes = new String[] {AdminDistributedSystemJmxImpl.NOTIF_MEMBER_LEFT};
    notificationsInfo[1] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A GemFire manager, cache, or other member has left the distributed system.");
    
    notificationTypes = new String[] {AdminDistributedSystemJmxImpl.NOTIF_MEMBER_CRASHED};
    notificationsInfo[2] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A member of this distributed system has crashed instead of leaving cleanly.");
    
    notificationTypes = new String[] {AdminDistributedSystemJmxImpl.NOTIF_ALERT};
    notificationsInfo[3] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A member of this distributed system has generated an alert.");
    
    notificationTypes = new String[] {AdminDistributedSystemJmxImpl.NOTIF_ADMIN_SYSTEM_DISCONNECT};
    notificationsInfo[4] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A GemFire manager, cache, or other member has joined this distributed system.");
    
    notificationTypes = new String[] {SystemMemberJmx.NOTIF_CACHE_CREATED};
    notificationsInfo[5] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A cache got created on a member of this distributed system.");
    
    notificationTypes = new String[] {SystemMemberJmx.NOTIF_CACHE_CLOSED};
    notificationsInfo[6] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A cache is closed on a member of this distributed system.");
    
    notificationTypes = new String[] {SystemMemberJmx.NOTIF_REGION_CREATED};
    notificationsInfo[7] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A region is created in a cache on a member of this distributed system.");
    
    notificationTypes = new String[] {SystemMemberJmx.NOTIF_REGION_LOST};
    notificationsInfo[8] = new MBeanNotificationInfo(notificationTypes, 
                                                    Notification.class.getName(), 
                                                    "A region was removed from a cache on a member of this distributed system.");

//  String[] notificationTypes5 = new String[] {AdminDistributedSystemJmxImpl.NOTIF_STAT_ALERT};
//  notificationsInfo[9] = new MBeanNotificationInfo(notificationTypes5, 
//                                                  Notification.class.getName(), 
//                                                  "An alert based on statistic(s) has been raised.");    

    return notificationsInfo;
  }
  
  /**
   * 
   * @return ObjectName of this MBean
   */
  /*default*/ ObjectName getObjectName() {
    return objectName;
  }

  /**
   * Returns the version of the GemFire Enterprise instance as a string.
   * 
   * @return GemFire Enterprise version string derived from {@link GemFireVersion}
   */
  /* getter for attribute - Version */
  public String getVersion() {
    return version;
  }

  /**
   * @return the refreshInterval
   */
  public int getRefreshInterval() {
    return refreshInterval;
  }

  /**
   * @return the id
   */
  public String getId() {
    return id;
  }

  /**
   * Connects the Admin Agent in the DS
   * 
   * @return AdminDistributedSystem MBean ObjectName
   * @throws OperationsException
   *           if connection to the DS fails
   * @throws AdminException
   *           if connection to the DS fails
   */
  private ObjectName connectToSystem() throws OperationsException, AdminException {
    ObjectName adminDsObjName = agent.connectToSystem();
    
    AdminDistributedSystem adminDS = agent.getDistributedSystem();
    if (adminDSJmx == null && adminDS instanceof AdminDistributedSystemJmxImpl) {//instanceof checks for null
      adminDSJmx      = (AdminDistributedSystemJmxImpl) adminDS;
      refreshInterval = adminDSJmx.getRefreshInterval();
      id              = adminDSJmx.getId();
      forwarder.registerNotificationListener(adminDSJmx.getObjectName());
    }

    return adminDsObjName;
  }
  
  /**
   * 
   * @param memberId
   * @return SystemMemberJmx instance for given memberId
   * @throws AdminException
   */
  private SystemMemberJmx findMember(String memberId) throws AdminException {
    SystemMemberJmx foundMember = null;
    
    if (agent.isConnected()) {
      SystemMember[] members = adminDSJmx.getSystemMemberApplications();
      for (SystemMember app : members) {
        if (app.getId().equals(memberId)) {
          foundMember = (SystemMemberJmx) app;
          break;
        }
      }
      
      if (foundMember == null) {
        members = adminDSJmx.getCacheVms();
        for (SystemMember cacheVm : members) {
          if (cacheVm.getId().equals(memberId)) {
            foundMember = (SystemMemberJmx) cacheVm;
            break;
          }
        }
      }
    }
    
    return foundMember;
  }

  /**
   * Return ObjectNames for all the Member MBeans in the DS.
   * 
   * @return Array of ObjectNames of all Member MBeans
   * @throws OperationsException
   *           if (1)agent could not connect in the DS OR 
   *           (2)Notification Listener could not be registered for the Admin 
   *              DS MBean OR
   *           (3)fails to retrieve information from Admin DS
   */
  public String[] getMembers() throws OperationsException {
    String[] members = new String[0];
      
    try {
      if (!isInitialized) {
        initializeAll(); //initialize if not yet
      }

      if (adminDSJmx != null) {
        CacheVm[]      cacheVms = adminDSJmx.getCacheVms();
        SystemMember[] appVms   = adminDSJmx.getSystemMemberApplications();
        
        List membersList = new ArrayList();
        if (cacheVms != null && cacheVms.length !=0) {
          for (SystemMember cacheVm : cacheVms) {
            membersList.add(cacheVm.getId());
          }
        }
        if (appVms != null && appVms.length !=0) {
          for (SystemMember appVm : appVms) {
            membersList.add(appVm.getId());
          }
        }
        members = new String[membersList.size()];
        members = membersList.toArray(members);
      }
    } catch (AdminException e) {
      logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0, "getMembers", e);
      throw new OperationsException(e.getMessage());
    } catch (Exception e) {
      logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0, "getMembers", e);
      throw new OperationsException(e.getMessage());
    }
    
    return members;
  }

  /**
   * Returns information including ObjectNames for all regions on a member with
   * given member id.
   * 
   * @param memberId
   *          member identifier as a String
   * @return Map of details of all regions on a member with given id
   * @throws OperationsException
   *           if fails to retrieve the regions information
   */
  public Map> getRegions(String memberId) throws OperationsException {
    Map> regionsInfo = new LinkedHashMap>();
    
    if (memberId != null) {
      try {
        SystemMemberJmx foundMember = findMember(memberId);
        if (foundMember != null) {
          SystemMemberCacheJmxImpl cache = (SystemMemberCacheJmxImpl) foundMember.getCache();
          if (cache != null) {
            Map existingRegionMbeans = getExistingRegionMbeansFullPaths(memberId);
            //TODO: this is in-efficient
            //Can a region.create JMX notification be used?
            regionsInfo = getAllRegionsDetails(cache, existingRegionMbeans);
            existingRegionMbeans.clear();
          }
        }
      } catch (AdminException e) {
        logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0_FOR_MEMBER_1, new Object[]{"getRegions", memberId}, e);
        throw new OperationsException(e.getMessage());
      } catch (Exception e) {
        logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0_FOR_MEMBER_1, new Object[]{"getRegions", memberId}, e);
        throw new OperationsException(e.getMessage());
      }
    }
    
    return regionsInfo;
  }
  
  /* **************************************************************************/
  /* ************* INITIALIZE THE ENTIRE ADMIN DS AT A TIME *******************/
  /* **************************************************************************/
  /**
   * Initializes all the possible MBeans for all the members.
   * 
   */
  private void initializeAll() throws OperationsException {
    try {
      connectToSystem();
      if (adminDSJmx != null) {
        //Members are already inited after connectToSystem. Now init Cache, Region & Stats MBeans
        SystemMember[] cacheVms = adminDSJmx.getCacheVms();
        for (int i = 0; i < cacheVms.length; i++) {
          try {
            initializeCacheRegionsAndStats((SystemMemberJmx)cacheVms[i]);
          } catch (AdminException e) {
            logWriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INTIALIZING_0_CONTINUING, 
                           cacheVms[i].getId(), e);
          }
        }
        SystemMember[] appVms = adminDSJmx.getSystemMemberApplications();
        for (int i = 0; i < appVms.length; i++) {
          try {
            initializeCacheRegionsAndStats((SystemMemberJmx)appVms[i]);
          } catch (AdminException e) {
            logWriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INTIALIZING_0_CONTINUING, 
                           appVms[i].getId(), e);
          }
        }
      }
    } catch (AdminException e) {
      logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INTIALIZING, e);
      throw new OperationsException(e.getMessage());
    } catch (Exception e) {
      logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INTIALIZING, e);
      throw new OperationsException(e.getMessage());
    }
    
    isInitialized = true;
  }
  
  /**
   * Initializes Cache, Regions & Statistics Types MBeans for the given Member.
   * 
   * @param memberJmx
   *          Member Mbean instance
   * @throws OperationsException
   *           if fails to initialize required MBeans
   * @throws AdminException
   *           if fails to initialize required MBeans
   */
  private void initializeCacheRegionsAndStats(SystemMemberJmx memberJmx) 
    throws OperationsException, AdminException {
    if (memberJmx != null) {
      SystemMemberCacheJmxImpl cache = (SystemMemberCacheJmxImpl) memberJmx.getCache();
      if (cache != null) {
        RegionSubRegionSnapshot regionSnapshot = cache.getRegionSnapshot();
        initializeRegionSubRegions(cache, regionSnapshot);
      }
      initStats(memberJmx);
    }
  }

  /**
   * Initializes statistics for a member with the given mbean.
   * 
   * @param memberJmx
   *          Member Mbean instance
   * @throws AdminException
   *           if fails to initialize required statistic MBeans
   */
  private void initStats(SystemMemberJmx memberJmx) throws AdminException {
    StatisticResource[] statResources = memberJmx.getStats();
    for (StatisticResource statResource : statResources) {
      statResource.getStatistics();
    }
  }

  /**
   * Initializes all regions & its subregions using the Cache MBean and the
   * RegionSubRegionSnapshot for this cache MBean.
   * 
   * @param cache
   *          Cache MBean resource
   * @param regionSnapshot
   *          RegionSubRegionSnapshot instance for the cache
   * @throws MalformedObjectNameException
   *           if fails to initialize the region MBean
   * @throws AdminException
   *           if fails to initialize the region MBean
   */
  @SuppressWarnings("rawtypes")
  private void initializeRegionSubRegions(SystemMemberCacheJmxImpl cache, 
                                 RegionSubRegionSnapshot regionSnapshot) 
                                   throws MalformedObjectNameException, 
                                          AdminException {
    String fullPath = regionSnapshot.getFullPath();
    if (!fullPath.equals(PLACE_HOLDER_ROOT_REGION)) {
      fullPath = fullPath.substring(PLACE_HOLDER_ROOT_REGION.length()-1);

      cache.manageRegion(fullPath);
    }
    
    Set subRegionSnapshots = regionSnapshot.getSubRegionSnapshots();
    
    for (Iterator iterator = subRegionSnapshots.iterator(); iterator.hasNext();) {
      RegionSubRegionSnapshot subRegion = (RegionSubRegionSnapshot) iterator.next();
      try {
        initializeRegionSubRegions(cache, subRegion);
      } catch (AdminException e) {
        logWriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INTIALIZING_0_CONTINUING, subRegion.getFullPath(), e);
      }
    }
  }
  
  
  /* **************************************************************************/
  /* ********************** EVERYTHING HYPERIC NEEDS **************************/
  /* **************************************************************************/
  
  /* constants defined that could be used simply retrieve needed info from Map */
  private static final String TYPE_NAME_CACHESERVER = "Cache Server";
  private static final String TYPE_NAME_APPLICATION = "Application Peer";
  private static final String TYPE_NAME_GATEWAYHUB  = "Gateway Hub";
  /*
   * NOTE -
   * (My Understanding about the followings - abhishek)  
   * 1. CacheVM - a VM started using Cache Server Launcher. This is considered 
   * to be a dedicated cache VM because there is only GemFire Cache code 
   * running here.
   * 2. ApplicationVM - a VM started with a written code using APIs and we can 
   * not guarantee that there will be ONLY GemFire code running in this VM.
   * 3. Cache Server - Responsible for serving requests from the clients. There 
   * could be multiple of these per Cache and hence per VM - one of 1 or 2 above.
   * These could be specified by  (or deprecated )
   * element(s) in the cache-xml file or using an API Cache.addCacheServer().
   */

//  private static final String VERSION          = "gemfire.version.string";
//  private static final String MEMBER_COUNT     = "gemfire.membercount.int";
//  private static final String GATEWAYHUB_COUNT = "gemfire.gatewayhubcount.int";
//  private static final String CLIENT_COUNT     = "gemfire.clientcount.int";

  private static final String MEMBER_ID       = "gemfire.member.id.string";
  private static final String MEMBER_NAME     = "gemfire.member.name.string";
  private static final String MEMBER_HOST     = "gemfire.member.host.string";
  private static final String MEMBER_PORT     = "gemfire.member.port.int";
  private static final String MEMBER_UPTIME   = "gemfire.member.uptime.long";
  private static final String MEMBER_CLIENTS  = "gemfire.member.clients.map";
  private static final String MEMBER_REGIONS  = "gemfire.member.regions.map";
  private static final String MEMBER_TYPE     = "gemfire.member.type.string";
  private static final String IS_SERVER       = "gemfire.member.isserver.boolean";
  private static final String IS_GATEWAY      = "gemfire.member.isgateway.boolean";

  private static final String MEMBER_STATSAMPLING_ENABLED = "gemfire.member.config.statsamplingenabled.boolean";
  private static final String MEMBER_TIME_STATS_ENABLED   = "gemfire.member.config.timestatsenabled.boolean";
  
  private static final String GATEWAYHUB_ID            = "gemfire.member.gatewayhub.id.string";
  private static final String GATEWAYHUB_ISPRIMARY     = "gemfire.member.gatewayhub.isprimary.boolean";
  private static final String GATEWAYHUB_LISTENINGPORT = "gemfire.member.gatewayhub.listeningport.int";
  private static final String GATEWAYHUB_GATEWAYS      = "gemfire.member.gatewayhub.gateways.collection";
  private static final String GATEWAY_ID               = "gemfire.member.gateway.id.string";
  private static final String GATEWAY_QUEUESIZE        = "gemfire.member.gateway.queuesize.int";
  private static final String GATEWAY_ISCONNECTED      = "gemfire.member.gateway.isconnected.boolean";
  private static final String GATEWAY_ENDPOINTS        = "gemfire.member.gateway.endpoints.collection";
  private static final String GATEWAYENDPOINT_ID       = "gemfire.member.gateway.endpoint.id.string";
  private static final String GATEWAYENDPOINT_HOST     = "gemfire.member.gateway.endpoint.host.string";
  private static final String GATEWAYENDPOINT_PORT     = "gemfire.member.gateway.endpoint.port.int";

  private static final String STATS_PROCESSCPUTIME = "gemfire.member.stat.processcputime.long";
  private static final String STATS_CPUS           = "gemfire.member.stat.cpus.int";
  private static final String STATS_USEDMEMORY     = "gemfire.member.stat.usedmemory.long";
  private static final String STATS_MAXMEMORY      = "gemfire.member.stat.maxmemory.long";
  private static final String STATS_GETS           = "gemfire.member.stat.gets.int";
  private static final String STATS_GETTIME        = "gemfire.member.stat.gettime.long";
  private static final String STATS_PUTS           = "gemfire.member.stat.puts.int";
  private static final String STATS_PUTTIME        = "gemfire.member.stat.puttime.long";
  
  private static final String REGION_NAME           = "gemfire.region.name.string";
  private static final String REGION_PATH           = "gemfire.region.path.string";
  private static final String REGION_SCOPE          = "gemfire.region.scope.string";
  private static final String REGION_DATAPOLICY     = "gemfire.region.datapolicy.string";
  private static final String REGION_INTERESTPOLICY = "gemfire.region.interestpolicy.string";
  private static final String REGION_ENTRYCOUNT     = "gemfire.region.entrycount.int";
  private static final String REGION_DISKATTRS      = "gemfire.region.diskattrs.string";

  private static final String CLIENT_ID                = "gemfire.client.id.string";
  private static final String CLIENT_NAME              = "gemfire.client.name.string";
  private static final String CLIENT_HOST              = "gemfire.client.host.string";
  private static final String CLIENT_QUEUESIZE         = "gemfire.client.queuesize.int";
  private static final String CLIENT_STATS_GETS        = "gemfire.client.stats.gets.int";
  private static final String CLIENT_STATS_PUTS        = "gemfire.client.stats.puts.int";
  private static final String CLIENT_STATS_CACHEMISSES = "gemfire.client.stats.cachemisses.int";
  private static final String CLIENT_STATS_CPUUSAGE    = "gemfire.client.stats.cpuusage.long";
  private static final String CLIENT_STATS_CPUS        = "gemfire.client.stats.cpus.int";
  private static final String CLIENT_STATS_UPDATETIME  = "gemfire.client.stats.updatetime.long";
  private static final String CLIENT_STATS_THREADS     = "gemfire.client.stats.threads.int";
  
  /**
   * 
   * @param memberId
   * @return All the required details for a member with given memberId 
   * @throws OperationsException
   */
  public Map getMemberDetails(String memberId) 
    throws OperationsException {
    Map allDetails = new TreeMap();

    if (memberId != null) {
      try {
        SystemMemberJmx member = findMember(memberId);
        if (member != null) {
          SystemMemberCacheJmxImpl cache = (SystemMemberCacheJmxImpl) member.getCache();
          GemFireMemberStatus snapshot   = cache.getSnapshot();
          boolean isServer     = snapshot.getIsServer();
          boolean isGatewayHub = snapshot.getIsGatewayHub();
          
          //1. Member info
          allDetails.put(MEMBER_ID, member.getId());
          allDetails.put(MEMBER_NAME, member.getName());
          String host = member.getHost();//from of GemFireVM.getHost
          InetAddress hostAddr = member.getHostAddress();
          //possibility of null host address
          if (hostAddr != null) {
            host = hostAddr.getHostName();
          }
          allDetails.put(MEMBER_HOST, host);
          allDetails.put(MEMBER_UPTIME, snapshot.getUpTime());
          allDetails.put(IS_SERVER, isServer);
          allDetails.put(IS_GATEWAY, isGatewayHub);
          
          String memberType = "";
          if (isGatewayHub) {
            memberType = TYPE_NAME_GATEWAYHUB;
          } else if (member instanceof CacheServerJmxImpl) {
            memberType = TYPE_NAME_CACHESERVER;
          } else {//Mark it of Application type if neither a gateway hub nor a server
            memberType = TYPE_NAME_APPLICATION;
          }
//          if (isGatewayHub) {
//            memberType = TYPE_NAME_GATEWAYHUB;
//          } else if (isServer) {
//            memberType = TYPE_NAME_CACHESERVER;
//          } else {//Mark it of Application type if neither a gateway nor a server
//            memberType = TYPE_NAME_APPLICATION;
//          }
          allDetails.put(MEMBER_TYPE, memberType);

          //2. Region info
          Map existingRegionMbeans = getExistingRegionMbeansFullPaths(memberId);
          allDetails.put(MEMBER_REGIONS, getAllRegionsDetails(cache, existingRegionMbeans));
          existingRegionMbeans.clear();

          //3. Clients info
          allDetails.put(MEMBER_CLIENTS, getClientDetails(snapshot));
          
          //4. GatewayHub info
          if (isGatewayHub) {
            GatewayHubStatus hubStatus = (GatewayHubStatus) snapshot.getGatewayHubStatus();
            allDetails.putAll(getAllGatewayDetails(hubStatus));
          }
          
          boolean statSamplingEnabled = true;
          //assuming will never return as per current implementation
          ConfigurationParameter[] configParams = member.getConfiguration();
          for (ConfigurationParameter configParam : configParams) {
            if (DistributionConfig.STATISTIC_SAMPLING_ENABLED_NAME.equals(configParam.getName())) {
              allDetails.put(MEMBER_STATSAMPLING_ENABLED, configParam.getValue());
              statSamplingEnabled = Boolean.parseBoolean(""+configParam.getValue());
            } else if (DistributionConfig.ENABLE_TIME_STATISTICS_NAME.equals(configParam.getName())) {
              allDetails.put(MEMBER_TIME_STATS_ENABLED, configParam.getValue());
            }
          }
          
          //5. Stats info
          allDetails.putAll(getRequiredStats(member, statSamplingEnabled));

          SystemMemberCacheServer[] cacheServers = cache.getCacheServers();
          //attempt refreshing the cache info once
          if (cacheServers.length == 0) {
            cache.refresh();
            cacheServers = cache.getCacheServers();
          }
          Integer memberCacheServerPort = Integer.valueOf(0);
          if (cacheServers.length != 0) {
            /*
             * Taking the first cache server port.
             * We don't recommend multiple cache severs for a cache.
             */
            memberCacheServerPort = Integer.valueOf(cacheServers[0].getPort());
          }
          allDetails.put(MEMBER_PORT, memberCacheServerPort);
        }
        
      } catch (AdminException e) {
        logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0_FOR_MEMBER_1, 
                          new Object[] {"getMemberDetails", memberId}, e);
        throw new OperationsException(e.getMessage());
      } catch (Exception e) {
        logWriter.warning(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0_FOR_MEMBER_1, 
                          new Object[] {"getMemberDetails", memberId}, e);
        throw new OperationsException(e.getMessage());
      }
    }
    
    return allDetails;
  }
  
  /**
   * 
   * @param snapshot
   * @return Map of client details 
   */
  @SuppressWarnings("rawtypes")
  private Map> getClientDetails(GemFireMemberStatus snapshot) {
    Map> clientsInfo = 
      new LinkedHashMap>();
    
    Set connectedClients = snapshot.getConnectedClients();
    if (!connectedClients.isEmpty()) {
      Map clientHealthStatsMap = snapshot.getClientHealthStats();
      
      for (Iterator iterator = connectedClients.iterator(); iterator.hasNext();) {
        Map clientData = new HashMap();
        String clientId = (String) iterator.next();
        String host     = snapshot.getClientHostName(clientId);
        clientData.put(CLIENT_ID, clientId);
        clientData.put(CLIENT_NAME, extractClientName(clientId, host));
        clientData.put(CLIENT_HOST, host);
        clientData.put(CLIENT_QUEUESIZE, snapshot.getClientQueueSize(clientId));
  
        ClientHealthStats clientHealthStats = (ClientHealthStats) clientHealthStatsMap.get(clientId);
        if (clientHealthStats != null) {
          clientData.put(CLIENT_STATS_GETS, clientHealthStats.getNumOfGets());
          clientData.put(CLIENT_STATS_PUTS, clientHealthStats.getNumOfPuts());
          clientData.put(CLIENT_STATS_CACHEMISSES, clientHealthStats.getNumOfMisses());
          clientData.put(CLIENT_STATS_CPUUSAGE, clientHealthStats.getProcessCpuTime());
          clientData.put(CLIENT_STATS_CPUS, clientHealthStats.getCpus());
          clientData.put(CLIENT_STATS_UPDATETIME, clientHealthStats.getUpdateTime().getTime());
          clientData.put(CLIENT_STATS_THREADS, clientHealthStats.getNumOfThreads());
        } else {
          clientData.put(CLIENT_STATS_GETS, Integer.valueOf(0));
          clientData.put(CLIENT_STATS_PUTS, Integer.valueOf(0));
          clientData.put(CLIENT_STATS_CACHEMISSES, Integer.valueOf(0));
          clientData.put(CLIENT_STATS_CPUUSAGE, Long.valueOf(0));
          clientData.put(CLIENT_STATS_CPUS, Integer.valueOf(0));
          clientData.put(CLIENT_STATS_UPDATETIME, Long.valueOf(0));
          clientData.put(CLIENT_STATS_THREADS, Integer.valueOf(0));
        }
        
        clientsInfo.put(clientId, clientData);
      }
    }
    
    return clientsInfo;
  }

  /**
   * Returns a Map containing information about regions.
   * 
   * @param cache
   *          Reference to an MBean representing a Cache on a member
   * @param existingRegionMbeans
   *          Map of Path against Region MBean ObjectNames
   * @return Map of all region details
   * @throws OperationsException
   *           if fails to retrieve
   */
  private Map> getAllRegionsDetails(
                                  SystemMemberCacheJmxImpl cache, 
                                  Map existingRegionMbeans) 
                                  throws OperationsException {
    Map> regionsInfo = 
                        new TreeMap>();
    
    if (cache != null) {
      try {
        RegionSubRegionSnapshot regionSnapshot = cache.getRegionSnapshot();
        collectAllRegionsDetails(cache, regionSnapshot, regionsInfo, existingRegionMbeans);
      } catch (AdminException e) {
        logWriter.warning(LocalizedStrings.ONE_ARG, "Exception occurred while getting region details.", e);
        throw new OperationsException(e.getMessage());
      } catch (Exception e) {
        logWriter.warning(LocalizedStrings.ONE_ARG, "Exception occurred while getting region details.", e);
        throw new OperationsException(e.getMessage());
      }
    }
    
    return regionsInfo;
  }

  /**
   * Collects all the region details from the RegionSubRegionSnapshot instance
   * passed and the Cache MBean. Checks in the set of existingRegionMbeans
   * before initializing Region Mbeans if there are not initialized yet.
   * 
   * @param cache
   *          Cache MBean instance
   * @param regionSnapshot
   *          RegionSubRegionSnapshot instance
   * @param regionsInfo
   *          Map of regions information that gets populated recursively
   * @param existingRegionMbeans
   *          Map of ObjectNames of existing region MBeans
   * @throws AdminException
   *           if unable to initialize region MBean
   * @throws OperationsException
   *           if fails to retrieve the Region MBean attribute info
   * @throws MBeanException
   *           if fails to retrieve the Region MBean attribute info
   * @throws ReflectionException
   *           if fails to retrieve the Region MBean attribute info
   */
  @SuppressWarnings("rawtypes")
  private void collectAllRegionsDetails(SystemMemberCacheJmxImpl cache, 
                                 RegionSubRegionSnapshot regionSnapshot, 
                                 Map> regionsInfo,
                                 Map existingRegionMbeans) 
                                 throws AdminException, OperationsException, 
                                        MBeanException, ReflectionException {
    String fullPath = regionSnapshot.getFullPath();
    if (!fullPath.equals(PLACE_HOLDER_ROOT_REGION)) {
      fullPath = fullPath.substring(PLACE_HOLDER_ROOT_REGION.length()-1);
      String name = regionSnapshot.getName();
      Integer entryCount = Integer.valueOf(regionSnapshot.getEntryCount());
      Map details = new TreeMap();
      details.put(REGION_NAME, name);
      details.put(REGION_PATH, fullPath);
      details.put(REGION_ENTRYCOUNT, entryCount);
      
      ObjectName regionObjectName = existingRegionMbeans.get(fullPath);
      if (regionObjectName == null) {//initialize if has not yet been 
        regionObjectName = cache.manageRegion(fullPath);
      }

      Object attribute = getAttribute(regionObjectName, "scope", NOT_AVAILABLE);
      attribute = attribute != null ? attribute.toString() : attribute;
      details.put(REGION_SCOPE, attribute);
      
      attribute = getAttribute(regionObjectName, "dataPolicy", NOT_AVAILABLE);
      attribute = attribute != null ? attribute.toString() : attribute;
      details.put(REGION_DATAPOLICY, attribute);

      SubscriptionAttributes interestPolicyAttr = 
          (SubscriptionAttributes) getAttribute(regionObjectName, 
                                                "subscriptionAttributes", null);
      String interestPolicyStr = NOT_AVAILABLE;
      if (interestPolicyAttr != null) {
        InterestPolicy interestPolicy = interestPolicyAttr.getInterestPolicy();
        if (interestPolicy != null) {
          interestPolicyStr = interestPolicy.toString();
        }
      }
      details.put(REGION_INTERESTPOLICY, interestPolicyStr);

      attribute = getAttribute(regionObjectName, "diskWriteAttributes", NOT_AVAILABLE);
      attribute = attribute != null ? attribute.toString() : attribute;
      details.put(REGION_DISKATTRS, attribute);
      
      regionsInfo.put(fullPath, details);
    }
    
    Set subRegionSnapshots = regionSnapshot.getSubRegionSnapshots();
    
    for (Iterator iterator = subRegionSnapshots.iterator(); iterator.hasNext();) {
      RegionSubRegionSnapshot subRegion = (RegionSubRegionSnapshot) iterator.next();
      collectAllRegionsDetails(cache, subRegion, regionsInfo, existingRegionMbeans);
    }
  }
  
  /**
   * Checks if the given host name string contains ':' as in IPv6 host address.
   * 
   * @param host
   *          host name string
   * @return true if the host string contains ':', false otherwise
   */
  private static boolean isIPv6(String host) {
    return host.contains(":");
  }

  /**
   * Checks if the given host name is actually a String representation of an
   * IPv4 address.
   * 
   * @param host
   *          host name string
   * @return true if given host name is a String representation of an IPv4
   *         address, false otherwise
   */
  private static boolean isIPv4(String host) {
    String regex = "\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}";
    
    return host.matches(regex);
  }
  
  /**
   * Excludes the host name from the client id and returns the String. If the
   * host name can not be detected, returns an empty string. Typically, the
   * client id looks like: HOST(VM_PID:VM_KIND):PORT:RANDOM_STRING:CLIENT_NAME
   * 
   * Extracts the client name from the client id. If the client id is not in the
   * expected format, returns 'N/A'
   * 
   * @param clientId
   *          string identifier for a client
   * @param host
   *          host name (FQDN) the client is running on
   * @return name extracted from given client id
   */
  /*
   * Some examples of Client Id format: 
   * (1) Java Client:
   * nase(21716:loner):51789:42e9a0bf:client_nase_21716
   * nase(2560:loner):2:7a84729a:Feeder
   * 
   * (2) Native Client:
   * nase(21045:loner):2:GFNative_OnNnEpyRWL:ExampleDistributedSystem
   * 
   * (3) IPv6 Host whose name can not be resolved:
   * fdf0:76cf:a0ed:9449:0:0:0:1001(21716:loner):51789:42e9a0b:client_nase_21716
   * fdf0:76cf:a0ed:9449:0:0:0:1001:51789:42e9a0b:client_nase_21716 
   */
  private static String extractClientName(String clientId, String host) {
    /* This isIPv6, isIPv4, extractClientName is taken from GFMon code base*/
    String hostExcludedId = "";
    if ( (isIPv6(host) || isIPv4(host))&& clientId.startsWith(host)) {
      hostExcludedId = clientId.substring(host.length());
    } else {
      int firstDotIndex = host.indexOf(".");
      if (firstDotIndex != -1) {
        String hostShortName = host.substring(0, firstDotIndex);
        hostExcludedId = clientId.substring(hostShortName.length());
      }
    }
    
    String vmPIDAndKindRegex = "\\(\\w+:\\w+\\)";
    String regex             = "(\\)?:[0-9]+(:\\w+){2}+";
    String name              = NOT_AVAILABLE;
    String temp              = hostExcludedId;

    int openIndex = temp.indexOf("(");
    if (openIndex != -1) {      
      regex = vmPIDAndKindRegex + regex;
    }

    if (temp.matches(regex)) {
      String[] splitted = temp.split(":");
      name = splitted[splitted.length - 1];
    }
    
    return name;
  }

  /**
   * Returns all the gateway hub details from the given GatewayHubStatus
   * 
   * @param gatewayHubStatus GatewayHubStatus instance
   * @return Map of gateway hub details
   */
  private Map getAllGatewayDetails(
                                    GatewayHubStatus gatewayHubStatus){
    Map hubDetails = new TreeMap();
    
    if (gatewayHubStatus != null) {
      hubDetails.put(GATEWAYHUB_ID, gatewayHubStatus.getId());
      hubDetails.put(GATEWAYHUB_ISPRIMARY, gatewayHubStatus.getIsPrimary());
      hubDetails.put(GATEWAYHUB_LISTENINGPORT, gatewayHubStatus.getPort());
  
      GatewayStatus[] gatewayStatuses = gatewayHubStatus.getGatewayStatuses();

      List> gatewayList = new ArrayList>();
      for (GatewayStatus gatewayStatus : gatewayStatuses) {
        Map gatewayDetails = new HashMap();
        gatewayDetails.put(GATEWAY_ID, gatewayStatus.getId());
        gatewayDetails.put(GATEWAY_QUEUESIZE, gatewayStatus.getQueueSize());
        gatewayDetails.put(GATEWAY_ISCONNECTED, gatewayStatus.getIsConnected());
        
        List> endpointsList = new ArrayList>();
        GatewayEndpointStatus[] endpointStatuses = gatewayStatus.getEndpointStatuses();
        for (GatewayEndpointStatus endpointStatus : endpointStatuses) {
          Map gatewayEndpointDetails = new HashMap();
          gatewayEndpointDetails.put(GATEWAYENDPOINT_ID, endpointStatus.getId());
          gatewayEndpointDetails.put(GATEWAYENDPOINT_HOST, endpointStatus.getHost());
          gatewayEndpointDetails.put(GATEWAYENDPOINT_PORT, endpointStatus.getPort());
          endpointsList.add(gatewayEndpointDetails);
        }
        gatewayDetails.put(GATEWAY_ENDPOINTS, endpointsList);
        gatewayList.add(gatewayDetails);
      }
      hubDetails.put(GATEWAYHUB_GATEWAYS, gatewayList);
    }
    
    return hubDetails;
  }

  /**
   * Returns a Map of all the statistics required for Hyperic currently. It
   * relies on the attribute of the StatisticsResource Mbeans.
   * 
   * @param member
   *          instance for which the stats are needed
   * @return Map of all the statistics required for Hyperic currently.
   * @throws OperationsException
   *           exceptions thrown while retrieving the attributes
   */
  private Map getRequiredStats(SystemMemberJmx member, boolean statSamplingEnabled) throws OperationsException {
    Map statDetails = new TreeMap();
    
    try {
      if (!statSamplingEnabled) {
        statDetails.put(STATS_PROCESSCPUTIME, NOT_AVAILABLE_NUMBER);
        statDetails.put(STATS_CPUS, NOT_AVAILABLE_NUMBER);
        statDetails.put(STATS_MAXMEMORY, NOT_AVAILABLE_NUMBER);
        statDetails.put(STATS_USEDMEMORY, NOT_AVAILABLE_NUMBER);
        statDetails.put(STATS_GETS, NOT_AVAILABLE_NUMBER);
        statDetails.put(STATS_GETTIME, NOT_AVAILABLE_NUMBER);
        statDetails.put(STATS_PUTS, NOT_AVAILABLE_NUMBER);
        statDetails.put(STATS_PUTTIME, NOT_AVAILABLE_NUMBER);
      } else {
        MBeanServer mBeanServer = agent.getMBeanServer();
        Number defaultVal     = NOT_AVAILABLE_NUMBER;
        Number processCpuTime = defaultVal;
        Number cpus           = defaultVal;
        Number maxMemory      = defaultVal;
        Number usedMemory     = defaultVal;
        Number gets           = defaultVal;
        Number getTime        = defaultVal;
        Number puts           = defaultVal;
        Number putTime        = defaultVal;
        
        ObjectName[] vmMemoryUsageStats = getExistingStats(member.getId(), "vmHeapMemoryStats");
        ObjectName[] vmStats            = getExistingStats(member.getId(), "vmStats");
        ObjectName[] cachePerfStats     = getExistingStats(member.getId(), "cachePerfStats");
        boolean needToReinit = false;
        if (vmMemoryUsageStats.length == 0 || vmStats.length == 0 || cachePerfStats.length == 0) {
          //if the StatisticResource MBeans are not created
          needToReinit = true;
        }
        if(!needToReinit) {
          /*
           * To handle a case when the StatisticResource MBeans are created but 
           * not registered with RefreshTimer. If VMMemoryUsageStats are 
           * present, maxMemory should always be non-zero. */
          for (int i = 0; i < vmMemoryUsageStats.length; i++) {//ideally there should be a single instance
            String type = (String) mBeanServer.getAttribute(vmMemoryUsageStats[i], "type");
            
            if ("VMMemoryUsageStats".equals(type)) { //first instance that has Statistics Type name
              maxMemory  = (Number) getAttribute(vmMemoryUsageStats[i], "maxMemory", defaultVal);
              break;
            }
          }
          
          needToReinit = 0 == maxMemory.longValue(); 
        }

        if(needToReinit) {
          logWriter.info(LocalizedStrings.MemberInfoWithStatsMBean_REINITIALIZING_STATS_FOR_0, member.getId());
          initStats(member);

          vmMemoryUsageStats = getExistingStats(member.getId(), "vmHeapMemoryStats");
          vmStats            = getExistingStats(member.getId(), "vmStats");
          cachePerfStats     = getExistingStats(member.getId(), "cachePerfStats");
        }
        
        for (int i = 0; i < vmMemoryUsageStats.length; i++) {//ideally there should be a single instance
          String type = (String) mBeanServer.getAttribute(vmMemoryUsageStats[i], "type");
          
          if ("VMMemoryUsageStats".equals(type)) { //first instance that has Statistics Type name
            maxMemory  = (Number) getAttribute(vmMemoryUsageStats[i], "maxMemory", defaultVal);
            usedMemory = (Number) getAttribute(vmMemoryUsageStats[i], "usedMemory", defaultVal);
            break;
          }
        }

        for (int i = 0; i < vmStats.length; i++) {//ideally there should be a single instance
          String type = (String) mBeanServer.getAttribute(vmStats[i], "type");
          
          if ("VMStats".equals(type)) { //first instance that has Statistics Type name
            processCpuTime = (Number) getAttribute(vmStats[i], "processCpuTime", defaultVal);
            cpus           = (Number) getAttribute(vmStats[i], "cpus", defaultVal);
            break;
          }
        }
        
        for (int i = 0; i < cachePerfStats.length; i++) {//ideally there should be a single instance
          String type = (String) mBeanServer.getAttribute(cachePerfStats[i], "type");
          
          if ("CachePerfStats".equals(type)) { //first instance that has Statistics Type name
            gets    = (Number) getAttribute(cachePerfStats[i], "gets", defaultVal);
            getTime = (Number) getAttribute(cachePerfStats[i], "getTime", defaultVal);
            puts    = (Number) getAttribute(cachePerfStats[i], "puts", defaultVal);
            putTime = (Number) getAttribute(cachePerfStats[i], "putTime", defaultVal);
            break;
          }
        }
        
        statDetails.put(STATS_PROCESSCPUTIME, processCpuTime == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : processCpuTime.longValue());
        statDetails.put(STATS_CPUS, cpus == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : cpus.intValue());
        statDetails.put(STATS_MAXMEMORY,  maxMemory == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : maxMemory.longValue());
        statDetails.put(STATS_USEDMEMORY, usedMemory == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : usedMemory.longValue());
        statDetails.put(STATS_GETS, gets == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : gets.intValue());
        statDetails.put(STATS_GETTIME, getTime == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : getTime.intValue());
        statDetails.put(STATS_PUTS, puts == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : puts.intValue());
        statDetails.put(STATS_PUTTIME, putTime == NOT_AVAILABLE_NUMBER ? NOT_AVAILABLE_NUMBER : putTime.longValue());
      }
    } catch (Exception e) {
      logWriter.warning(e);
      throw new OperationsException(e.getMessage());
    }
    
    return statDetails;
  }

  /**
   * Returns attribute with given attribute name on MBean with given ObjectName.
   * 
   * 
   * @param objectName
   *          ObjectName for the MBean
   * @param attribute
   *          attribute name
   * @param unavailableValue
   *          return this value if the attribute value is null
   * @return value of attribute with given attribute name
   * @throws OperationsException
   *           if attribute is not found for MBean with this ObjectName or MBean
   *           instance is not found
   * @throws MBeanException
   *           if MBeans getter throws exception
   * @throws ReflectionException
   *           thrown when trying to invoke the setter.
   */
  private Object getAttribute(ObjectName objectName, String attribute, 
      Object unavailableValue) 
        throws OperationsException, MBeanException, ReflectionException {
    /* NOTE: callers methods rely on non-null value being returned */
    Object value = null;
    
    MBeanServer mBeanServer = agent.getMBeanServer();
    value = mBeanServer.getAttribute(objectName, attribute);
    
    value = (value != null)? value : unavailableValue;
    
    return value;
  }

  /**
   * Return Map of region full against the ObjectName of existing region MBeans.
   * 
   * @param memberId
   *          string identifier of a member
   * @return Map of region path vs ObjectName for existing region MBeans
   * @throws MalformedObjectNameException
   *           If the query expression used is not valid
   */
  private Map getExistingRegionMbeansFullPaths(String memberId) throws MalformedObjectNameException {
    Map pathsToObjName = new HashMap();

    if (memberId != null && memberId.trim().length() != 0) {
      Object[] params = new Object[] {MBeanUtil.makeCompliantMBeanNameProperty(memberId)};
      Set queryNames = queryObjectNames(REGION_QUERY_EXPRESSION, params);
      for (ObjectName objectName : queryNames) {
        pathsToObjName.put(objectName.getKeyProperty("path"), objectName);
      }
    }
    
    return pathsToObjName;
  }

  /**
   * Returns an array of ObjectNames existing statistics types MBeans
   * 
   * @param memberId
   *          string identifier of a member
   * @param name
   *          text id of the stats which appears in the stats ObjectName as name
   *          keyProperty
   * @return Array of Stats MBean ObjectNames
   * @throws MalformedObjectNameException
   *           If the query expression used is not valid
   */
  private ObjectName[] getExistingStats(String memberId, String name) throws MalformedObjectNameException {
    ObjectName[] statObjectNames = new ObjectName[0];
    
    if (memberId != null && memberId.trim().length() != 0) {
      Object[] params = new Object[] {MBeanUtil.makeCompliantMBeanNameProperty(memberId), name};
      Set queryNames = queryObjectNames(STATS_QUERY_EXPRESSION, params);
      statObjectNames = new ObjectName[queryNames.size()];
      statObjectNames = queryNames.toArray(statObjectNames);
    }
    
    return statObjectNames;
  }

  /**
   * Queries the MBean server with the string formed using placing the params in
   * the parameterized string passed as queryStr.
   * 
   * @param queryStr
   *          parameterized string
   * @param params
   *          params to put in the string
   * @return results of an ObjectName query
   * @throws MalformedObjectNameException
   *           If the query expression ObjectName formed is not valid
   */
  private Set queryObjectNames(String queryStr, Object ... params) 
    throws MalformedObjectNameException {
    Set queried = Collections.emptySet();    
    
    queryStr = MessageFormat.format(queryStr, params);    
    ObjectName queryExp = ObjectName.getInstance(queryStr);
    queried = agent.getMBeanServer().queryNames(null, queryExp);
    
    return queried;
  }
  
  
  /* *************************************************************************/
  /* **************** NOTIFICATION EMITTER IMPLEMENTATION ********************/
  /* *************************************************************************/

  /**
   * @see NotificationEmitter#addNotificationListener(NotificationListener, NotificationFilter, Object)
   */
  public void addNotificationListener(NotificationListener listener,
      NotificationFilter filter, Object handback)
      throws IllegalArgumentException {
    forwarder.addNotificationListener(listener, filter, handback);
  }

  /**
   * @see NotificationEmitter#removeNotificationListener(NotificationListener)
   */
  public void removeNotificationListener(NotificationListener listener)
      throws ListenerNotFoundException {
    forwarder.removeNotificationListener(listener);
  }

  /**
   * @see NotificationEmitter#getNotificationInfo()
   */
  public MBeanNotificationInfo[] getNotificationInfo() {
    return getMBeanInfo().getNotifications();
  }
  
  /**
   * @see NotificationEmitter#removeNotificationListener(NotificationListener, NotificationFilter, Object)
   */
  public void removeNotificationListener(NotificationListener listener,
      NotificationFilter filter, Object handback)
      throws ListenerNotFoundException {
    forwarder.removeNotificationListener(listener, filter, handback);
  }
  
}

/**
 * This class acts as a hub for the Notifications defined on
 * AdminDistributedSystem & SystemMember MBeans. This acts as a listener for
 * these notifications and broadcasts them as notifications from the
 * {@link MemberInfoWithStatsMBean} MBean. This class extends
 * {@link NotificationBroadcasterSupport} only to have the functionality to send
 * notifications.
 * 
 * @author abhishek
 * 
 * @since 6.5
 */
class NotificationForwarder extends NotificationBroadcasterSupport 
  implements NotificationListener {
  /* sequence generator for notifications from GemFireTypesWrapper MBean */
  private static AtomicLong notificationSequenceNumber = new AtomicLong();

  /* reference to the MBeanServer instance */
  private MBeanServer   mBeanServer;
  /* reference to the logger instance */
  private LogWriterI18n logwriter;

  /**
   * Default Constructor
   * 
   * @param mBeanServer
   *          reference to the MBeanServer instance
   * @param logwriter
   *          reference to the logger instance
   */
  /*default*/NotificationForwarder(MBeanServer mBeanServer, 
                                   LogWriterI18n logwriter) {
    this.mBeanServer = mBeanServer;
    this.logwriter   = logwriter;
  }

  /**
   * Handles notifications as: 1. Member Joined: Registers this
   * NotificationForwarder as a notification listener for Cache/Region
   * Notifications. 2. Member Left/Crashed: Unregisters this
   * NotificationForwarder as a notification listener for Cache/Region
   * Notifications. 3. AdminDistributedSystem Disconnected: Unregisters this
   * NotificationForwarder as a notification listener for member Notifications.
   * 
   * Forwards the notifications to the JMX Clients that have registered for
   * notifications on this MBean
   * 
   * @param notification
   *          notification to be handled
   * @param handback
   *          handback object used while NotificationForwarder was registered
   * 
   * @see NotificationListener#handleNotification(Notification, Object)
   */
  public void handleNotification(Notification notification, Object handback) {
    Object notifSource = notification.getSource();
    if (AdminDistributedSystemJmxImpl.NOTIF_MEMBER_JOINED.equals(notification.getType())) {
      ObjectName source = (ObjectName) notifSource;
      //initialize statistics/register with refreshTimer for new member
      String[] noArgs = {};
      try {
        ObjectName[] stats = (ObjectName[]) mBeanServer.invoke(source, "manageStats", noArgs, noArgs);
        if (stats != null) {
          for (ObjectName stat : stats) {
            mBeanServer.invoke(stat, "getStatistics", noArgs, noArgs);
          }
        }
        logwriter.fine("getStatistics call completed with no exceptions.");
      } catch (ReflectionException e) {
        logwriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INITIALIZING_STATISICS_FOR_0, source.toString(), e);
      } catch (MBeanException e) {
        logwriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INITIALIZING_STATISICS_FOR_0, source.toString(), e);
      } catch (InstanceNotFoundException e) {
        logwriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INITIALIZING_STATISICS_FOR_0, source.toString(), e);
      }
      //register this listener for joined member's cache/region notifications
      try {
        registerNotificationListener(source);
      } catch (OperationsException e) {
        logwriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_REGISTERING_NOTIFICATION_LISTENER_FOR_0, source.toString(), e);
      }
    } /*else if (AdminDistributedSystemJmxImpl.NOTIF_MEMBER_LEFT.equals(notification.getType()) || 
               AdminDistributedSystemJmxImpl.NOTIF_MEMBER_CRASHED.equals(notification.getType())) {
      ObjectName source = (ObjectName) notifSource;
      //unregister this listener from left member's cache/region notifications
      try {
        unregisterNotificationListener(source);
      } catch (OperationsException e) {
        logwriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_UNREGISTERING_NOTIFICATION_LISTENER_FOR_0, source.toString(), e);
      }
    } else if (AdminDistributedSystemJmxImpl.NOTIF_ADMIN_SYSTEM_DISCONNECT.equals(notification.getType())) {
      String source = (String) notifSource;
      //This notification does not have ObjectName as a source. 
      try {
        ObjectName instance = ObjectName.getInstance(source);
        unregisterNotificationListener(instance);
      } catch (OperationsException e) {
        logwriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_UNREGISTERING_NOTIFICATION_LISTENER_FOR_0, source.toString(), e);
      } catch (NullPointerException e) {
        logwriter.info(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_UNREGISTERING_NOTIFICATION_LISTENER_FOR_0, source.toString(), e);
      }
    } */
    //NOTIF_ALERT is sent as is
    
    //TODO: Check if same notification instance can be reused by simply changing the sequence number 
    notification = new Notification(notification.getType(), notifSource, 
                                    notificationSequenceNumber.addAndGet(1L), 
                                    notification.getTimeStamp(), 
                                    notification.getMessage());
    
    sendNotification(notification);
  }

  /**
   * Registers itself as a NotificationListener for Notifications sent from
   * MBean with the ObjectName given as source.
   * 
   * @param source
   *          source of notifications
   * @throws InstanceNotFoundException
   *           The MBean name provided does not match any of the registered
   *           MBeans.
   */
  /*default*/void registerNotificationListener(ObjectName source) 
    throws InstanceNotFoundException {
    mBeanServer.addNotificationListener(source, this, null/*handback*/, source);
  }

  /**
   * Unregisters itself as a NotificationListener for Notifications sent from
   * MBean with the ObjectName given as source.
   * 
   * @param source source of notifications
   * @throws InstanceNotFoundException
   *           The MBean name provided does not match any of the registered
   *           MBeans.
   * @throws ListenerNotFoundException
   *           The listener is not registered in the MBean.
   */
  /*default*/void unregisterNotificationListener(ObjectName source) 
    throws InstanceNotFoundException, ListenerNotFoundException {
    mBeanServer.removeNotificationListener(source, this);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy