blob: a2e5237affc793ca62026fcfd5d4cc0ce1542bde [file] [log] [blame]
/*
* 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.admin.jmx.internal;
import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_TIME_STATISTICS;
import static org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLING_ENABLED;
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 org.apache.logging.log4j.Logger;
import org.apache.geode.admin.AdminDistributedSystem;
import org.apache.geode.admin.AdminException;
import org.apache.geode.admin.CacheVm;
import org.apache.geode.admin.ConfigurationParameter;
import org.apache.geode.admin.GemFireMemberStatus;
import org.apache.geode.admin.RegionSubRegionSnapshot;
import org.apache.geode.admin.StatisticResource;
import org.apache.geode.admin.SystemMember;
import org.apache.geode.admin.SystemMemberCacheServer;
import org.apache.geode.admin.jmx.Agent;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.cache.InterestPolicy;
import org.apache.geode.cache.SubscriptionAttributes;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.internal.GemFireVersion;
import org.apache.geode.internal.admin.remote.ClientHealthStats;
import org.apache.geode.internal.logging.LogService;
/**
* 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.
*
*
* @since GemFire 6.5
*/
public class MemberInfoWithStatsMBean extends AbstractDynamicMBean implements NotificationEmitter {
private static final Logger logger = LogService.getLogger();
/* 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 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
*/
MemberInfoWithStatsMBean(Agent agent)
throws OperationsException, MBeanRegistrationException, AdminException {
this.agent = agent;
this.objectName = ObjectName.getInstance(MBEAN_NAME);
this.version = GemFireVersion.getGemFireVersion();
this.refreshInterval = -1;
this.id = NOT_AVAILABLE_STR;
this.forwarder = new NotificationForwarder(agent.getMBeanServer());
}
/**
* 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.");
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;
}
/**
*
* @return SystemMemberJmx instance for given memberId
*/
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<String> membersList = new ArrayList<String>();
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) {
logger.warn("Exception occurred for operation: getMembers",
e);
throw new OperationsException(e.getMessage());
} catch (Exception e) {
logger.warn(
"Exception occurred for operation: 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<String, Map<String, ?>> getRegions(String memberId) throws OperationsException {
Map<String, Map<String, ?>> regionsInfo = new LinkedHashMap<String, Map<String, ?>>();
if (memberId != null) {
try {
SystemMemberJmx foundMember = findMember(memberId);
if (foundMember != null) {
SystemMemberCacheJmxImpl cache = (SystemMemberCacheJmxImpl) foundMember.getCache();
if (cache != null) {
Map<String, ObjectName> 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) {
logger.warn(String.format("Exception occurred for operation: %s for member: %s",
new Object[] {"getRegions", memberId}),
e);
throw new OperationsException(e.getMessage());
} catch (Exception e) {
logger.warn(String.format("Exception occurred for operation: %s for member: %s",
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) {
logger.info(String.format(
"Exception occurred while intializing : %s. Contiuning with next ...",
cacheVms[i].getId()),
e);
}
}
SystemMember[] appVms = adminDSJmx.getSystemMemberApplications();
for (int i = 0; i < appVms.length; i++) {
try {
initializeCacheRegionsAndStats((SystemMemberJmx) appVms[i]);
} catch (AdminException e) {
logger.info(String.format(
"Exception occurred while intializing : %s. Contiuning with next ...",
appVms[i].getId()),
e);
}
}
}
} catch (AdminException e) {
logger.warn("Exception occurred while intializing.", e);
throw new OperationsException(e.getMessage());
} catch (Exception e) {
logger.warn("Exception occurred 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) {
logger.info(
String.format("Exception occurred while intializing : %s. Contiuning with next ...",
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";
/*
* 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 <cache-server> (or
* deprecated <bridge-server>) element(s) in the cache-xml file or using an API
* Cache.addCacheServer().
*/
private static final String MEMBER_ID = DistributionConfig.GEMFIRE_PREFIX + "member.id.string";
private static final String MEMBER_NAME =
DistributionConfig.GEMFIRE_PREFIX + "member.name.string";
private static final String MEMBER_HOST =
DistributionConfig.GEMFIRE_PREFIX + "member.host.string";
private static final String MEMBER_PORT = DistributionConfig.GEMFIRE_PREFIX + "member.port.int";
private static final String MEMBER_UPTIME =
DistributionConfig.GEMFIRE_PREFIX + "member.uptime.long";
private static final String MEMBER_CLIENTS =
DistributionConfig.GEMFIRE_PREFIX + "member.clients.map";
private static final String MEMBER_REGIONS =
DistributionConfig.GEMFIRE_PREFIX + "member.regions.map";
private static final String MEMBER_TYPE =
DistributionConfig.GEMFIRE_PREFIX + "member.type.string";
private static final String IS_SERVER =
DistributionConfig.GEMFIRE_PREFIX + "member.isserver.boolean";
private static final String IS_GATEWAY =
DistributionConfig.GEMFIRE_PREFIX + "member.isgateway.boolean";
private static final String MEMBER_STATSAMPLING_ENABLED =
DistributionConfig.GEMFIRE_PREFIX + "member.config.statsamplingenabled.boolean";
private static final String MEMBER_TIME_STATS_ENABLED =
DistributionConfig.GEMFIRE_PREFIX + "member.config.timestatsenabled.boolean";
private static final String STATS_PROCESSCPUTIME =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.processcputime.long";
private static final String STATS_CPUS =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.cpus.int";
private static final String STATS_USEDMEMORY =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.usedmemory.long";
private static final String STATS_MAXMEMORY =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.maxmemory.long";
private static final String STATS_GETS =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.gets.int";
private static final String STATS_GETTIME =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.gettime.long";
private static final String STATS_PUTS =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.puts.int";
private static final String STATS_PUTTIME =
DistributionConfig.GEMFIRE_PREFIX + "member.stat.puttime.long";
private static final String REGION_NAME =
DistributionConfig.GEMFIRE_PREFIX + "region.name.string";
private static final String REGION_PATH =
DistributionConfig.GEMFIRE_PREFIX + "region.path.string";
private static final String REGION_SCOPE =
DistributionConfig.GEMFIRE_PREFIX + "region.scope.string";
private static final String REGION_DATAPOLICY =
DistributionConfig.GEMFIRE_PREFIX + "region.datapolicy.string";
private static final String REGION_INTERESTPOLICY =
DistributionConfig.GEMFIRE_PREFIX + "region.interestpolicy.string";
private static final String REGION_ENTRYCOUNT =
DistributionConfig.GEMFIRE_PREFIX + "region.entrycount.int";
private static final String REGION_DISKATTRS =
DistributionConfig.GEMFIRE_PREFIX + "region.diskattrs.string";
private static final String CLIENT_ID = DistributionConfig.GEMFIRE_PREFIX + "client.id.string";
private static final String CLIENT_NAME =
DistributionConfig.GEMFIRE_PREFIX + "client.name.string";
private static final String CLIENT_HOST =
DistributionConfig.GEMFIRE_PREFIX + "client.host.string";
private static final String CLIENT_QUEUESIZE =
DistributionConfig.GEMFIRE_PREFIX + "client.queuesize.int";
private static final String CLIENT_STATS_GETS =
DistributionConfig.GEMFIRE_PREFIX + "client.stats.gets.int";
private static final String CLIENT_STATS_PUTS =
DistributionConfig.GEMFIRE_PREFIX + "client.stats.puts.int";
private static final String CLIENT_STATS_CACHEMISSES =
DistributionConfig.GEMFIRE_PREFIX + "client.stats.cachemisses.int";
private static final String CLIENT_STATS_CPUUSAGE =
DistributionConfig.GEMFIRE_PREFIX + "client.stats.cpuusage.long";
private static final String CLIENT_STATS_CPUS =
DistributionConfig.GEMFIRE_PREFIX + "client.stats.cpus.int";
private static final String CLIENT_STATS_UPDATETIME =
DistributionConfig.GEMFIRE_PREFIX + "client.stats.updatetime.long";
private static final String CLIENT_STATS_THREADS =
DistributionConfig.GEMFIRE_PREFIX + "client.stats.threads.int";
/**
*
* @return All the required details for a member with given memberId
*/
public Map<String, Object> getMemberDetails(String memberId) throws OperationsException {
Map<String, Object> allDetails = new TreeMap<String, Object>();
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 (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;
}
allDetails.put(MEMBER_TYPE, memberType);
// 2. Region info
Map<String, ObjectName> existingRegionMbeans = getExistingRegionMbeansFullPaths(memberId);
allDetails.put(MEMBER_REGIONS, getAllRegionsDetails(cache, existingRegionMbeans));
existingRegionMbeans.clear();
// 3. Clients info
allDetails.put(MEMBER_CLIENTS, getClientDetails(snapshot));
boolean statSamplingEnabled = true;
// assuming will never return as per current implementation
ConfigurationParameter[] configParams = member.getConfiguration();
for (ConfigurationParameter configParam : configParams) {
if (STATISTIC_SAMPLING_ENABLED.equals(configParam.getName())) {
allDetails.put(MEMBER_STATSAMPLING_ENABLED, configParam.getValue());
statSamplingEnabled = Boolean.parseBoolean("" + configParam.getValue());
} else if (ENABLE_TIME_STATISTICS.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) {
logger.warn(String.format("Exception occurred for operation: %s for member: %s",
new Object[] {"getMemberDetails", memberId}),
e);
throw new OperationsException(e.getMessage());
} catch (Exception e) {
logger.warn(String.format("Exception occurred for operation: %s for member: %s",
new Object[] {"getMemberDetails", memberId}),
e);
throw new OperationsException(e.getMessage());
}
}
return allDetails;
}
/**
*
* @return Map of client details
*/
@SuppressWarnings("rawtypes")
private Map<String, Map<String, ?>> getClientDetails(GemFireMemberStatus snapshot) {
Map<String, Map<String, ?>> clientsInfo = new LinkedHashMap<String, Map<String, ?>>();
Set connectedClients = snapshot.getConnectedClients();
if (!connectedClients.isEmpty()) {
Map clientHealthStatsMap = snapshot.getClientHealthStats();
for (Iterator iterator = connectedClients.iterator(); iterator.hasNext();) {
Map<String, Object> clientData = new HashMap<String, Object>();
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<String, Map<String, ?>> getAllRegionsDetails(SystemMemberCacheJmxImpl cache,
Map<String, ObjectName> existingRegionMbeans) throws OperationsException {
Map<String, Map<String, ?>> regionsInfo = new TreeMap<String, Map<String, ?>>();
if (cache != null) {
try {
RegionSubRegionSnapshot regionSnapshot = cache.getRegionSnapshot();
collectAllRegionsDetails(cache, regionSnapshot, regionsInfo, existingRegionMbeans);
} catch (AdminException e) {
logger.warn("Exception occurred while getting region details.", e);
throw new OperationsException(e.getMessage());
} catch (Exception e) {
logger.warn("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<String, Map<String, ?>> regionsInfo,
Map<String, ObjectName> 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<String, Object> details = new TreeMap<String, Object>();
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 = "(\\<ec\\>)?:[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 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<String, Object> getRequiredStats(SystemMemberJmx member, boolean statSamplingEnabled)
throws OperationsException {
Map<String, Object> statDetails = new TreeMap<String, Object>();
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) {
logger.info("Re-initializing statistics for: {}",
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) {
logger.warn(e.getMessage(), 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<String, ObjectName> getExistingRegionMbeansFullPaths(String memberId)
throws MalformedObjectNameException {
Map<String, ObjectName> pathsToObjName = new HashMap<String, ObjectName>();
if (memberId != null && memberId.trim().length() != 0) {
Object[] params = new Object[] {MBeanUtil.makeCompliantMBeanNameProperty(memberId)};
Set<ObjectName> 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<ObjectName> 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<ObjectName> queryObjectNames(String queryStr, Object... params)
throws MalformedObjectNameException {
Set<ObjectName> 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)
*/
@Override
public void addNotificationListener(NotificationListener listener, NotificationFilter filter,
Object handback) throws IllegalArgumentException {
forwarder.addNotificationListener(listener, filter, handback);
}
/**
* @see NotificationEmitter#removeNotificationListener(NotificationListener)
*/
@Override
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
forwarder.removeNotificationListener(listener);
}
/**
* @see NotificationEmitter#getNotificationInfo()
*/
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
return getMBeanInfo().getNotifications();
}
/**
* @see NotificationEmitter#removeNotificationListener(NotificationListener, NotificationFilter,
* Object)
*/
@Override
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.
*
*
* @since GemFire 6.5
*/
class NotificationForwarder extends NotificationBroadcasterSupport implements NotificationListener {
private static final Logger logger = LogService.getLogger();
/* sequence generator for notifications from GemFireTypesWrapper MBean */
@MakeNotStatic
private static final AtomicLong notificationSequenceNumber = new AtomicLong();
/* reference to the MBeanServer instance */
private MBeanServer mBeanServer;
/**
* Default Constructor
*
* @param mBeanServer reference to the MBeanServer instance
*/
/* default */ NotificationForwarder(MBeanServer mBeanServer) {
this.mBeanServer = mBeanServer;
}
/**
* 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)
*/
@Override
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);
}
}
logger.debug("getStatistics call completed with no exceptions.");
} catch (ReflectionException e) {
logger.info(String.format("Exception while initializing statistics for: %s",
source.toString()),
e);
} catch (MBeanException e) {
logger.info(String.format("Exception while initializing statistics for: %s",
source.toString()),
e);
} catch (InstanceNotFoundException e) {
logger.info(String.format("Exception while initializing statistics for: %s",
source.toString()),
e);
}
// register this listener for joined member's cache/region notifications
try {
registerNotificationListener(source);
} catch (OperationsException e) {
logger.info(String.format("Exception while registering notification listener for: %s",
source.toString()),
e);
}
}
// 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);
}
}