/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.admin.jmx.internal; import org.apache.geode.DataSerializer; import org.apache.geode.SystemFailure; import org.apache.geode.admin.*; import org.apache.geode.admin.internal.AdminDistributedSystemImpl; import org.apache.geode.admin.internal.CacheServerConfigImpl; import org.apache.geode.admin.internal.DistributionLocatorImpl; import org.apache.geode.cache.persistence.PersistentID; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.distributed.internal.InternalDistributedSystem; import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.Assert; import org.apache.geode.internal.admin.Alert; import org.apache.geode.internal.admin.*; import org.apache.geode.internal.admin.remote.UpdateAlertDefinitionMessage; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.InternalLogWriter; import org.apache.geode.internal.logging.LogService; import org.apache.geode.internal.logging.log4j.LocalizedMessage; import org.apache.logging.log4j.Logger; import javax.management.*; import javax.management.modelmbean.ModelMBean; import javax.management.openmbean.*; import java.io.*; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * Provides MBean support for managing a GemFire distributed system. * <p> * TODO: refactor to implement DistributedSystem and delegate to instance of DistributedSystemImpl. * Wrap all delegate calls w/ e.printStackTrace() since the HttpAdaptor devours them (what to do w/ * template methods then?) * * @since GemFire 3.5 */ public class AdminDistributedSystemJmxImpl extends AdminDistributedSystemImpl implements ManagedResource, DistributedSystemConfig, StatAlertsAggregator { private static final Logger logger = LogService.getLogger(); private Properties mailProps; // The file name where the StatAlertDefinitions would be serialized private String statAlertDefnSerFile = System.getProperty("user.dir"); /** * Simple counter incrementing on each notification. This this currently resets at every restart * of Agent */ private final AtomicInteger notificationSequenceNumber = new AtomicInteger(); /** * Variable to indicate if there are no Rmi clients connected. */ private volatile boolean isRmiClientCountZero; /** * Variable to indicate if Statistics Alert definitions could be persisted across runs/sessions. */ private volatile boolean canPersistStatAlertDefs = true; /** Cache Listener to listen to Cache & Region create/destroy events */ private CacheAndRegionListenerImpl cacheRegionListener; // ------------------------------------------------------------------------- // Constructor(s) // ------------------------------------------------------------------------- /** * Constructs new DistributedSystemJmxImpl and registers an MBean to represent it. * * @param config configuration defining the JMX agent. */ public AdminDistributedSystemJmxImpl(AgentConfigImpl config) throws org.apache.geode.admin.AdminException { super(config); this.mbeanName = "GemFire:type=AdminDistributedSystem,id=" + MBeanUtil.makeCompliantMBeanNameProperty(getId()); this.objectName = MBeanUtil.createMBean(this); isEmailNotificationEnabled = config.isEmailNotificationEnabled(); if (isEmailNotificationEnabled) { initMailProps(config); } // Init file name for StatAlertDefns initStateSaveFile(config); Assert.assertTrue(this.objectName != null); cacheRegionListener = new CacheAndRegionListenerImpl(this); } private void initMailProps(AgentConfigImpl config) { mailProps = new Properties(); mailProps.put(MailManager.PROPERTY_MAIL_FROM, config.getEmailNotificationFrom()); mailProps.put(MailManager.PROPERTY_MAIL_HOST, config.getEmailNotificationHost()); mailProps.put(MailManager.PROPERTY_MAIL_TO_LIST, config.getEmailNotificationToList()); } private void initStateSaveFile(AgentConfigImpl agentConfig) { // Init file name for StatAlertDefns AgentConfigImpl impl = (AgentConfigImpl) agentConfig; File propFile = impl.getPropertyFile(); if (propFile != null) { if (propFile.isDirectory()) { statAlertDefnSerFile = propFile.getAbsolutePath(); } else if (propFile.getParentFile() != null) { statAlertDefnSerFile = propFile.getParentFile().getAbsolutePath(); } } statAlertDefnSerFile = statAlertDefnSerFile + File.separator + agentConfig.getStateSaveFile(); } // ------------------------------------------------------------------------- // MBean operations // ------------------------------------------------------------------------- /** * Registers the MBeans for monitoring the health of GemFire * * @see org.apache.geode.admin.internal.AdminDistributedSystemImpl#getGemFireHealth */ public ObjectName monitorGemFireHealth() throws MalformedObjectNameException { GemFireHealthJmxImpl health = (GemFireHealthJmxImpl) getGemFireHealth(); health.ensureMBeansAreRegistered(); return health.getObjectName(); } /** * Creates a new DistributionLocator for this system and registers an MBean for managing it. * <p> * If the Locator already exists, then this will simply register an MBean for it. * * @param host the host name or IP address of the locator * @param port the port the locator service listens on * @param workingDirectory directory path for the locator and its log * @param productDirectory directory path to the GemFire product to use */ public ObjectName createDistributionLocator(String host, int port, String workingDirectory, String productDirectory) throws MalformedObjectNameException { return createDistributionLocator(host, port, workingDirectory, productDirectory, getRemoteCommand()); } /** * Creates a new DistributionLocator for this system and registers an MBean for managing it. * <p> * If the Locator already exists, then this will simply register an MBean for it. * * @param host the host name or IP address of the locator * @param port the port the locator service listens on * @param workingDirectory directory path for the locator and its log * @param productDirectory directory path to the GemFire product to use * @param remoteCommand formatted remote command to control remotely */ public ObjectName createDistributionLocator(String host, int port, String workingDirectory, String productDirectory, String remoteCommand) throws MalformedObjectNameException { try { DistributionLocatorJmxImpl locator = (DistributionLocatorJmxImpl) addDistributionLocator(); DistributionLocatorConfig config = locator.getConfig(); config.setHost(host); config.setPort(port); config.setWorkingDirectory(workingDirectory); config.setProductDirectory(productDirectory); config.setRemoteCommand(remoteCommand); return new ObjectName(locator.getMBeanName()); } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } // ------------------------------------------------------------------------- // Template methods overriden from superclass... // ------------------------------------------------------------------------- /** Override createSystemMember by instantiating SystemMemberJmxImpl */ @Override protected SystemMember createSystemMember(ApplicationVM app) throws org.apache.geode.admin.AdminException { return new SystemMemberJmxImpl(this, app); } /** * Constructs & returns a SystemMember instance using the corresponding InternalDistributedMember * object. * * @param member InternalDistributedMember instance for which a SystemMember instance is to be * constructed. * @return constructed SystemMember instance * @throws org.apache.geode.admin.AdminException if construction of SystemMember instance fails * * @since GemFire 6.5 */ protected SystemMember createSystemMember(InternalDistributedMember member) throws org.apache.geode.admin.AdminException { return new SystemMemberJmxImpl(this, member); } @Override protected CacheServer createCacheServer(ApplicationVM member) throws AdminException { return new CacheServerJmxImpl(this, member); } @Override protected CacheServer createCacheServer(CacheServerConfigImpl config) throws AdminException { return new CacheServerJmxImpl(this, config); } /** Override createGemFireHealth by instantiating GemFireHealthJmxImpl */ @Override protected GemFireHealth createGemFireHealth(GfManagerAgent system) throws org.apache.geode.admin.AdminException { if (system == null) { throw new IllegalStateException( LocalizedStrings.AdminDistributedSystemJmxImpl_GFMANAGERAGENT_MUST_NOT_BE_NULL .toLocalizedString()); } return new GemFireHealthJmxImpl(system, this); } /** Template-method for creating a DistributionLocatorImpl instance. */ @Override protected DistributionLocatorImpl createDistributionLocatorImpl( DistributionLocatorConfig config) { return new DistributionLocatorJmxImpl(config, this); } // ------------------------------------------------------------------------- // Internal Admin listeners and JMX Notifications // ------------------------------------------------------------------------- /** Notification type for indicating system member joined */ public static final String NOTIF_MEMBER_JOINED = DistributionConfig.GEMFIRE_PREFIX + "distributedsystem.member.joined"; /** Notification type for indicating system member left */ public static final String NOTIF_MEMBER_LEFT = DistributionConfig.GEMFIRE_PREFIX + "distributedsystem.member.left"; /** Notification type for indicating system member crashed */ public static final String NOTIF_MEMBER_CRASHED = DistributionConfig.GEMFIRE_PREFIX + "distributedsystem.member.crashed"; /** Notification type for sending GemFire alerts as JMX notifications */ public static final String NOTIF_ALERT = DistributionConfig.GEMFIRE_PREFIX + "distributedsystem.alert"; /** Notification type for sending GemFire StatAlerts as JMX notifications */ public static final String NOTIF_STAT_ALERT = DistributionConfig.GEMFIRE_PREFIX + "distributedsystem.statalert"; /** Notification type for indicating abnormal disconnection from the distributed system */ public static final String NOTIF_ADMIN_SYSTEM_DISCONNECT = DistributionConfig.GEMFIRE_PREFIX + "distributedsystem.disconnect"; private static final String EML_SUBJ_PRFX_GFE_ALERT = "[GemFire Alert] "; private static final String EML_SUBJ_PRFX_GFE_NOTFY = "[GemFire Notification] "; private static final String EML_SUBJ_ITEM_GFE_DS = "Distributed System: "; // --------- org.apache.geode.internal.admin.JoinLeaveListener --------- /** * Listener callback for when a member has joined this DistributedSystem. * <p> * React by creating an MBean for managing the SystemMember and then fire a Notification with the * internal Id of the member VM. * * @param source the distributed system that fired nodeJoined * @param joined the VM that joined * @see org.apache.geode.internal.admin.JoinLeaveListener#nodeJoined */ @Override public void nodeJoined(GfManagerAgent source, GemFireVM joined) { try { super.nodeJoined(source, joined); /* * super.nodeJoined results in creation of a new SystemMember which registers itself as an * MBean, so now we try to find it... */ SystemMember member = findSystemMember(joined); if (null == member) { if (logger.isDebugEnabled()) { logger.debug( "AdminDistributedSystemJmxImpl.nodeJoined(), Could not find SystemMember for VM {}", joined); } return; } try { if (logger.isDebugEnabled()) { logger.debug("Processing node joined for: {}", member); logger.debug("RemoteGemFireVM.nodeJoined(), setting alerts manager *************"); } setAlertsManager(joined); this.modelMBean.sendNotification( new Notification(NOTIF_MEMBER_JOINED, ((ManagedResource) member).getObjectName(), // Pass // the // ObjName // of // the // Source // Member notificationSequenceNumber.addAndGet(1), joined.getId().toString())); // String mess = "Gemfire AlertNotification: System Member Joined, System member Id: " + // joined.getId().toString(); // sendEmail("Gemfire AlertNotification: Member Joined, ID: " + joined.getId().toString(), // mess); if (isEmailNotificationEnabled) { String mess = LocalizedStrings.AdminDistributedSystemJmxImpl_MEMBER_JOINED_THE_DISTRIBUTED_SYSTEM_MEMBER_ID_0 .toLocalizedString(new Object[] {joined.getId().toString()}); sendEmail(EML_SUBJ_PRFX_GFE_NOTFY + EML_SUBJ_ITEM_GFE_DS + getName() + " <" + LocalizedStrings.AdminDistributedSystemJmxImpl_MEMBER_JOINED.toLocalizedString() + ">", mess); } } catch (javax.management.MBeanException e) { logger.warn(e.getMessage(), e); } } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } /** * Listener callback for when a member has left this DistributedSystem. * <p> * React by removing the member's MBean. Also fire a Notification with the internal Id of the * member VM. * * @param source the distributed system that fired nodeLeft * @param left the VM that left * @see org.apache.geode.internal.admin.JoinLeaveListener#nodeLeft */ @Override public void nodeLeft(GfManagerAgent source, GemFireVM left) { try { SystemMember member = findSystemMember(left, false); super.nodeLeft(source, left); if (logger.isDebugEnabled()) { logger.debug("Processing node left for: {}", member); } try { this.modelMBean.sendNotification( new Notification(NOTIF_MEMBER_LEFT, ((ManagedResource) member).getObjectName(), // Pass // the // ObjName // of // the // Source // Member notificationSequenceNumber.addAndGet(1), left.getId().toString())); // String mess = "Gemfire AlertNotification: System Member Left the system, System member // Id: " + left.getId().toString(); // sendEmail("Gemfire AlertNotification: Member Left, ID: " + left.getId().toString(), // mess); if (isEmailNotificationEnabled) { String mess = LocalizedStrings.AdminDistributedSystemJmxImpl_MEMBER_LEFT_THE_DISTRIBUTED_SYSTEM_MEMBER_ID_0 .toLocalizedString(new Object[] {left.getId().toString()}); sendEmail(EML_SUBJ_PRFX_GFE_NOTFY + EML_SUBJ_ITEM_GFE_DS + getName() + " <" + LocalizedStrings.AdminDistributedSystemJmxImpl_MEMBER_LEFT.toLocalizedString() + ">", mess); } } catch (javax.management.MBeanException e) { logger.warn(e.getMessage(), e); } SystemMemberType memberType = member.getType(); if (/* member != null && */ memberType.isApplication() || memberType.isCacheVm()) { // automatically unregister the MBean... MBeanUtil.unregisterMBean((ManagedResource) member); } } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } /** * Listener callback for when a member of this DistributedSystem has crashed. * <p> * Also fires a Notification with the internal Id of the member VM. * * @param source the distributed system that fired nodeCrashed * @param crashed the VM that crashed * @see org.apache.geode.internal.admin.JoinLeaveListener#nodeCrashed */ @Override public void nodeCrashed(GfManagerAgent source, GemFireVM crashed) { try { // SystemMember application has left... SystemMember member = findSystemMember(crashed, false); super.nodeCrashed(source, crashed); if (logger.isDebugEnabled()) { logger.debug("Processing node crash for: {}", member); } try { this.modelMBean.sendNotification( new Notification(NOTIF_MEMBER_CRASHED, ((ManagedResource) member).getObjectName(), // Pass // the // ObjName // of // the // Source // Member notificationSequenceNumber.addAndGet(1), crashed.getId().toString())); // String mess = "Gemfire AlertNotification: System Member Crashed, System member Id: " + // crashed.getId().toString(); // sendEmail("Gemfire AlertNotification: Member Crashed, ID: " + crashed.getId().toString(), // mess); if (isEmailNotificationEnabled) { String mess = LocalizedStrings.AdminDistributedSystemJmxImpl_MEMBER_CRASHED_IN_THE_DISTRIBUTED_SYSTEM_MEMBER_ID_0 .toLocalizedString(new Object[] {crashed.getId().toString()}); sendEmail(EML_SUBJ_PRFX_GFE_ALERT + EML_SUBJ_ITEM_GFE_DS + getName() + " <" + LocalizedStrings.AdminDistributedSystemJmxImpl_MEMBER_CRASHED.toLocalizedString() + ">", mess); } } catch (javax.management.MBeanException e) { logger.warn(e.getMessage(), e); } SystemMemberType memberType = member.getType(); if (/* member != null && */ memberType.isApplication() || memberType.isCacheVm()) { // automatically unregister the MBean... MBeanUtil.unregisterMBean((ManagedResource) member); } } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } // ----------- org.apache.geode.internal.admin.AlertListener ----------- /** * Listener callback for when a SystemMember of this DistributedSystem has crashed. * <p> * Fires a Notification with the information from the alert. * * @param alert the gemfire alert to broadcast as a notification * @see org.apache.geode.internal.admin.AlertListener#alert */ @Override public void alert(Alert alert) { try { super.alert(alert); try { String strAlert = alert.toString(); this.modelMBean.sendNotification(new Notification(NOTIF_ALERT, this.mbeanName, notificationSequenceNumber.addAndGet(1), strAlert)); // String mess = "Gemfire AlertNotification: System Alert :" + alert.toString(); // sendEmail("Gemfire AlertNotification: System Alert", mess); if (isEmailNotificationEnabled) { String mess = LocalizedStrings.AdminDistributedSystemJmxImpl_SYSTEM_ALERT_FROM_DISTRIBUTED_SYSTEM_0 .toLocalizedString(strAlert); sendEmail(EML_SUBJ_PRFX_GFE_ALERT + EML_SUBJ_ITEM_GFE_DS + getName() + " <System Alert>", mess); } } catch (javax.management.MBeanException e) { logger.warn(e.getMessage(), e); } } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } @Override public void onDisconnect(InternalDistributedSystem sys) { if (logger.isDebugEnabled()) { this.logger.debug("Calling AdminDistributedSystemJmxImpl#onDisconnect"); } try { super.onDisconnect(sys); try { this.modelMBean.sendNotification(new Notification(NOTIF_ADMIN_SYSTEM_DISCONNECT, this.mbeanName, notificationSequenceNumber.addAndGet(1), null)); } catch (MBeanException e) { logger.warn(e.getMessage(), e); } } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } if (logger.isDebugEnabled()) { this.logger.debug("Completed AdminDistributedSystemJmxImpl#onDisconnect"); } } // ------------------------------------------------------------------------- // ManagedResource implementation // ------------------------------------------------------------------------- /** The name of the MBean that will manage this resource */ private String mbeanName; /** The remotable ObjectName that the MBean is registered under */ final private ObjectName objectName; /** The ModelMBean that is configured to manage this resource */ private ModelMBean modelMBean; public String getMBeanName() { return this.mbeanName; } public ModelMBean getModelMBean() { return this.modelMBean; } public void setModelMBean(ModelMBean modelMBean) { this.modelMBean = modelMBean; } public ObjectName getObjectName() { return this.objectName; } public ManagedResourceType getManagedResourceType() { return ManagedResourceType.DISTRIBUTED_SYSTEM; } // ------------------------------------------------------------------------- // Error traps added to overridden methods... // ------------------------------------------------------------------------- @Override public boolean isRunning() { try { return super.isRunning(); } catch (java.lang.RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (java.lang.Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } @Override public void start() throws AdminException { try { super.start(); } catch (java.lang.RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (java.lang.Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } @Override public void stop() throws AdminException { try { super.stop(); } catch (java.lang.RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (java.lang.Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } @Override public boolean waitToBeConnected(long timeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); try { return super.waitToBeConnected(timeout); } catch (java.lang.RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (java.lang.Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } @Override public String displayMergedLogs() { try { return super.displayMergedLogs(); } catch (java.lang.RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (java.lang.Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } public ObjectName manageDistributionLocator() throws MalformedObjectNameException { try { return new ObjectName(((ManagedResource) addDistributionLocator()).getMBeanName()); } // catch (AdminException e) { logger.warn(e.getMessage(), e); throw e; } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } public ObjectName[] manageDistributionLocators() throws MalformedObjectNameException { try { DistributionLocator[] locs = getDistributionLocators(); ObjectName[] onames = new javax.management.ObjectName[locs.length]; for (int i = 0; i < locs.length; i++) { DistributionLocatorJmxImpl loc = (DistributionLocatorJmxImpl) locs[i]; onames[i] = new ObjectName(loc.getMBeanName()); } return onames; } // catch (AdminException e) { logger.warn(e.getMessage(), e); throw e; } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } /** * @deprecated as of 5.7 use {@link #manageCacheVm} instead. */ @Deprecated public ObjectName manageCacheServer() throws AdminException, MalformedObjectNameException { return manageCacheVm(); } public ObjectName manageCacheVm() throws AdminException, MalformedObjectNameException { try { return new ObjectName(((ManagedResource) addCacheVm()).getMBeanName()); } catch (AdminException e) { logger.warn(e.getMessage(), e); throw e; } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.warn(e.getMessage(), e); throw e; } } /** * @deprecated as of 5.7 use {@link #manageCacheVms} instead. */ @Deprecated public ObjectName[] manageCacheServers() throws AdminException, MalformedObjectNameException { return manageCacheVms(); } public ObjectName[] manageCacheVms() throws AdminException, MalformedObjectNameException { try { CacheVm[] mgrs = getCacheVms(); ObjectName[] onames = new javax.management.ObjectName[mgrs.length]; for (int i = 0; i < mgrs.length; i++) { CacheServerJmxImpl mgr = (CacheServerJmxImpl) mgrs[i]; onames[i] = new ObjectName(mgr.getMBeanName()); } return onames; } catch (AdminException e) { logger.warn(e.getMessage(), e); throw e; } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } public ObjectName[] manageSystemMemberApplications() throws AdminException, MalformedObjectNameException { try { SystemMember[] apps = getSystemMemberApplications(); ObjectName[] onames = new javax.management.ObjectName[apps.length]; for (int i = 0; i < apps.length; i++) { SystemMemberJmxImpl app = (SystemMemberJmxImpl) apps[i]; onames[i] = new ObjectName(app.getMBeanName()); } return onames; } catch (AdminException e) { logger.warn(e.getMessage(), e); throw e; } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } /** * Return the ObjectName for the SystemMemberMBean representing the specified distributed member * or null if the member is not found. * * @param distributedMember the distributed member to manage * @return the ObjectName for the SystemMemberMBean */ public ObjectName manageSystemMember(DistributedMember distributedMember) throws AdminException, MalformedObjectNameException { try { SystemMember member = lookupSystemMember(distributedMember); if (member == null) return null; SystemMemberJmxImpl jmx = (SystemMemberJmxImpl) member; ObjectName oname = new ObjectName(jmx.getMBeanName()); return oname; } catch (AdminException e) { logger.warn(e.getMessage(), e); throw e; } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } @Override public void connect(InternalLogWriter logWriter) { try { // LOG: passes the AdminDistributedSystemImpl LogWriterLogger into GfManagerAgentConfig super.connect(logWriter); // Load existing StatAlert Definitions readAlertDefinitionsAsSerializedObjects(); /* Add Cache Listener to listen to Cache & Region create/destroy events */ if (logger.isDebugEnabled()) { logger.debug("Adding CacheAndRegionListener .... "); } addCacheListener(cacheRegionListener); } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } @Override public void disconnect() { try { super.disconnect(); // Save existing StatAlert Definitions saveAlertDefinitionsAsSerializedObjects(); /* Remove Cache Listener to listen to Cache & Region create/destroy events */ if (logger.isDebugEnabled()) { logger.debug("Removing CacheAndRegionListener .... "); } removeCacheListener(cacheRegionListener); } catch (RuntimeException e) { logger.warn(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, re-throw the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } public void cleanupResource() { disconnect(); } /** * @return the isRmiClientCountZero * @since GemFire 6.0 */ public boolean isRmiClientCountZero() { return isRmiClientCountZero; } /** * @param isRmiClientCountZero the isJmxClientCountZero to set * @since GemFire 6.0 */ void setRmiClientCountZero(boolean isRmiClientCountZero) { this.isRmiClientCountZero = isRmiClientCountZero; if (isRmiClientCountZero) { logger.info( LocalizedStrings.AdminDistributedSystemJmxImpl_JMX_CLIENT_COUNT_HAS_DROPPED_TO_ZERO); } } /////////////////////// Configuration /////////////////////// public String getEntityConfigXMLFile() { return this.getConfig().getEntityConfigXMLFile(); } public void setEntityConfigXMLFile(String xmlFile) { this.getConfig().setEntityConfigXMLFile(xmlFile); } public String getSystemId() { return this.getConfig().getSystemId(); } public void setSystemId(String systemId) { this.getConfig().setSystemId(systemId); } @Override public String getSystemName() { return this.getConfig().getSystemName(); } public void setSystemName(final String name) { this.getConfig().setSystemName(name); } @Override public boolean getDisableTcp() { return this.getConfig().getDisableTcp(); } public void setEnableNetworkPartitionDetection(boolean newValue) { getConfig().setEnableNetworkPartitionDetection(newValue); } public boolean getEnableNetworkPartitionDetection() { return getConfig().getEnableNetworkPartitionDetection(); } public int getMemberTimeout() { return getConfig().getMemberTimeout(); } public void setMemberTimeout(int value) { getConfig().setMemberTimeout(value); } @Override public String getMcastAddress() { return this.getConfig().getMcastAddress(); } public void setMcastAddress(String mcastAddress) { this.getConfig().setMcastAddress(mcastAddress); } @Override public int getMcastPort() { return this.getConfig().getMcastPort(); } public void setMcastPort(int mcastPort) { this.getConfig().setMcastPort(mcastPort); } public int getAckWaitThreshold() { return getConfig().getAckWaitThreshold(); } public void setAckWaitThreshold(int seconds) { getConfig().setAckWaitThreshold(seconds); } public int getAckSevereAlertThreshold() { return getConfig().getAckSevereAlertThreshold(); } public void setAckSevereAlertThreshold(int seconds) { getConfig().setAckSevereAlertThreshold(seconds); } @Override public String getLocators() { return this.getConfig().getLocators(); } @Override public void setLocators(String locators) { this.getConfig().setLocators(locators); } /* * Note that the getter & setter for membership port range are referred from the super class * AdminDistributedSystemImpl */ public String getBindAddress() { return this.getConfig().getBindAddress(); } public void setBindAddress(String bindAddress) { this.getConfig().setBindAddress(bindAddress); } public String getServerBindAddress() { return this.getConfig().getServerBindAddress(); } public void setServerBindAddress(String bindAddress) { this.getConfig().setServerBindAddress(bindAddress); } @Override public String getRemoteCommand() { return this.getConfig().getRemoteCommand(); } @Override public void setRemoteCommand(String command) { this.getConfig().setRemoteCommand(command); } public boolean isSSLEnabled() { return this.getConfig().isSSLEnabled(); } public void setSSLEnabled(boolean enabled) { this.getConfig().setSSLEnabled(enabled); } public String getSSLProtocols() { return this.getConfig().getSSLProtocols(); } public void setSSLProtocols(String protocols) { this.getConfig().setSSLProtocols(protocols); } public String getSSLCiphers() { return this.getConfig().getSSLCiphers(); } public void setSSLCiphers(String ciphers) { this.getConfig().setSSLCiphers(ciphers); } public boolean isSSLAuthenticationRequired() { return this.getConfig().isSSLAuthenticationRequired(); } public void setSSLAuthenticationRequired(boolean authRequired) { this.getConfig().setSSLAuthenticationRequired(authRequired); } public Properties getSSLProperties() { return this.getConfig().getSSLProperties(); } public void setSSLProperties(Properties sslProperties) { this.getConfig().setSSLProperties(sslProperties); } public void addSSLProperty(String key, String value) { this.getConfig().addSSLProperty(key, value); } public void removeSSLProperty(String key) { this.getConfig().removeSSLProperty(key); } public String getLogFile() { return this.getConfig().getLogFile(); } public void setLogFile(String logFile) { this.getConfig().setLogFile(logFile); } public String getLogLevel() { return this.getConfig().getLogLevel(); } public void setLogLevel(String logLevel) { this.getConfig().setLogLevel(logLevel); } public int getLogDiskSpaceLimit() { return this.getConfig().getLogDiskSpaceLimit(); } public void setLogDiskSpaceLimit(int limit) { this.getConfig().setLogDiskSpaceLimit(limit); } public int getLogFileSizeLimit() { return this.getConfig().getLogFileSizeLimit(); } public void setLogFileSizeLimit(int limit) { this.getConfig().setLogFileSizeLimit(limit); } public void setDisableTcp(boolean flag) { this.getConfig().setDisableTcp(flag); } public int getRefreshInterval() { return this.getConfig().getRefreshInterval(); } /* * The interval (in seconds) between auto-polling for updating AdminDistributedSystem constituents * including SystemMember, SystemMemberCache and StatisticResource. This applies only to the * default interval set when the resource is created. Changes to this interval will not get * propagated to existing resources but will apply to all new resources */ public void setRefreshInterval(int interval) { this.getConfig().setRefreshInterval(interval); } public CacheServerConfig[] getCacheServerConfigs() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public CacheServerConfig createCacheServerConfig() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public void removeCacheServerConfig(CacheServerConfig config) { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public CacheVmConfig[] getCacheVmConfigs() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public CacheVmConfig createCacheVmConfig() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public void removeCacheVmConfig(CacheVmConfig config) { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public DistributionLocatorConfig[] getDistributionLocatorConfigs() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public DistributionLocatorConfig createDistributionLocatorConfig() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public void removeDistributionLocatorConfig(DistributionLocatorConfig config) { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public void addListener(ConfigListener listener) { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public void removeListener(ConfigListener listener) { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public void validate() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } @Override public Object clone() throws CloneNotSupportedException { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } private static final String[] PERSISTENT_ID_FIELDS = new String[] {"host", "directory", "uuid"}; private static final String[] PERSISTENT_ID_DESCRIPTIONS = new String[] {"The host that was persisting the missing files", "The directory where the files were persisted", "The unique id for the persistent files"}; private final CompositeType PERSISTENT_ID_TYPE; private final TabularType PERSISTENT_ID_TABLE_TYPE; { try { PERSISTENT_ID_TYPE = new CompositeType(PersistentID.class.getCanonicalName(), "A single member's a set of persistent files for a region", PERSISTENT_ID_FIELDS, PERSISTENT_ID_DESCRIPTIONS, new OpenType[] {SimpleType.STRING, SimpleType.STRING, SimpleType.STRING}); PERSISTENT_ID_TABLE_TYPE = new TabularType("TABLE_" + PERSISTENT_ID_TYPE.getTypeName(), "A table of persistent member ids", PERSISTENT_ID_TYPE, PERSISTENT_ID_FIELDS); } catch (OpenDataException e) { throw new ExceptionInInitializerError(e); } } public TabularData getMissingPersistentMembersJMX() throws AdminException { try { Set<PersistentID> members = super.getMissingPersistentMembers(); TabularData results = new TabularDataSupport(PERSISTENT_ID_TABLE_TYPE); int index = 0; for (PersistentID id : members) { CompositeData idData = new CompositeDataSupport(PERSISTENT_ID_TYPE, PERSISTENT_ID_FIELDS, new Object[] {id.getHost().toString(), id.getDirectory(), id.getUUID().toString()}); results.put(idData); index++; } return results; } catch (OpenDataException e) { logger.warn("Exception occurred while getting missing persistent members.", e); throw new AdminException(e); } } public void revokePersistentMember(String host, String directory) throws AdminException, UnknownHostException { super.revokePersistentMember(InetAddress.getByName(host), directory); } public void revokePersistentMember(String uuid) throws AdminException, UnknownHostException { super.revokePersistentMember(UUID.fromString(uuid)); } /* ********************************************************************* */ /* ************** Implementing StatAlertsAggregator interface ********** */ /* ********************************************************************* */ /* Map to store admin stat alert definitions */ private final Map ALERT_DEFINITIONS = new Hashtable(); /* Refresh interval for all stat alerts managers in seconds */ private int refreshIntervalForStatAlerts = 20; /* * This map contains list of stat alerts as a value for alert def ID as a key */ private final HashMap alertsStore = new HashMap(); // TODO: yet to set the timer task // private SystemTimer systemwideAlertNotificationScheduler = new SystemTimer(); private MailManager mailManager = null; private final boolean isEmailNotificationEnabled; /** * Convenience method to retrieve admin stat alert definition. * * @param alertDefinitionId id of a stat alert definition * @return StatAlertDefinition reference to an instance of StatAlertDefinition * @since GemFire 5.7 */ private StatAlertDefinition getAlertDefinition(int alertDefinitionId) { synchronized (ALERT_DEFINITIONS) { return (StatAlertDefinition) ALERT_DEFINITIONS.get(Integer.valueOf(alertDefinitionId)); } } /* * private void setAlertDefinition(StatAlertDefinition alertDefinition) { * ALERT_DEFINITIONS.put(Integer.valueOf(alertDefinition.getId()), alertDefinition); } */ /** * This method can be used to get an alert definition. * * @param alertDefinition StatAlertDefinition to retrieve * @return StatAlertDefinition * @since GemFire 5.7 */ public StatAlertDefinition getAlertDefinition(StatAlertDefinition alertDefinition) { return getAlertDefinition(alertDefinition.getId()); } /** * This method is used to write existing StatAlertDefinitions to a file */ protected void readAlertDefinitionsAsSerializedObjects() { StatAlertDefinition[] defns = new StatAlertDefinition[0]; File serFile = null; FileInputStream foStr = null; DataInputStream ooStr = null; try { serFile = new File(statAlertDefnSerFile); if (!canWriteToFile(serFile)) {/* can not write a file */ canPersistStatAlertDefs = false; } if (!serFile.exists()) {/* file does not exist */ return; } if (logger.isDebugEnabled()) { logger.debug( "AdminDistributedSystemJmxImpl.readAlertDefinitionsAsSerializedObjects: File: {}", serFile.getPath()); } foStr = new FileInputStream(serFile); ooStr = new DataInputStream(foStr); defns = (StatAlertDefinition[]) DataSerializer.readObjectArray(ooStr); } catch (ClassNotFoundException cnfEx) { logger.error(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystem_ENCOUNTERED_A_0_WHILE_LOADING_STATALERTDEFINITIONS_1, new Object[] {cnfEx.getClass().getName(), statAlertDefnSerFile}), cnfEx); canPersistStatAlertDefs = false; } catch (IOException ex) { logger.error(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystem_ENCOUNTERED_A_0_WHILE_LOADING_STATALERTDEFINITIONS_1_LOADING_ABORTED, new Object[] {ex.getClass().getName(), statAlertDefnSerFile}), ex); canPersistStatAlertDefs = false; } finally { if (foStr != null) { try { foStr.close(); } catch (IOException ex) { ; } } if (ooStr != null) { try { ooStr.close(); } catch (IOException ex) { ; } } } for (int i = 0; i < defns.length; i++) { updateAlertDefinition(defns[i]); } } /** * This method is used to write existing StatAlertDefinitions to a file */ public void saveAlertDefinitionsAsSerializedObjects() { File serFile = null; FileOutputStream foStr = null; DataOutputStream ooStr = null; try { serFile = new File(statAlertDefnSerFile); if (logger.isDebugEnabled()) { logger.debug( "AdminDistributedSystemJmxImpl.saveAlertDefinitionsAsSerializedObjects: File: {}", serFile.getPath()); } if (!canWriteToFile(serFile)) { return; } foStr = new FileOutputStream(serFile); ooStr = new DataOutputStream(foStr); int numOfAlerts = 0; StatAlertDefinition[] defs = null; synchronized (ALERT_DEFINITIONS) { numOfAlerts = ALERT_DEFINITIONS.size(); defs = new StatAlertDefinition[numOfAlerts]; int i = 0; for (Iterator iter = ALERT_DEFINITIONS.keySet().iterator(); iter.hasNext();) { Integer key = (Integer) iter.next(); StatAlertDefinition readDefn = (StatAlertDefinition) ALERT_DEFINITIONS.get(key); defs[i] = readDefn; i++; } } DataSerializer.writeObjectArray(defs, ooStr); } catch (IOException ex) { logger.error(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystem_ENCOUNTERED_A_0_WHILE_SAVING_STATALERTDEFINITIONS_1, new Object[] {ex.getClass().getName(), statAlertDefnSerFile}), ex); } finally { if (foStr != null) try { foStr.close(); } catch (IOException ex) { ; } if (ooStr != null) try { ooStr.close(); } catch (IOException ex) { ; } } } /** * Checks if the given file is writable. * * @param file file to check write permissions for * @return true if file is writable, false otherwise */ private boolean canWriteToFile(File file) { boolean fileIsWritable = true; // Fix for BUG40360 : When the user does not have write permissions for // saving the stat-alert definitions, then appropriate warning message is // logged and the operation is aborted. In case the file doesn't exist, then // it attempts to create a file. If the attempt fails then it logs // appropriate warning and the operation is aborted. File.canWrite check for // a directory sometimes fails on Windows platform. Hence file creation is // necessary. if (file.exists()) { if (!file.canWrite()) { logger.warn(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystemJmxImpl_READONLY_STAT_ALERT_DEF_FILE_0, new Object[] {file})); fileIsWritable = false; } } else { try { file.createNewFile(); } catch (IOException e) { logger.warn(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystemJmxImpl_FAILED_TO_CREATE_STAT_ALERT_DEF_FILE_0, new Object[] {file}), e); fileIsWritable = false; } finally { // Since we had created this file only for testing purpose, delete the // same. if ((file.exists() && !file.delete()) && logger.isDebugEnabled()) { logger.debug("Could not delete file :'{}' which is created for checking permissions.", file.getAbsolutePath()); } } } return fileIsWritable; } /** * 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. A new alert definition will be created if matching one not found. * * @param alertDefinition alertDefinition to be updated * @since GemFire 5.7 */ public void updateAlertDefinition(StatAlertDefinition alertDefinition) { if (logger.isDebugEnabled()) { logger.debug( "Entered AdminDistributedSystemJmxImpl.updateAlertDefinition(StatAlertDefinition) *****"); } /* * What to update in the alert definition? There should be another argument or arguments in a * map. 1. Need to update the list/map of alert definitions across members. */ synchronized (ALERT_DEFINITIONS) { ALERT_DEFINITIONS.put(Integer.valueOf(alertDefinition.getId()), alertDefinition); if (logger.isDebugEnabled()) { logger.debug( "AdminDistributedSystemJmxImpl.updateAlertDefinition : alertDefinition :: id={} :: {}", alertDefinition.getId(), alertDefinition.getStringRepresentation()); } /* TODO: add code to retry on failure */ notifyMembersForAlertDefinitionChange(alertDefinition); } if (logger.isDebugEnabled()) { logger.debug( "Exiting AdminDistributedSystemJmxImpl.updateAlertDefinition(StatAlertDefinition) *****"); } } /** * This method can be used to remove 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. * * @param defId id of the alert definition to be removed * @since GemFire 5.7 */ public void removeAlertDefinition(Integer defId) { if (logger.isDebugEnabled()) { logger.debug("Entered AdminDistributedSystemJmxImpl.removeAlertDefinition id *****"); } /* * alert passed to be deleted from the list/map of alerts on JMX MBean & all Member MBeans */ synchronized (ALERT_DEFINITIONS) { StatAlertDefinition alertDefinition = (StatAlertDefinition) ALERT_DEFINITIONS.get(defId); if (alertDefinition != null) { ALERT_DEFINITIONS.remove(defId); synchronized (alertsStore) { alertsStore.remove(defId); } /* TODO: add code to retry on failure */ notifyMembersForAlertDefinitionRemoval(alertDefinition); } } if (logger.isDebugEnabled()) { logger.debug("Exiting AdminDistributedSystemJmxImpl.removeAlertDefinition() *****"); } } /** * Convenience method to check whether an alert definition is created. * * @param alertDefinition alert definition to check whether already created * @return true if the alert definition is already created, false otherwise * @since GemFire 5.7 */ public boolean isAlertDefinitionCreated(StatAlertDefinition alertDefinition) { /* * Need to maintain a map of stat against the StatAlertDefinitions. check in that map whether * the alert definition is there for the given alert * * TODO: optimize to use Map.containsKey - DONE */ synchronized (ALERT_DEFINITIONS) { return ALERT_DEFINITIONS.containsKey(Integer.valueOf(alertDefinition.getId())); } } /** * Returns the refresh interval for the Stats in seconds. * * @return refresh interval for the Stats(in seconds) * @since GemFire 5.7 */ public synchronized int getRefreshIntervalForStatAlerts() { /* * state to store the refresh interval set by the user/GFMon client */ return refreshIntervalForStatAlerts; } /** * This method is used to set the refresh interval for the Stats in seconds * * @param refreshIntervalForStatAlerts refresh interval for the Stats(in seconds) * @since GemFire 5.7 */ public synchronized void setRefreshIntervalForStatAlerts(int refreshIntervalForStatAlerts) { /* * change the state refresh interval here. */ this.refreshIntervalForStatAlerts = refreshIntervalForStatAlerts; notifyMembersForRefreshIntervalChange(this.refreshIntervalForStatAlerts * 1000l); } /** * Returns whether Statistics Alert definitions could be persisted across runs/sessions * * @return value of canPersistStatAlertDefs. * @since GemFire 6.5 */ public boolean canPersistStatAlertDefs() { return canPersistStatAlertDefs; } /** * An intermediate method to notify all members for change in refresh interval. * * @param newInterval refresh interval to be set for members(in milliseconds) */ private void notifyMembersForRefreshIntervalChange(long newInterval) { GfManagerAgent agent = getGfManagerAgent(); ApplicationVM[] VMs = agent.listApplications(); // TODO: is there any other way to get all VMs? for (int i = 0; i < VMs.length; i++) { VMs[i].setRefreshInterval(newInterval); } } /** * An intermediate method to notify all members for change in stat alert definition. * * @param alertDef stat alert definition that got changed */ private void notifyMembersForAlertDefinitionChange(StatAlertDefinition alertDef) { if (logger.isDebugEnabled()) { logger.debug( "Entered AdminDistributedSystemJmxImpl.notifyMembersForAlertDefinitionChange(StatAlertDefinition) *****"); } GfManagerAgent agent = getGfManagerAgent(); StatAlertDefinition[] alertDefs = new StatAlertDefinition[] {alertDef}; ApplicationVM[] VMs = agent.listApplications(); for (int i = 0; i < VMs.length; i++) { VMs[i].updateAlertDefinitions(alertDefs, UpdateAlertDefinitionMessage.UPDATE_ALERT_DEFINITION); } if (logger.isDebugEnabled()) { logger.debug( "Exiting AdminDistributedSystemJmxImpl.notifyMembersForAlertDefinitionChange(StatAlertDefinition) " + VMs.length + " members notified.*****"); } } /** * An intermediate method to notify all members for removal of stat alert definition. * * @param alertDef stat alert definition to be removed */ private void notifyMembersForAlertDefinitionRemoval(StatAlertDefinition alertDef) { GfManagerAgent agent = getGfManagerAgent(); StatAlertDefinition[] alertDefs = new StatAlertDefinition[] {alertDef}; ApplicationVM[] VMs = agent.listApplications(); for (int i = 0; i < VMs.length; i++) { VMs[i].updateAlertDefinitions(alertDefs, UpdateAlertDefinitionMessage.REMOVE_ALERT_DEFINITION); } } /** * This method can be used to set the AlertsManager for the newly joined member VM. * * @param memberVM Member VM to set AlertsManager for * @since GemFire 5.7 */ public synchronized void setAlertsManager(GemFireVM memberVM) { /* * 1. Who'll call this method? Who gets notified when a member joins? I think that's * AdminDistributedSystemJmxImpl.nodeCreated() 2. Is the argument GemFireVM correct? Need to * modify this interface to add method to set an interface. Need to see how it can be passed to * the RemoteGemFireVM implementation. Also need to check whetherother implementors (like * DistributedSystemHealthMonitor) of GemFireVM even need to have the AlertsManager 3. Would the * alerts manager be set by aggregator or a JMXAgent i.e. AdminDistributedSystemJmxImpl 4. * Setting the list of available alert definitions & refresh interval at this moment only would * be better/easier. 5. Need to know Alerts Manager creation/construction. Need to decide how * the object would be set & sent across to the Agent VM. */ if (logger.isDebugEnabled()) { logger.debug("Entered AdminDistributedSystemJmxImpl.setAlertsManager(GemFireVM) *****"); } // creating an array of stat alert definition objects StatAlertDefinition[] alertDefs = new StatAlertDefinition[0]; Collection alertDefsCollection = null; synchronized (ALERT_DEFINITIONS) { alertDefsCollection = ALERT_DEFINITIONS.values(); } alertDefs = (StatAlertDefinition[]) alertDefsCollection.toArray(alertDefs); memberVM.setAlertsManager(alertDefs, getRefreshIntervalForStatAlerts() * 1000l, true); if (logger.isDebugEnabled()) { logger.debug("Exiting AdminDistributedSystemJmxImpl.setAlertsManager(GemFireVM) *****"); } } /** * This method can be used to retrieve all available stat alert definitions. Returns empty array * if there are no stat alert definitions defined. * * @return An array of all available StatAlertDefinition objects * @since GemFire 5.7 */ public StatAlertDefinition[] getAllStatAlertDefinitions() { if (logger.isDebugEnabled()) { logger.debug("Entered AdminDistributedSystemJmxImpl.getAllStatAlertDefinitions() *****"); } Collection alertDefs = null; synchronized (ALERT_DEFINITIONS) { alertDefs = ALERT_DEFINITIONS.values(); } StatAlertDefinition[] alertDefsArr = null; if (alertDefs != null) { alertDefsArr = new StatAlertDefinition[alertDefs.size()]; alertDefsArr = (StatAlertDefinition[]) alertDefs.toArray(alertDefsArr); } else { alertDefsArr = new StatAlertDefinition[0]; } if (logger.isDebugEnabled()) { logger.debug("Exiting AdminDistributedSystemJmxImpl.getAllStatAlertDefinitions() *****"); } return alertDefsArr; } /** * 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 Remote Member VM that sent Stat Alerts for processing the notifications to the * clients */ public void processNotifications(StatAlert[] alerts, GemFireVM remoteVM) { if (logger.isDebugEnabled()) { logger.debug( "Entered AdminDistributedSystemJmxImpl.processNotifications(StatAlert[{}], GemFireVM) *************", alerts.length); } /* * Notifications can not be processed if the remote VM is not available. NOTE: Should this * method get the required GemFireVM information instead of its reference so that even if the * member leaves we still have the information collected earlier to process the notification? */ if (remoteVM == null) { if (logger.isDebugEnabled()) { logger.debug("Could not process stat alert notifications as given GemFireVM is null."); } return; } /* * 1. The implementation idea is yet not clear. 2. The StatAlert array would received directly * or from a request object. */ ArrayList notificationObjects = new ArrayList(); String memberId = remoteVM.getId().getId(); final boolean isSystemWide = false; StatAlert alert = null; Integer defId = null; // Number[] values = null; for (int i = 0; i < alerts.length; i++) { alert = alerts[i]; // defId = Integer.valueOf(alert.getDefinitionId()); if (getAlertDefinition(alert.getDefinitionId()) == null) continue; // Ignore any removed AlertDefns // values = alert.getValues(); // StatAlertDefinition statAlertDef = (StatAlertDefinition)ALERT_DEFINITIONS.get(defId); /* * 1. check if it's system-wide. 2. if system-wide keep, it in a collection (that should get * cleared on timeout). Process all alerts when notifications from all members are received. * Need to check if the member leaves meanwhile. * * 1. Check if function evaluation is required? 2. If it's not required, the notification * should directly be sent to clients. */ // if (statAlertDef.getFunctionId() != 0) { /* * StatAlert with alert definitions having functions assigned will get evaluated at manager * side only. * * Is combination of systemwide alerts with function valid? It should be & hence such * evaluation should be skipped on manager side. Or is it to be evaluated at manager as well * as aggragator? */ // } // TODO: is this object required? Or earlier canbe resused? StatAlertNotification alertNotification = new StatAlertNotification(alert, memberId); /* * variable isSystemWide is created only for convienience, there should be an indication for * the same in the alert definition. Currently there is no systemWide definition * * Evaluating system wide alerts: 1. It'll take time for aggregator to gather alerts from all * members. Member might keep joining & leaving in between. The member for whom the stat-alert * value was taken might have left & new ones might have joined leave until all the * calculations are complete. A disclaimer to be put that these are not exact values. 2. How * would the aggregator know that it has received alerts from all the managers? Is the concept * of system-wide alerts valid? System-wide stats might be! * */ if (!isSystemWide) { notificationObjects.add(alertNotification); continue; } HashSet accumulatedAlertValues; synchronized (alertsStore) { accumulatedAlertValues = (HashSet) alertsStore.get(defId); if (accumulatedAlertValues == null) { accumulatedAlertValues = new HashSet(); alertsStore.put(defId, accumulatedAlertValues); } } synchronized (accumulatedAlertValues) { accumulatedAlertValues.add(alertNotification); } } // for ends if (!notificationObjects.isEmpty()) { /* TODO: should ths method send & forget or be fail-safe? */ sendNotifications(notificationObjects, getObjectName()); } if (logger.isDebugEnabled()) { logger.debug( "Exiting AdminDistributedSystemJmxImpl.processNotifications(StatAlert[], GemFireVM) *************"); } } private byte[] convertNotificationsDataToByteArray(ArrayList notificationObjects) { if (logger.isDebugEnabled()) { logger.debug( "AdminDistributedSystemJmxImpl#convertNotificationsDataToByteArray: {} notifications", notificationObjects.size()); } byte[] arr = null; try { ByteArrayOutputStream byteArrStr = new ByteArrayOutputStream(); DataOutputStream str = new DataOutputStream(byteArrStr); DataSerializer.writeArrayList(notificationObjects, str); str.flush(); arr = byteArrStr.toByteArray(); } catch (IOException ex) { logger.warn(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystem_ENCOUNTERED_AN_IOEXCEPTION_0, ex.getLocalizedMessage())); } return arr; } /** * An intermediate method to send notifications to the clients. * * @param notificationObjects list of StatAlertNotification objects */ private void sendNotifications(ArrayList notificationObjects, ObjectName objName) { try { if (logger.isDebugEnabled()) { logger.debug("AdminDistributedSystemJmxImpl#sendNotifications: sending {} notifications", notificationObjects.size()); } byte[] notifBytes = convertNotificationsDataToByteArray(notificationObjects); if (notifBytes != null) { Notification notif = new Notification(NOTIF_STAT_ALERT, objName, // Pass the // StatNotifications notificationSequenceNumber.addAndGet(1), "StatAlert Notifications"); notif.setUserData(notifBytes); this.modelMBean.sendNotification(notif); } // IOException handled and logged in convertNotificationsDataToByteArray StringBuffer buf = new StringBuffer(); for (int i = 0; i < notificationObjects.size(); i++) { StatAlertNotification not = (StatAlertNotification) notificationObjects.get(i); buf.append(not.toString(getAlertDefinition(not.getDefinitionId()))); } // sendEmail("Gemfire AlertNotification on Member:" + objName, buf.toString()); if (isEmailNotificationEnabled) { String mess = LocalizedStrings.AdminDistributedSystemJmxImpl_STATISTICS_ALERT_FROM_DISTRIBUTED_SYSTEM_MEMBER_0_STATISTICS_1 .toLocalizedString(new Object[] {objName.getCanonicalName(), buf.toString()}); sendEmail(EML_SUBJ_PRFX_GFE_ALERT + EML_SUBJ_ITEM_GFE_DS + getName() + " <" + LocalizedStrings.AdminDistributedSystemJmxImpl_STATISTICS_ALERT_FOR_MEMBER .toLocalizedString() + ">", mess); } } catch (javax.management.MBeanException e) { logger.error(e.getMessage(), e); } catch (RuntimeException e) { logger.error(e.getMessage(), e); throw e; } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } catch (Error e) { // Whenever you catch Error or Throwable, you must also // catch VirtualMachineError (see above). However, there is // _still_ a possibility that you are dealing with a cascading // error condition, so you also need to check to see if the JVM // is still usable: SystemFailure.checkFailure(); logger.error(e.getMessage(), e); throw e; } } /** * Sends an email to the configured recipients using configured email server. The given message * will be the email body. NOTE: the check whether email notfication is enabled or not should done * using {@link #isEmailNotificationEnabled} before calling this method. * * @param subject subject of the email * @param message message body of the email */ private void sendEmail(String subject, String message) { if (mailManager == null) { mailManager = new MailManager(mailProps); } this.mailManager.sendEmail(subject, message); } public void processSystemwideNotifications() {} public String getId() { String myId = super.getId(); return MBeanUtil.makeCompliantMBeanNameProperty(myId); } /** * This method is used to process ClientMembership events sent for BridgeMembership by bridge * servers to all admin members. * * @param senderId id of the member that sent the ClientMembership changes for processing (could * be null) * @param clientId id of a client for which the notification was sent * @param clientHost host on which the client is/was running * @param eventType denotes whether the client Joined/Left/Crashed should be one of * ClientMembershipMessage#JOINED, ClientMembershipMessage#LEFT, * ClientMembershipMessage#CRASHED */ @Override public void processClientMembership(String senderId, String clientId, String clientHost, int eventType) { logger.info(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystemJmxImpl_PROCESSING_CLIENT_MEMBERSHIP_EVENT_0_FROM_1_FOR_2_RUNNING_ON_3, new String[] {ClientMembershipMessage.getEventTypeString(eventType), senderId, clientId, clientHost})); try { SystemMemberJmx systemMemberJmx = null; CacheVm[] cacheVms = getCacheVms(); for (CacheVm cacheVm : cacheVms) { if (cacheVm.getId().equals(senderId) && cacheVm instanceof CacheServerJmxImpl) { systemMemberJmx = (CacheServerJmxImpl) cacheVm; break; } } if (systemMemberJmx == null) { SystemMember[] appVms = getSystemMemberApplications(); for (SystemMember appVm : appVms) { if (appVm.getId().equals(senderId) && appVm instanceof SystemMemberJmxImpl) { systemMemberJmx = (SystemMemberJmxImpl) appVm; break; } } } if (systemMemberJmx != null) { systemMemberJmx.handleClientMembership(clientId, eventType); } } catch (AdminException e) { logger.error(LocalizedMessage.create( LocalizedStrings.AdminDistributedSystemJmxImpl_FAILED_TO_PROCESS_CLIENT_MEMBERSHIP_FROM_0_FOR_1, new Object[] {senderId, clientId}), e); return; } catch (RuntimeOperationsException e) { logger.warn(e.getMessage(), e);// failed to send notification } } public String getAlertLevelAsString() { return super.getAlertLevelAsString(); } public void setAlertLevelAsString(String level) { String newLevel = level != null ? level.trim() : null; try { super.setAlertLevelAsString(newLevel); } catch (IllegalArgumentException e) { throw new RuntimeMBeanException(e, e.getMessage()); } } /** * Finds the member as per details in the given event object and passes on this event for handling * the cache creation. * * @param event event object corresponding to the creation of the cache */ public void handleCacheCreateEvent(SystemMemberCacheEvent event) { String memberId = event.getMemberId(); SystemMemberJmx systemMemberJmx = (SystemMemberJmx) findCacheOrAppVmById(memberId); if (systemMemberJmx != null) { systemMemberJmx.handleCacheCreate(event); } } /** * Finds the member as per details in the given event object and passes on this event for handling * the cache closure. * * @param event event object corresponding to the closure of the cache */ public void handleCacheCloseEvent(SystemMemberCacheEvent event) { String memberId = event.getMemberId(); SystemMemberJmx systemMemberJmx = (SystemMemberJmx) findCacheOrAppVmById(memberId); if (systemMemberJmx != null) { systemMemberJmx.handleCacheClose(event); } } /** * Finds the member as per details in the given event object and passes on this event for handling * the region creation. * * @param event event object corresponding to the creation of a region */ public void handleRegionCreateEvent(SystemMemberRegionEvent event) { String memberId = event.getMemberId(); SystemMemberJmx systemMemberJmx = (SystemMemberJmx) findCacheOrAppVmById(memberId); if (systemMemberJmx != null) { systemMemberJmx.handleRegionCreate(event); } } /** * Finds the member as per details in the given event object and passes on this event for handling * the region loss. * * @param event event object corresponding to the loss of a region */ public void handleRegionLossEvent(SystemMemberRegionEvent event) { String memberId = event.getMemberId(); SystemMemberJmx systemMemberJmx = (SystemMemberJmx) findCacheOrAppVmById(memberId); if (systemMemberJmx != null) { systemMemberJmx.handleRegionLoss(event); } } @Override public void setDisableAutoReconnect(boolean newValue) { getConfig().setDisableAutoReconnect(newValue); } @Override public boolean getDisableAutoReconnect() { return getConfig().getDisableAutoReconnect(); } } class ProcessSystemwideStatAlertsNotification extends TimerTask { private StatAlertsAggregator aggregator; ProcessSystemwideStatAlertsNotification(StatAlertsAggregator aggregator) { this.aggregator = aggregator; } @Override public void run() { aggregator.processSystemwideNotifications(); } } /** * Implementation of SystemMemberCacheListener used for listening to events: * <ol> * <li>Cache Created</li> * <li>Cache Closed</li> * <li>Region Created</li> * <li>Region Loss</li> * </ol> * */ class CacheAndRegionListenerImpl implements SystemMemberCacheListener { private AdminDistributedSystemJmxImpl adminDS; /** * Csontructor to create CacheAndRegionListenerImpl * * @param adminDSResource instance of AdminDistributedSystemJmxImpl */ CacheAndRegionListenerImpl(AdminDistributedSystemJmxImpl adminDSResource) { this.adminDS = adminDSResource; } /** * See SystemMemberCacheListener#afterCacheClose(SystemMemberCacheEvent) */ public void afterCacheClose(SystemMemberCacheEvent event) { adminDS.handleCacheCloseEvent(event); } /** * See SystemMemberCacheListener#afterCacheCreate(SystemMemberCacheEvent) */ public void afterCacheCreate(SystemMemberCacheEvent event) { adminDS.handleCacheCreateEvent(event); } /** * See SystemMemberCacheListener#afterRegionCreate(SystemMemberCacheEvent) */ public void afterRegionCreate(SystemMemberRegionEvent event) { adminDS.handleRegionCreateEvent(event); } /** * See SystemMemberCacheListener#afterRegionLoss(SystemMemberCacheEvent) */ public void afterRegionLoss(SystemMemberRegionEvent event) { adminDS.handleRegionLossEvent(event); } }