Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id ADD64200BBB for ; Thu, 27 Oct 2016 00:11:42 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id AC836160B06; Wed, 26 Oct 2016 22:11:42 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 5F299160B04 for ; Thu, 27 Oct 2016 00:11:40 +0200 (CEST) Received: (qmail 53272 invoked by uid 500); 26 Oct 2016 22:11:39 -0000 Mailing-List: contact commits-help@geode.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.incubator.apache.org Delivered-To: mailing list commits@geode.incubator.apache.org Received: (qmail 53263 invoked by uid 99); 26 Oct 2016 22:11:39 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Oct 2016 22:11:39 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 0CCC5C0DE9 for ; Wed, 26 Oct 2016 22:11:39 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -6.219 X-Spam-Level: X-Spam-Status: No, score=-6.219 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-2.999] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id u7Q4bHrV0MtL for ; Wed, 26 Oct 2016 22:11:26 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 3338A5FC27 for ; Wed, 26 Oct 2016 22:11:24 +0000 (UTC) Received: (qmail 52486 invoked by uid 99); 26 Oct 2016 22:11:23 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Oct 2016 22:11:23 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 4F255E0ADC; Wed, 26 Oct 2016 22:11:23 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: klund@apache.org To: commits@geode.incubator.apache.org Date: Wed, 26 Oct 2016 22:11:29 -0000 Message-Id: <2c6e7a03bfe24d7dad87a1cb14bfd86b@git.apache.org> In-Reply-To: <606d6e83890e45d287c1a915237dfb50@git.apache.org> References: <606d6e83890e45d287c1a915237dfb50@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [07/42] incubator-geode git commit: GEODE-288: move admin package to internal archived-at: Wed, 26 Oct 2016 22:11:42 -0000 http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/096b622d/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..3066148 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MemberInfoWithStatsMBean.java @@ -0,0 +1,1360 @@ +/* + * 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 membersList = new ArrayList(); + if (cacheVms != null && cacheVms.length !=0) { + for (SystemMember cacheVm : cacheVms) { + membersList.add(cacheVm.getId()); + } + } + if (appVms != null && appVms.length !=0) { + for (SystemMember appVm : appVms) { + membersList.add(appVm.getId()); + } + } + members = new String[membersList.size()]; + members = membersList.toArray(members); + } + } catch (AdminException e) { + 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> getRegions(String memberId) throws OperationsException { + Map> regionsInfo = new LinkedHashMap>(); + + if (memberId != null) { + try { + SystemMemberJmx foundMember = findMember(memberId); + if (foundMember != null) { + SystemMemberCacheJmxImpl cache = (SystemMemberCacheJmxImpl) foundMember.getCache(); + if (cache != null) { + Map existingRegionMbeans = getExistingRegionMbeansFullPaths(memberId); + //TODO: this is in-efficient + //Can a region.create JMX notification be used? + regionsInfo = getAllRegionsDetails(cache, existingRegionMbeans); + existingRegionMbeans.clear(); + } + } + } catch (AdminException e) { + 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 (or deprecated ) + * element(s) in the cache-xml file or using an API Cache.addCacheServer(). + */ + +// private static final String VERSION = "gemfire.version.string"; +// private static final String MEMBER_COUNT = "gemfire.membercount.int"; +// private static final String GATEWAYHUB_COUNT = "gemfire.gatewayhubcount.int"; +// private static final String CLIENT_COUNT = "gemfire.clientcount.int"; + + private static final String MEMBER_ID = 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 getMemberDetails(String memberId) + throws OperationsException { + Map allDetails = new TreeMap(); + + if (memberId != null) { + try { + SystemMemberJmx member = findMember(memberId); + if (member != null) { + SystemMemberCacheJmxImpl cache = (SystemMemberCacheJmxImpl) member.getCache(); + GemFireMemberStatus snapshot = cache.getSnapshot(); + boolean isServer = snapshot.getIsServer(); + boolean isGatewayHub = snapshot.getIsGatewayHub(); + + //1. Member info + allDetails.put(MEMBER_ID, member.getId()); + allDetails.put(MEMBER_NAME, member.getName()); + String host = member.getHost();//from of GemFireVM.getHost + InetAddress hostAddr = member.getHostAddress(); + //possibility of null host address + if (hostAddr != null) { + host = hostAddr.getHostName(); + } + allDetails.put(MEMBER_HOST, host); + allDetails.put(MEMBER_UPTIME, snapshot.getUpTime()); + allDetails.put(IS_SERVER, isServer); + allDetails.put(IS_GATEWAY, isGatewayHub); + + String memberType = ""; + if (member instanceof CacheServerJmxImpl) { + memberType = TYPE_NAME_CACHESERVER; + } else {//Mark it of Application type if neither a gateway hub nor a server + memberType = TYPE_NAME_APPLICATION; + } +// if (isGatewayHub) { +// memberType = TYPE_NAME_GATEWAYHUB; +// } else if (isServer) { +// memberType = TYPE_NAME_CACHESERVER; +// } else {//Mark it of Application type if neither a gateway nor a server +// memberType = TYPE_NAME_APPLICATION; +// } + allDetails.put(MEMBER_TYPE, memberType); + + //2. Region info + Map existingRegionMbeans = getExistingRegionMbeansFullPaths(memberId); + allDetails.put(MEMBER_REGIONS, getAllRegionsDetails(cache, existingRegionMbeans)); + existingRegionMbeans.clear(); + + //3. Clients info + allDetails.put(MEMBER_CLIENTS, getClientDetails(snapshot)); + + 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> getClientDetails(GemFireMemberStatus snapshot) { + Map> clientsInfo = + new LinkedHashMap>(); + + Set connectedClients = snapshot.getConnectedClients(); + if (!connectedClients.isEmpty()) { + Map clientHealthStatsMap = snapshot.getClientHealthStats(); + + for (Iterator iterator = connectedClients.iterator(); iterator.hasNext();) { + Map clientData = new HashMap(); + String clientId = (String) iterator.next(); + String host = snapshot.getClientHostName(clientId); + clientData.put(CLIENT_ID, clientId); + clientData.put(CLIENT_NAME, extractClientName(clientId, host)); + clientData.put(CLIENT_HOST, host); + clientData.put(CLIENT_QUEUESIZE, snapshot.getClientQueueSize(clientId)); + + ClientHealthStats clientHealthStats = (ClientHealthStats) clientHealthStatsMap.get(clientId); + if (clientHealthStats != null) { + clientData.put(CLIENT_STATS_GETS, clientHealthStats.getNumOfGets()); + clientData.put(CLIENT_STATS_PUTS, clientHealthStats.getNumOfPuts()); + clientData.put(CLIENT_STATS_CACHEMISSES, clientHealthStats.getNumOfMisses()); + clientData.put(CLIENT_STATS_CPUUSAGE, clientHealthStats.getProcessCpuTime()); + clientData.put(CLIENT_STATS_CPUS, clientHealthStats.getCpus()); + clientData.put(CLIENT_STATS_UPDATETIME, clientHealthStats.getUpdateTime().getTime()); + clientData.put(CLIENT_STATS_THREADS, clientHealthStats.getNumOfThreads()); + } else { + clientData.put(CLIENT_STATS_GETS, Integer.valueOf(0)); + clientData.put(CLIENT_STATS_PUTS, Integer.valueOf(0)); + clientData.put(CLIENT_STATS_CACHEMISSES, Integer.valueOf(0)); + clientData.put(CLIENT_STATS_CPUUSAGE, Long.valueOf(0)); + clientData.put(CLIENT_STATS_CPUS, Integer.valueOf(0)); + clientData.put(CLIENT_STATS_UPDATETIME, Long.valueOf(0)); + clientData.put(CLIENT_STATS_THREADS, Integer.valueOf(0)); + } + + clientsInfo.put(clientId, clientData); + } + } + + return clientsInfo; + } + + /** + * Returns a Map containing information about regions. + * + * @param cache + * Reference to an MBean representing a Cache on a member + * @param existingRegionMbeans + * Map of Path against Region MBean ObjectNames + * @return Map of all region details + * @throws OperationsException + * if fails to retrieve + */ + private Map> getAllRegionsDetails( + SystemMemberCacheJmxImpl cache, + Map existingRegionMbeans) + throws OperationsException { + Map> regionsInfo = + new TreeMap>(); + + if (cache != null) { + try { + RegionSubRegionSnapshot regionSnapshot = cache.getRegionSnapshot(); + collectAllRegionsDetails(cache, regionSnapshot, regionsInfo, existingRegionMbeans); + } catch (AdminException e) { + 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> regionsInfo, + Map existingRegionMbeans) + throws AdminException, OperationsException, + MBeanException, ReflectionException { + String fullPath = regionSnapshot.getFullPath(); + if (!fullPath.equals(PLACE_HOLDER_ROOT_REGION)) { + fullPath = fullPath.substring(PLACE_HOLDER_ROOT_REGION.length()-1); + String name = regionSnapshot.getName(); + Integer entryCount = Integer.valueOf(regionSnapshot.getEntryCount()); + Map details = new TreeMap(); + details.put(REGION_NAME, name); + details.put(REGION_PATH, fullPath); + details.put(REGION_ENTRYCOUNT, entryCount); + + ObjectName regionObjectName = existingRegionMbeans.get(fullPath); + if (regionObjectName == null) {//initialize if has not yet been + regionObjectName = cache.manageRegion(fullPath); + } + + Object attribute = getAttribute(regionObjectName, "scope", NOT_AVAILABLE); + attribute = attribute != null ? attribute.toString() : attribute; + details.put(REGION_SCOPE, attribute); + + attribute = getAttribute(regionObjectName, "dataPolicy", NOT_AVAILABLE); + attribute = attribute != null ? attribute.toString() : attribute; + details.put(REGION_DATAPOLICY, attribute); + + SubscriptionAttributes interestPolicyAttr = + (SubscriptionAttributes) getAttribute(regionObjectName, + "subscriptionAttributes", null); + String interestPolicyStr = NOT_AVAILABLE; + if (interestPolicyAttr != null) { + InterestPolicy interestPolicy = interestPolicyAttr.getInterestPolicy(); + if (interestPolicy != null) { + interestPolicyStr = interestPolicy.toString(); + } + } + details.put(REGION_INTERESTPOLICY, interestPolicyStr); + + attribute = getAttribute(regionObjectName, "diskWriteAttributes", NOT_AVAILABLE); + attribute = attribute != null ? attribute.toString() : attribute; + details.put(REGION_DISKATTRS, attribute); + + regionsInfo.put(fullPath, details); + } + + Set subRegionSnapshots = regionSnapshot.getSubRegionSnapshots(); + + for (Iterator iterator = subRegionSnapshots.iterator(); iterator.hasNext();) { + RegionSubRegionSnapshot subRegion = (RegionSubRegionSnapshot) iterator.next(); + collectAllRegionsDetails(cache, subRegion, regionsInfo, existingRegionMbeans); + } + } + + /** + * Checks if the given host name string contains ':' as in IPv6 host address. + * + * @param host + * host name string + * @return true if the host string contains ':', false otherwise + */ + private static boolean isIPv6(String host) { + return host.contains(":"); + } + + /** + * Checks if the given host name is actually a String representation of an + * IPv4 address. + * + * @param host + * host name string + * @return true if given host name is a String representation of an IPv4 + * address, false otherwise + */ + private static boolean isIPv4(String host) { + String regex = "\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}"; + + return host.matches(regex); + } + + /** + * Excludes the host name from the client id and returns the String. If the + * host name can not be detected, returns an empty string. Typically, the + * client id looks like: HOST(VM_PID:VM_KIND):PORT:RANDOM_STRING:CLIENT_NAME + * + * Extracts the client name from the client id. If the client id is not in the + * expected format, returns 'N/A' + * + * @param clientId + * string identifier for a client + * @param host + * host name (FQDN) the client is running on + * @return name extracted from given client id + */ + /* + * Some examples of Client Id format: + * (1) Java Client: + * nase(21716:loner):51789:42e9a0bf:client_nase_21716 + * nase(2560:loner):2:7a84729a:Feeder + * + * (2) Native Client: + * nase(21045:loner):2:GFNative_OnNnEpyRWL:ExampleDistributedSystem + * + * (3) IPv6 Host whose name can not be resolved: + * fdf0:76cf:a0ed:9449:0:0:0:1001(21716:loner):51789:42e9a0b:client_nase_21716 + * fdf0:76cf:a0ed:9449:0:0:0:1001:51789:42e9a0b:client_nase_21716 + */ + private static String extractClientName(String clientId, String host) { + /* This isIPv6, isIPv4, extractClientName is taken from GFMon code base*/ + String hostExcludedId = ""; + if ( (isIPv6(host) || isIPv4(host))&& clientId.startsWith(host)) { + hostExcludedId = clientId.substring(host.length()); + } else { + int firstDotIndex = host.indexOf("."); + if (firstDotIndex != -1) { + String hostShortName = host.substring(0, firstDotIndex); + hostExcludedId = clientId.substring(hostShortName.length()); + } + } + + String vmPIDAndKindRegex = "\\(\\w+:\\w+\\)"; + String regex = "(\\)?:[0-9]+(:\\w+){2}+"; + String name = NOT_AVAILABLE; + String temp = hostExcludedId; + + int openIndex = temp.indexOf("("); + if (openIndex != -1) { + regex = vmPIDAndKindRegex + regex; + } + + if (temp.matches(regex)) { + String[] splitted = temp.split(":"); + name = splitted[splitted.length - 1]; + } + + return name; + } + + /** + * Returns a Map of all the statistics required for Hyperic currently. It + * relies on the attribute of the StatisticsResource Mbeans. + * + * @param member + * instance for which the stats are needed + * @return Map of all the statistics required for Hyperic currently. + * @throws OperationsException + * exceptions thrown while retrieving the attributes + */ + private Map getRequiredStats(SystemMemberJmx member, boolean statSamplingEnabled) throws OperationsException { + Map statDetails = new TreeMap(); + + try { + if (!statSamplingEnabled) { + statDetails.put(STATS_PROCESSCPUTIME, NOT_AVAILABLE_NUMBER); + statDetails.put(STATS_CPUS, NOT_AVAILABLE_NUMBER); + statDetails.put(STATS_MAXMEMORY, NOT_AVAILABLE_NUMBER); + statDetails.put(STATS_USEDMEMORY, NOT_AVAILABLE_NUMBER); + statDetails.put(STATS_GETS, NOT_AVAILABLE_NUMBER); + statDetails.put(STATS_GETTIME, NOT_AVAILABLE_NUMBER); + statDetails.put(STATS_PUTS, NOT_AVAILABLE_NUMBER); + statDetails.put(STATS_PUTTIME, NOT_AVAILABLE_NUMBER); + } else { + MBeanServer mBeanServer = agent.getMBeanServer(); + Number defaultVal = NOT_AVAILABLE_NUMBER; + Number processCpuTime = defaultVal; + Number cpus = defaultVal; + Number maxMemory = defaultVal; + Number usedMemory = defaultVal; + Number gets = defaultVal; + Number getTime = defaultVal; + Number puts = defaultVal; + Number putTime = defaultVal; + + ObjectName[] vmMemoryUsageStats = getExistingStats(member.getId(), "vmHeapMemoryStats"); + ObjectName[] vmStats = getExistingStats(member.getId(), "vmStats"); + ObjectName[] cachePerfStats = getExistingStats(member.getId(), "cachePerfStats"); + boolean needToReinit = false; + if (vmMemoryUsageStats.length == 0 || vmStats.length == 0 || cachePerfStats.length == 0) { + //if the StatisticResource MBeans are not created + needToReinit = true; + } + if(!needToReinit) { + /* + * To handle a case when the StatisticResource MBeans are created but + * not registered with RefreshTimer. If VMMemoryUsageStats are + * present, maxMemory should always be non-zero. */ + for (int i = 0; i < vmMemoryUsageStats.length; i++) {//ideally there should be a single instance + String type = (String) mBeanServer.getAttribute(vmMemoryUsageStats[i], "type"); + + if ("VMMemoryUsageStats".equals(type)) { //first instance that has Statistics Type name + maxMemory = (Number) getAttribute(vmMemoryUsageStats[i], "maxMemory", defaultVal); + break; + } + } + + needToReinit = 0 == maxMemory.longValue(); + } + + if(needToReinit) { + 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 getExistingRegionMbeansFullPaths(String memberId) throws MalformedObjectNameException { + Map pathsToObjName = new HashMap(); + + if (memberId != null && memberId.trim().length() != 0) { + Object[] params = new Object[] {MBeanUtil.makeCompliantMBeanNameProperty(memberId)}; + Set queryNames = queryObjectNames(REGION_QUERY_EXPRESSION, params); + for (ObjectName objectName : queryNames) { + pathsToObjName.put(objectName.getKeyProperty("path"), objectName); + } + } + + return pathsToObjName; + } + + /** + * Returns an array of ObjectNames existing statistics types MBeans + * + * @param memberId + * string identifier of a member + * @param name + * text id of the stats which appears in the stats ObjectName as name + * keyProperty + * @return Array of Stats MBean ObjectNames + * @throws MalformedObjectNameException + * If the query expression used is not valid + */ + private ObjectName[] getExistingStats(String memberId, String name) throws MalformedObjectNameException { + ObjectName[] statObjectNames = new ObjectName[0]; + + if (memberId != null && memberId.trim().length() != 0) { + Object[] params = new Object[] {MBeanUtil.makeCompliantMBeanNameProperty(memberId), name}; + Set queryNames = queryObjectNames(STATS_QUERY_EXPRESSION, params); + statObjectNames = new ObjectName[queryNames.size()]; + statObjectNames = queryNames.toArray(statObjectNames); + } + + return statObjectNames; + } + + /** + * Queries the MBean server with the string formed using placing the params in + * the parameterized string passed as queryStr. + * + * @param queryStr + * parameterized string + * @param params + * params to put in the string + * @return results of an ObjectName query + * @throws MalformedObjectNameException + * If the query expression ObjectName formed is not valid + */ + private Set queryObjectNames(String queryStr, Object ... params) + throws MalformedObjectNameException { + Set queried = Collections.emptySet(); + + queryStr = MessageFormat.format(queryStr, params); + ObjectName queryExp = ObjectName.getInstance(queryStr); + queried = agent.getMBeanServer().queryNames(null, queryExp); + + return queried; + } + + + /* *************************************************************************/ + /* **************** NOTIFICATION EMITTER IMPLEMENTATION ********************/ + /* *************************************************************************/ + + /** + * @see NotificationEmitter#addNotificationListener(NotificationListener, NotificationFilter, Object) + */ + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) + throws IllegalArgumentException { + forwarder.addNotificationListener(listener, filter, handback); + } + + /** + * @see NotificationEmitter#removeNotificationListener(NotificationListener) + */ + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + forwarder.removeNotificationListener(listener); + } + + /** + * @see NotificationEmitter#getNotificationInfo() + */ + public MBeanNotificationInfo[] getNotificationInfo() { + return getMBeanInfo().getNotifications(); + } + + /** + * @see NotificationEmitter#removeNotificationListener(NotificationListener, NotificationFilter, Object) + */ + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) + throws ListenerNotFoundException { + forwarder.removeNotificationListener(listener, filter, handback); + } + +} + +/** + * This class acts as a hub for the Notifications defined on + * AdminDistributedSystem & SystemMember MBeans. This acts as a listener for + * these notifications and broadcasts them as notifications from the + * {@link MemberInfoWithStatsMBean} MBean. This class extends + * {@link NotificationBroadcasterSupport} only to have the functionality to send + * notifications. + * + * + * @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/096b622d/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..3e0a385 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryService.java @@ -0,0 +1,237 @@ +/* + * 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 name 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/096b622d/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..5fbe62e --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RMIRegistryServiceMBean.java @@ -0,0 +1,84 @@ +/* + * 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 name 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/096b622d/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..d0cabfd --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/RefreshNotificationType.java @@ -0,0 +1,130 @@ +/* + * 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/096b622d/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..ceb2787 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/StatAlertNotification.java @@ -0,0 +1,161 @@ +/* + * 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: + * [ + * = + * .. + * ]" + * @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; + } +}