You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/10/26 22:49:24 UTC
[58/93] incubator-geode git commit: GEODE-288: move admin package to
internal
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MemberInfoWithStatsMBean.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MemberInfoWithStatsMBean.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MemberInfoWithStatsMBean.java
new file mode 100644
index 0000000..44c9795
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MemberInfoWithStatsMBean.java
@@ -0,0 +1,1355 @@
+/*
+ * 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.internal.admin.api.jmx.impl;
+
+import static org.apache.geode.distributed.ConfigurationProperties.*;
+
+import org.apache.geode.internal.admin.api.AdminDistributedSystem;
+import org.apache.geode.internal.admin.api.AdminException;
+import org.apache.geode.internal.admin.api.CacheVm;
+import org.apache.geode.internal.admin.api.ConfigurationParameter;
+import org.apache.geode.internal.admin.api.GemFireMemberStatus;
+import org.apache.geode.internal.admin.api.RegionSubRegionSnapshot;
+import org.apache.geode.internal.admin.api.StatisticResource;
+import org.apache.geode.internal.admin.api.SystemMember;
+import org.apache.geode.internal.admin.api.SystemMemberCacheServer;
+import org.apache.geode.internal.admin.api.jmx.Agent;
+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.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.log4j.LocalizedMessage;
+import mx4j.AbstractDynamicMBean;
+import org.apache.logging.log4j.Logger;
+
+import javax.management.*;
+import java.net.InetAddress;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * 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
+ * @throws MBeanRegistrationException
+ * @throws AdminException
+ */
+ 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.");
+
+ // 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<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(
+ LocalizedMessage.create(
+ LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0, "getMembers"),
+ e);
+ throw new OperationsException(e.getMessage());
+ } catch (Exception e) {
+ logger.warn(
+ LocalizedMessage.create(
+ 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<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(LocalizedMessage.create(
+ LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0_FOR_MEMBER_1,
+ new Object[] {"getRegions", memberId}), e);
+ throw new OperationsException(e.getMessage());
+ } catch (Exception e) {
+ logger.warn(LocalizedMessage.create(
+ 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) {
+ logger.info(LocalizedMessage.create(
+ 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) {
+ logger.info(LocalizedMessage.create(
+ LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INTIALIZING_0_CONTINUING,
+ appVms[i].getId()), e);
+ }
+ }
+ }
+ } catch (AdminException e) {
+ logger.warn(LocalizedMessage
+ .create(LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INTIALIZING), e);
+ throw new OperationsException(e.getMessage());
+ } catch (Exception e) {
+ logger.warn(LocalizedMessage
+ .create(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) {
+ logger.info(LocalizedMessage.create(
+ 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";
+ /*
+ * 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 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 = 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";
+
+ /**
+ *
+ * @param memberId
+ * @return All the required details for a member with given memberId
+ * @throws OperationsException
+ */
+ 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;
+ }
+ // 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<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(LocalizedMessage.create(
+ LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_FOR_OPERATION_0_FOR_MEMBER_1,
+ new Object[] {"getMemberDetails", memberId}), e);
+ throw new OperationsException(e.getMessage());
+ } catch (Exception e) {
+ logger.warn(LocalizedMessage.create(
+ 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<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(LocalizedMessage.create(LocalizedStrings.ONE_ARG,
+ "Exception occurred while getting region details."), e);
+ throw new OperationsException(e.getMessage());
+ } catch (Exception e) {
+ logger.warn(LocalizedMessage.create(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<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(LocalizedMessage.create(
+ 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) {
+ 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)
+ */
+ 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.
+ *
+ *
+ * @since GemFire 6.5
+ */
+class NotificationForwarder extends NotificationBroadcasterSupport implements NotificationListener {
+
+ private static final Logger logger = LogService.getLogger();
+
+ /* sequence generator for notifications from GemFireTypesWrapper MBean */
+ private static 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)
+ */
+ 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(LocalizedMessage.create(
+ LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INITIALIZING_STATISICS_FOR_0,
+ source.toString()), e);
+ } catch (MBeanException e) {
+ logger.info(LocalizedMessage.create(
+ LocalizedStrings.MemberInfoWithStatsMBean_EXCEPTION_WHILE_INITIALIZING_STATISICS_FOR_0,
+ source.toString()), e);
+ } catch (InstanceNotFoundException e) {
+ logger.info(LocalizedMessage.create(
+ 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) {
+ logger.info(LocalizedMessage.create(
+ 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(LocalizedMessage.create(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(LocalizedMessage.create(LocalizedStrings.
+ * MemberInfoWithStatsMBean_EXCEPTION_WHILE_UNREGISTERING_NOTIFICATION_LISTENER_FOR_0,
+ * source.toString(), e); } catch (NullPointerException e) {
+ * logwriter.info(LocalizedMessage.create(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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryService.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryService.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryService.java
new file mode 100644
index 0000000..5664f85
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryService.java
@@ -0,0 +1,221 @@
+/*
+ * 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.internal.admin.api.jmx.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+import java.rmi.NoSuchObjectException;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.RMIServerSocketFactory;
+import java.rmi.server.UnicastRemoteObject;
+
+/**
+ * This MBean is an implementation of {@link RMIRegistryServiceMBean}.
+ *
+ */
+public class RMIRegistryService implements RMIRegistryServiceMBean {
+ /* RMI Registry host */
+ private String host;
+ /* RMI Registry port */
+ private int port;
+ /* RMI Registry */
+ private Registry registry;
+ /* RMI Server Socket Factory */
+ private RMIServerSocketFactory ssf;
+ /* Whether RMI Registry is started & running */
+ private boolean isRunning;
+
+ /**
+ * Constructor to configure RMI Registry to start using default RMI Registry port:
+ * {@link Registry#REGISTRY_PORT}
+ */
+ public RMIRegistryService() {
+ this(Registry.REGISTRY_PORT);
+ }
+
+ /**
+ * Constructor to configure RMI Registry to start using given RMI Registry port.
+ *
+ * @param port to run RMI Registry on
+ */
+ public RMIRegistryService(int port) {
+ setPort(port);
+ }
+
+ /**
+ * Constructor to configure RMI Registry to start using given RMI Registry port & host bind
+ * address.
+ *
+ * @param host to bind RMI Registry to
+ * @param port to run RMI Registry on
+ *
+ * @throws UnknownHostException if IP Address can not be resolved for the given host string while
+ * creating the RMIServerSocketFactory
+ */
+ public RMIRegistryService(String host, int port) throws UnknownHostException {
+ setPort(port);
+ setHost(host);
+ if (host != null && !host.trim().equals("")) {
+ ssf = new RMIServerSocketFactoryImpl(host);
+ }
+ }
+
+ /**
+ * Returns the host on which rmiregistry listens for incoming connections
+ *
+ * @return the host on which rmiregistry listens for incoming connections
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * Sets the host on which rmiregistry listens for incoming connections
+ *
+ * @param host the host on which rmiregistry listens for incoming connections
+ */
+ protected void setHost(String host) {
+ if (isRunning()) {
+ throw new IllegalStateException("RMIRegistryService is running, cannot change the host");
+ }
+ this.host = host;
+ }
+
+ /**
+ * Returns the port on which rmiregistry listens for incoming connections
+ *
+ * @return the port on which rmiregistry listens for incoming connections
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Sets the port on which rmiregistry listens for incoming connections
+ *
+ * @param port the port on which rmiregistry listens for incoming connections
+ */
+ protected void setPort(int port) {
+ if (isRunning()) {
+ throw new IllegalStateException("RMIRegistryService is running, cannot change the port");
+ }
+ this.port = port;
+ }
+
+ /**
+ * Starts this MBean: rmiregistry can now accept incoming calls
+ *
+ * @see #stop
+ * @see #isRunning
+ */
+ public synchronized void start() throws RemoteException {
+ if (!isRunning()) {
+ if (ssf != null) {
+ registry = LocateRegistry.createRegistry(port, null, // RMIClientSocketFactory
+ ssf); // RMIServerSocketFactory
+ } else {
+ registry = LocateRegistry.createRegistry(port);
+ }
+
+ isRunning = true;
+ }
+ }
+
+ /**
+ * Returns whether this MBean has been started and not yet stopped.
+ *
+ * @return whether this MBean has been started and not yet stopped.
+ * @see #start
+ */
+ public synchronized boolean isRunning() {
+ return isRunning;
+ }
+
+ /**
+ * Stops this MBean: rmiregistry cannot accept anymore incoming calls
+ *
+ * @see #start
+ */
+ public synchronized void stop() throws NoSuchObjectException {
+ if (isRunning()) {
+ isRunning = !UnicastRemoteObject.unexportObject(registry, true);
+ }
+ }
+
+ /**
+ * Returns an array of the names bound in the rmiregistry
+ *
+ * @return an array of the names bound in the rmiregistry
+ * @see java.rmi.registry.Registry#list()
+ */
+ public String[] list() throws RemoteException {
+ if (!isRunning()) {
+ throw new IllegalStateException("RMIRegistryService is not running");
+ }
+ return registry.list();
+ }
+
+ /**
+ * Removes the binding for the specified <code>name</code> in the rmiregistry
+ *
+ * @see java.rmi.registry.Registry#unbind(String)
+ */
+ public void unbind(String name) throws RemoteException, NotBoundException {
+ if (!isRunning()) {
+ throw new IllegalStateException("RMIRegistryService is not running");
+ }
+ registry.unbind(name);
+ }
+}
+
+
+/**
+ * Custom implementation of the {@link RMIServerSocketFactory}
+ *
+ */
+class RMIServerSocketFactoryImpl implements RMIServerSocketFactory {
+ /* IP address to use for creating ServerSocket */
+ private InetAddress bindAddress;
+
+ /**
+ * Constructs a RMIServerSocketFactory. The given rmiBindAddress is used to bind the ServerSockets
+ * created from this factory.
+ *
+ * @param rmiBindAddress String representation of the address to bind the ServerSockets to
+ *
+ * @throws UnknownHostException if IP Address can not be resolved for the given host string
+ */
+ /* default */ RMIServerSocketFactoryImpl(String rmiBindAddress) throws UnknownHostException {
+ this.bindAddress = InetAddress.getByName(rmiBindAddress);
+ }
+
+ /**
+ * Create a server socket on the specified port (port 0 indicates an anonymous port).
+ *
+ * @param port the port number
+ * @return the server socket on the specified port
+ * @exception IOException if an I/O error occurs during server socket creation
+ */
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return new ServerSocket(port, 0/* backlog - for '0' internally uses the default */,
+ bindAddress);
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryServiceMBean.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryServiceMBean.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryServiceMBean.java
new file mode 100644
index 0000000..f9ae8c5
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryServiceMBean.java
@@ -0,0 +1,80 @@
+/*
+ * 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.internal.admin.api.jmx.impl;
+
+import java.rmi.NoSuchObjectException;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+
+/**
+ * This interface is similar to mx4j.tools.naming.NamingServiceMBean. Features that differ are: 1.
+ * This MBean interface additionally provides a way to specify the host that the RMI Registry should
+ * get bound to. 2. Port property can not be changed once set.
+ *
+ */
+public interface RMIRegistryServiceMBean {
+
+ /**
+ * Returns the host on which rmiregistry listens for incoming connections
+ *
+ * @return the host on which rmiregistry listens for incoming connections
+ */
+ public String getHost();
+
+ /**
+ * Returns the port on which rmiregistry listens for incoming connections
+ *
+ * @return the port on which rmiregistry listens for incoming connections
+ */
+ public int getPort();
+
+ /**
+ * Returns whether this MBean has been started and not yet stopped.
+ *
+ * @return whether this MBean has been started and not yet stopped.
+ * @see #start
+ */
+ public boolean isRunning();
+
+ /**
+ * Starts this MBean: rmiregistry can now accept incoming calls
+ *
+ * @see #stop
+ * @see #isRunning
+ */
+ public void start() throws RemoteException;
+
+ /**
+ * Stops this MBean: rmiregistry cannot accept anymore incoming calls
+ *
+ * @see #start
+ */
+ public void stop() throws NoSuchObjectException;
+
+ /**
+ * Returns an array of the names bound in the rmiregistry
+ *
+ * @return an array of the names bound in the rmiregistry
+ * @see java.rmi.registry.Registry#list()
+ */
+ public String[] list() throws RemoteException;
+
+ /**
+ * Removes the binding for the specified <code>name</code> in the rmiregistry
+ *
+ * @see java.rmi.registry.Registry#unbind(String)
+ */
+ public void unbind(String name) throws RemoteException, NotBoundException;
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RefreshNotificationType.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RefreshNotificationType.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RefreshNotificationType.java
new file mode 100755
index 0000000..f6633f2
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RefreshNotificationType.java
@@ -0,0 +1,126 @@
+/*
+ * 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.internal.admin.api.jmx.impl;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Type-safe definition for refresh notifications.
+ *
+ * @since GemFire 3.5
+ *
+ */
+public class RefreshNotificationType implements java.io.Serializable {
+ private static final long serialVersionUID = 4376763592395613794L;
+
+ /** Notify StatisticResource to refresh statistics */
+ public static final RefreshNotificationType STATISTIC_RESOURCE_STATISTICS =
+ new RefreshNotificationType("GemFire.Timer.StatisticResource.statistics.refresh", "refresh");
+
+ /** Notify SystemMember to refresh config */
+ public static final RefreshNotificationType SYSTEM_MEMBER_CONFIG =
+ new RefreshNotificationType("GemFire.Timer.SystemMember.config.refresh", "refresh");
+
+ /** Notification type for the javax.management.Notification */
+ private final transient String type;
+
+ /** Notification msg for the javax.management.Notification */
+ private final transient String msg;
+
+ // The 4 declarations below are necessary for serialization
+ /** int used as ordinal to represent this Scope */
+ public final int ordinal = nextOrdinal++;
+
+ private static int nextOrdinal = 0;
+
+ private static final RefreshNotificationType[] VALUES =
+ {STATISTIC_RESOURCE_STATISTICS, SYSTEM_MEMBER_CONFIG};
+
+ private Object readResolve() throws java.io.ObjectStreamException {
+ return VALUES[ordinal]; // Canonicalize
+ }
+
+ /** Creates a new instance of RefreshNotificationType. */
+ private RefreshNotificationType(String type, String msg) {
+ this.type = type;
+ this.msg = msg;
+ }
+
+ /** Return the RefreshNotificationType represented by specified ordinal */
+ public static RefreshNotificationType fromOrdinal(int ordinal) {
+ return VALUES[ordinal];
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public String getMessage() {
+ return this.msg;
+ }
+
+ /**
+ * Returns a string representation for this notification type.
+ *
+ * @return the type string for this Notification
+ */
+ @Override
+ public String toString() {
+ return this.type;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param other the reference object with which to compare.
+ * @return true if this object is the same as the obj argument; false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this)
+ return true;
+ if (other == null)
+ return false;
+ if (!(other instanceof RefreshNotificationType))
+ return false;
+ final RefreshNotificationType that = (RefreshNotificationType) other;
+
+ if (!StringUtils.equals(this.type, that.type))
+ return false;
+ if (!StringUtils.equals(this.msg, that.msg))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Returns a hash code for the object. This method is supported for the benefit of hashtables such
+ * as those provided by java.util.Hashtable.
+ *
+ * @return the integer 0 if description is null; otherwise a unique integer.
+ */
+ @Override
+ public int hashCode() {
+ int result = 17;
+ final int mult = 37;
+
+ result = mult * result + (this.type == null ? 0 : this.type.hashCode());
+ result = mult * result + (this.msg == null ? 0 : this.msg.hashCode());
+
+ return result;
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertNotification.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertNotification.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertNotification.java
new file mode 100644
index 0000000..534d060
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertNotification.java
@@ -0,0 +1,150 @@
+/*
+ * 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.internal.admin.api.jmx.impl;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.Serializable;
+import java.io.IOException;
+
+import org.apache.geode.DataSerializer;
+import org.apache.geode.DataSerializable;
+import org.apache.geode.internal.DataSerializableFixedID;
+import org.apache.geode.internal.Version;
+import org.apache.geode.internal.admin.StatAlert;
+import org.apache.geode.internal.admin.StatAlertDefinition;
+
+/**
+ * Notification to be sent to clients (e.g GFMon2.0 ). It incorporates
+ *
+ * @see StatAlert raised and also Gemfire member id which raised the alert
+ *
+ *
+ * @since GemFire 5.7
+ */
+public class StatAlertNotification extends StatAlert
+ implements Serializable, DataSerializable, DataSerializableFixedID {
+ private static final long serialVersionUID = -1634729103430107871L;
+ private String memberId;
+
+ public StatAlertNotification() {}
+
+ public StatAlertNotification(StatAlert statAlert, String memberId) {
+ this.setDefinitionId(statAlert.getDefinitionId());
+ this.setValues(statAlert.getValues());
+ this.setTime(statAlert.getTime());
+ this.memberId = memberId;
+ }
+
+ public int getDSFID() {
+ return DataSerializableFixedID.STAT_ALERT_NOTIFICATION;
+ }
+
+ /**
+ * @return the memberId
+ */
+ public String getMemberId() {
+ return memberId;
+ }
+
+ /**
+ *
+ * @param id of gemfire member which raised the alert
+ */
+ public void setMemberId(String id) {
+ memberId = id;
+ }
+
+ /**
+ * @return String representation of this object
+ */
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("[");
+ for (int i = 0; i < getValues().length; i++) {
+ buf.append(getValues()[i] + ", ");
+ }
+ buf.append("]");
+ return Integer.valueOf(getDefinitionId()) + ":" + buf.toString();
+ }
+
+ /**
+ * The notification is something like this "For Member ID: <ID> [ <StatName> = <Value> .. ]"
+ *
+ * @param defn {@link StatAlertDefinition}
+ * @return String representation of this object based on {@link StatAlertDefinition}
+ */
+ public String toString(StatAlertDefinition defn) {
+ StringBuffer buf = new StringBuffer();
+ buf.append("For Member ID: ");
+ buf.append(this.memberId);
+ buf.append("\n");
+ buf.append("[ ");
+ for (int i = 0; i < getValues().length; i++) {
+ buf.append(defn.getStatisticInfo()[i].toString() + "=" + getValues()[i] + "\n");
+ }
+ buf.append("]");
+ return getTime().toString() + ":" + buf.toString();
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object != null && !(object instanceof StatAlertNotification)) {
+ return false;
+ }
+
+ StatAlertNotification other = (StatAlertNotification) object;
+
+ int defId = getDefinitionId();
+
+ if (defId != -1 && defId == other.getDefinitionId() && memberId != null
+ && memberId.equals(other.getMemberId())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return memberId.hashCode();
+ }
+
+ public void toData(DataOutput out) throws IOException {
+ // Do not modify StatAlert to allow 57 cacheservers to function with 57+ agent
+ // However, update of a new StatAlertDefn on 57 server from 57+ agent not covered with this
+ DataSerializer.writePrimitiveInt(this.getDefinitionId(), out);
+ DataSerializer.writeDate(this.getTime(), out);
+ DataSerializer.writeObjectArray(this.getValues(), out);
+
+ DataSerializer.writeString(this.memberId, out);
+ }
+
+ public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+ // Do not modify StatAlert to allow 57 cacheservers to function with 57+ agent
+ // However, update of a new StatAlertDefn on 57 server from 57+ agent not covered with this
+ this.setDefinitionId(DataSerializer.readPrimitiveInt(in));
+ this.setTime(DataSerializer.readDate(in));
+ this.setValues((Number[]) DataSerializer.readObjectArray(in));
+
+ this.memberId = DataSerializer.readString(in);
+ }
+
+ @Override
+ public Version[] getSerializationVersions() {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertsAggregator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertsAggregator.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertsAggregator.java
new file mode 100644
index 0000000..d97d61e
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertsAggregator.java
@@ -0,0 +1,111 @@
+/*
+ * 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.internal.admin.api.jmx.impl;
+
+import org.apache.geode.internal.admin.GemFireVM;
+import org.apache.geode.internal.admin.StatAlert;
+import org.apache.geode.internal.admin.StatAlertDefinition;
+
+/**
+ * This interface represents an Aggregator entity and resides in JMXAgent. Responsibilities are as
+ * follows:
+ * <ol>
+ * <li>set AlertsManager in the newly joined members
+ * <li>create/update/remove alert
+ * <li>manage refresh interval
+ * <li>process notification from members
+ * <li>Aggregate stats & make available for clients thro' JMXAgent
+ * </ol>
+ *
+ */
+public interface StatAlertsAggregator {
+
+ /**
+ * This method can be used to get an alert definition.
+ *
+ * @param alertDefinition StatAlertDefinition to retrieve
+ * @return StatAlertDefinition
+ */
+ public StatAlertDefinition getAlertDefinition(StatAlertDefinition alertDefinition);
+
+ /**
+ * This method can be used to retrieve all available stat alert definitions.
+ *
+ * @return An array of all available StatAlertDefinition objects
+ */
+ public StatAlertDefinition[] getAllStatAlertDefinitions();
+
+ /**
+ * This method can be used to update alert definition for the Stat mentioned. This method should
+ * update the collection maintained at the aggregator and should notify members for the newly
+ * added alert definitions.
+ * <p>
+ * A new alert definition will be created if matching one not found.
+ *
+ * @param alertDefinition alertDefinition to be updated
+ */
+ public void updateAlertDefinition(StatAlertDefinition alertDefinition);
+
+ /**
+ * This method can be used to remove alert definition for the Stat mentioned.
+ * <p>
+ * This method should update the collection maintained at the aggregator and should notify members
+ * for the newly added alert definitions.
+ *
+ * @param defId id of the alert definition to be removed
+ */
+ public void removeAlertDefinition(Integer defId);
+
+ /**
+ * Convenience method to check whether an alert definition is created.
+ *
+ * @param alert alert definition to check whether already created
+ * @return true if the alert definition is already created, false otherwise
+ */
+ public boolean isAlertDefinitionCreated(StatAlertDefinition alert);
+
+ /**
+ * This method can be used to set the AlertManager for the newly joined member VM.
+ *
+ * @param memberVM Member VM to set AlertsManager for
+ */
+ public void setAlertsManager(GemFireVM memberVM);
+
+ /**
+ * Returns the refresh interval for the Stats in seconds.
+ *
+ * @return refresh interval for the Stats(in seconds)
+ */
+ public int getRefreshIntervalForStatAlerts();
+
+ /**
+ * This method is used to set the refresh interval for the Stats Alerts in seconds
+ *
+ * @param refreshInterval refresh interval for the Stats(in seconds)
+ */
+ public void setRefreshIntervalForStatAlerts(int refreshInterval);
+
+ /**
+ * This method can be used to process the notifications sent by the member(s). Actual aggregation
+ * of stats can occur here. The array contains alert objects with alert def. ID & value.
+ * AlertHelper class can be used to retrieve the corresponding alert definition.
+ *
+ * @param alerts array of Alert class(contains alert def. ID & value)
+ * @param remoteVM
+ */
+ public void processNotifications(StatAlert[] alerts, GemFireVM remoteVM);
+
+ public void processSystemwideNotifications();
+}