/* * 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 java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.ObjectName; import javax.management.modelmbean.ModelMBean; import javax.naming.OperationNotSupportedException; import org.apache.commons.modeler.ManagedBean; import org.apache.logging.log4j.Logger; import org.apache.geode.admin.AdminException; import org.apache.geode.admin.CacheServerConfig; import org.apache.geode.admin.CacheVmConfig; import org.apache.geode.admin.ConfigurationParameter; import org.apache.geode.admin.StatisticResource; import org.apache.geode.admin.SystemMemberCache; import org.apache.geode.admin.SystemMemberCacheEvent; import org.apache.geode.admin.SystemMemberRegionEvent; import org.apache.geode.admin.internal.CacheServerImpl; import org.apache.geode.admin.internal.ConfigurationParameterImpl; import org.apache.geode.internal.admin.ClientMembershipMessage; import org.apache.geode.internal.admin.GemFireVM; import org.apache.geode.internal.admin.StatResource; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.logging.LogService; /** * MBean representation of a {@link org.apache.geode.admin.CacheVm}. * * @since GemFire 4.0 */ public class CacheServerJmxImpl extends CacheServerImpl implements ManagedResource, CacheVmConfig, CacheServerConfig, SystemMemberJmx { private static final Logger logger = LogService.getLogger(); /** * Interval in seconds between refreshes. Value less than one results in no refreshing */ private int refreshInterval = 0; /** The object name of this managed resource */ private ObjectName objectName; /** The name of the MBean that will manage this resource */ private String mbeanName; /** The ModelMBean that is configured to manage this resource */ private ModelMBean modelMBean; /** Reference to the cache MBean representing a Cache in the Cache VM Member */ private SystemMemberCacheJmxImpl managedSystemMemberCache; /** collection to collect all the resources created for this member */ private Map<StatResource, StatisticResourceJmxImpl> managedStatisticsResourcesMap = new HashMap<StatResource, StatisticResourceJmxImpl>(); ////////////////////// Constructors ////////////////////// /** * Creates a new <code>CacheServerJmxImpl</code> for an existing cache server. */ CacheServerJmxImpl(AdminDistributedSystemJmxImpl system, GemFireVM vm) throws AdminException { super(system, vm); initializeMBean(); } /** * Creates a new <code>CacheServerJmxImpl</code> for an newly-created cache server. */ CacheServerJmxImpl(AdminDistributedSystemJmxImpl system, CacheVmConfig config) throws AdminException { super(system, config); initializeMBean(); } ////////////////////// Instance Methods ////////////////////// /** * Creates and registers the MBean to manage this resource */ private void initializeMBean() throws AdminException { // initialize Managed Resources for stats & cache first. // initializeManagedResources(); this.mbeanName = new StringBuffer("GemFire.CacheVm:").append("id=") .append(MBeanUtil.makeCompliantMBeanNameProperty(getId())).append(",type=") .append(MBeanUtil.makeCompliantMBeanNameProperty(getType().getName())).toString(); this.objectName = MBeanUtil.createMBean(this, addDynamicAttributes(MBeanUtil.lookupManagedBean(this))); // Refresh Interval AdminDistributedSystemJmxImpl sysJmx = (AdminDistributedSystemJmxImpl) system; if (sysJmx.getRefreshInterval() > 0) this.refreshInterval = sysJmx.getRefreshInterval(); } 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.CACHE_VM; } /** * Un-registers all the statistics & cache managed resource created for this member. After * un-registering the resource MBean instances, clears managedStatisticsResourcesMap collection & * sets managedSystemMemberCache to null. * * Creates ConfigurationParameterJmxImpl, StatisticResourceJmxImpl and SystemMemberCacheJmxImpl. * But cleans up only StatisticResourceJmxImpl and SystemMemberCacheJmxImpl which are of type * ManagedResource. */ public void cleanupResource() { synchronized (this.managedStatisticsResourcesMap) { ConfigurationParameter[] names = getConfiguration(); if (names != null) { for (int i = 0; i < names.length; i++) { ConfigurationParameter parm = names[i]; ((ConfigurationParameterImpl) parm).removeConfigurationParameterListener(this); } } this.parms.clear(); Collection<StatisticResourceJmxImpl> statisticResources = managedStatisticsResourcesMap.values(); for (StatisticResourceJmxImpl statisticResource : statisticResources) { MBeanUtil.unregisterMBean(statisticResource); } this.managedStatisticsResourcesMap.clear(); } MBeanUtil.unregisterMBean(this.managedSystemMemberCache); this.managedSystemMemberCache = null; } /////////////////////// Configuration /////////////////////// @Override public String getHost() { return this.getConfig().getHost(); } public void setHost(String host) { this.getConfig().setHost(host); } @Override public String getWorkingDirectory() { return this.getConfig().getWorkingDirectory(); } @Override public void setWorkingDirectory(String dir) { this.getConfig().setWorkingDirectory(dir); } @Override public String getProductDirectory() { return this.getConfig().getProductDirectory(); } @Override public void setProductDirectory(String dir) { this.getConfig().setProductDirectory(dir); } public String getRemoteCommand() { return this.getConfig().getRemoteCommand(); } public void setRemoteCommand(String remoteCommand) { this.getConfig().setRemoteCommand(remoteCommand); } public void validate() { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } @Override public Object clone() throws CloneNotSupportedException { throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString()); } public String getCacheXMLFile() { return this.getConfig().getCacheXMLFile(); } public void setCacheXMLFile(String cacheXMLFile) { this.getConfig().setCacheXMLFile(cacheXMLFile); } public String getClassPath() { return this.getConfig().getClassPath(); } public void setClassPath(String classpath) { this.getConfig().setClassPath(classpath); } // ------------------------------------------------------------------------- // MBean attribute accessors/mutators // ------------------------------------------------------------------------- /** * Gets the interval in seconds between config refreshes * * @return the current refresh interval in seconds */ public int getRefreshInterval() { return this.refreshInterval; } /** * Sets interval in seconds between cache config refreshes; zero or less turns off auto * refreshing. Manual refreshing has no effect on when the next scheduled refresh will occur. * * @param refreshInterval the new refresh interval in seconds */ public void _setRefreshInterval(int refreshInterval) { boolean isRegistered = MBeanUtil.isRefreshNotificationRegistered(this, RefreshNotificationType.SYSTEM_MEMBER_CONFIG); if (isRegistered && (getRefreshInterval() == refreshInterval)) return; this.refreshInterval = Helper.setAndReturnRefreshInterval(this, refreshInterval); } /** * RefreshInterval is now set only through the AdminDistributedSystem property refreshInterval. * Attempt to set refreshInterval on CacheServerJmx MBean would result in an * OperationNotSupportedException Auto-refresh is enabled on demand when a call to refreshConfig * is made * * @param refreshInterval the new refresh interval in seconds * @deprecated since 6.0 use DistributedSystemConfig.refreshInterval instead */ @Deprecated public void setRefreshInterval(int refreshInterval) throws OperationNotSupportedException { throw new OperationNotSupportedException( LocalizedStrings.MANAGED_RESOURCE_REFRESH_INTERVAL_CANT_BE_SET_DIRECTLY .toLocalizedString()); } // ------------------------------------------------------------------------- // MBean Operations // ------------------------------------------------------------------------- public void refreshConfig() throws org.apache.geode.admin.AdminException { // 1st call to refreshConfig would trigger // the auto-refresh if an interval is set if (this.refreshInterval > 0) { this._setRefreshInterval(this.refreshInterval); } super.refreshConfig(); } /** * Initializes Cache & Statistics managed resources. * * @throws AdminException if initialization of managed resources fails */ // private void initializeManagedResources() throws AdminException { // try { // manageCache(); // } catch (MalformedObjectNameException e) { // throw new // AdminException(LocalizedStrings.SystemMemberJmxImpl_EXCEPTION_OCCURRED_WHILE_INITIALIZING_0_MBEANS_FOR_1.toLocalizedString( // new Object[] {"Cache", getId()}), // e); // } catch (AdminException ae) { // if // (LocalizedStrings.SystemMemberJmx_THIS_SYSTEM_MEMBER_DOES_NOT_HAVE_A_CACHE.toLocalizedString().equals(ae.getMessage())) // { // //ignore this exception for a cache-less peer // } else { // throw ae; // } // } // try { // manageStats(); // } catch (MalformedObjectNameException e) { // throw new // AdminException(LocalizedStrings.SystemMemberJmxImpl_EXCEPTION_OCCURRED_WHILE_INITIALIZING_0_MBEANS_FOR_1.toLocalizedString( // new Object[] {"Statistics", getId()}), // e); // } // } /** * Gets this member's cache. * * @return array of ObjectName for this member's cache */ public ObjectName manageCache() throws AdminException, MalformedObjectNameException { return Helper.manageCache(this); } /** * Gets all active StatisticResources for this manager. * * @return array of ObjectName instances */ public ObjectName[] manageStats() throws AdminException, MalformedObjectNameException { return Helper.manageStats(this); } /** * Gets the active StatisticResources for this manager, based on the typeName as the key * * @return ObjectName of StatisticResourceJMX instance */ public ObjectName[] manageStat(String statisticsTypeName) throws AdminException, MalformedObjectNameException { return Helper.manageStat(this, statisticsTypeName); } // ------------------------------------------------------------------------- // JMX Notification listener // ------------------------------------------------------------------------- /** * Handles notification to refresh. Reacts by refreshing the values of this GemFireManager's * ConfigurationParamaters. Any other notification is ignored. Given notification is handled only * if there is any JMX client connected to the system. * * @param notification the JMX notification being received * @param hb handback object is unused */ public void handleNotification(Notification notification, Object hb) { AdminDistributedSystemJmxImpl systemJmx = (AdminDistributedSystemJmxImpl) this.system; if (!systemJmx.isRmiClientCountZero()) { Helper.handleNotification(this, notification, hb); } } // ------------------------------------------------------------------------- // Template methods overriden from superclass... // ------------------------------------------------------------------------- /** * Template method for creating instance of ConfigurationParameter. Overridden to return * ConfigurationParameterJmxImpl. */ @Override protected ConfigurationParameter createConfigurationParameter(String name, String description, Object value, Class type, boolean userModifiable) { return new ConfigurationParameterJmxImpl(name, description, value, type, userModifiable); } /** * Override createStatisticResource by instantiating StatisticResourceJmxImpl if it was not * created earlier otherwise returns the same instance. * * @param stat StatResource reference for which this JMX resource is to be created * @return StatisticResourceJmxImpl - JMX Implementation of StatisticResource * @throws AdminException if constructing StatisticResourceJmxImpl instance fails */ @Override protected StatisticResource createStatisticResource(StatResource stat) throws org.apache.geode.admin.AdminException { StatisticResourceJmxImpl managedStatisticResource = null; synchronized (this.managedStatisticsResourcesMap) { /* * Ensuring that a single instance of Statistic Resource is created per StatResource. */ StatisticResourceJmxImpl statisticResourceJmxImpl = managedStatisticsResourcesMap.get(stat); if (statisticResourceJmxImpl != null) { managedStatisticResource = statisticResourceJmxImpl; } else { managedStatisticResource = new StatisticResourceJmxImpl(stat, this); managedStatisticResource.getStatistics();// inits timer managedStatisticsResourcesMap.put(stat, managedStatisticResource); } } return managedStatisticResource; } /** * Override createSystemMemberCache by instantiating SystemMemberCacheJmxImpl if it was not * created earlier. * * @param vm GemFireVM reference for which this JMX resource is to be created * @return SystemMemberCacheJmxImpl - JMX Implementation of SystemMemberCache * @throws AdminException if constructing SystemMemberCacheJmxImpl instance fails */ @Override protected SystemMemberCache createSystemMemberCache(GemFireVM vm) throws org.apache.geode.admin.AdminException { if (managedSystemMemberCache == null) { managedSystemMemberCache = new SystemMemberCacheJmxImpl(vm); } return managedSystemMemberCache; } // ------------------------------------------------------------------------- // Create MBean attributes for each ConfigurationParameter // ------------------------------------------------------------------------- /** * Add MBean attribute definitions for each ConfigurationParameter. * * @param managed the mbean definition to add attributes to * @return a new instance of ManagedBean copied from <code>managed</code> but with the new * attributes added */ public ManagedBean addDynamicAttributes(ManagedBean managed) throws org.apache.geode.admin.AdminException { return Helper.addDynamicAttributes(this, managed); } /** * Cleans up Managed Resources created for the client that was connected to the server represented * by this class. * * @param clientId id of the client to be removed * @return List of ManagedResources associated with the client of given client id */ /* * This clean up is for the clients. The clients are started with a loner DM. Hence the clientId * is not supposed to contain '/' as per InternalDistributedMember.toString(). */ public List<ManagedResource> cleanupBridgeClientResources(String clientId) { List<ManagedResource> returnedResources = new ArrayList<ManagedResource>(); String compatibleId = "id_" + MBeanUtil.makeCompliantMBeanNameProperty(clientId); synchronized (this.managedStatisticsResourcesMap) { Set<Entry<StatResource, StatisticResourceJmxImpl>> entrySet = this.managedStatisticsResourcesMap.entrySet(); for (Iterator<Entry<StatResource, StatisticResourceJmxImpl>> it = entrySet.iterator(); it .hasNext();) { Entry<StatResource, StatisticResourceJmxImpl> entry = it.next(); StatisticResourceJmxImpl resource = entry.getValue(); if (resource.getMBeanName().contains(compatibleId)) { it.remove(); // remove matching entry returnedResources.add(resource); } } } return returnedResources; } /** * Implementation handles client membership changes. * * @param clientId id of the client for whom membership change happened * @param eventType membership change type; one of {@link ClientMembershipMessage#JOINED}, * {@link ClientMembershipMessage#LEFT}, {@link ClientMembershipMessage#CRASHED} */ public void handleClientMembership(String clientId, int eventType) { String notifType = null; List<ManagedResource> cleanedUp = null; if (eventType == ClientMembershipMessage.LEFT) { notifType = NOTIF_CLIENT_LEFT; cleanedUp = cleanupBridgeClientResources(clientId); } else if (eventType == ClientMembershipMessage.CRASHED) { notifType = NOTIF_CLIENT_CRASHED; cleanedUp = cleanupBridgeClientResources(clientId); } else if (eventType == ClientMembershipMessage.JOINED) { notifType = NOTIF_CLIENT_JOINED; } if (cleanedUp != null) { for (ManagedResource resource : cleanedUp) { MBeanUtil.unregisterMBean(resource); } } Helper.sendNotification(this, new Notification(notifType, this.modelMBean, Helper.getNextNotificationSequenceNumber(), clientId)); } /** * Implementation handles creation of cache by extracting the details from the given event object * and sending the {@link SystemMemberJmx#NOTIF_CACHE_CREATED} notification to the connected JMX * Clients. * * @param event event object corresponding to the creation of the cache */ public void handleCacheCreate(SystemMemberCacheEvent event) { Helper.sendNotification(this, new Notification(NOTIF_CACHE_CREATED, this.modelMBean, Helper.getNextNotificationSequenceNumber(), Helper.getCacheEventDetails(event))); } /** * Implementation handles closure of cache by extracting the details from the given event object * and sending the {@link SystemMemberJmx#NOTIF_CACHE_CLOSED} notification to the connected JMX * Clients. * * @param event event object corresponding to the closure of the cache */ public void handleCacheClose(SystemMemberCacheEvent event) { Helper.sendNotification(this, new Notification(NOTIF_CACHE_CLOSED, this.modelMBean, Helper.getNextNotificationSequenceNumber(), Helper.getCacheEventDetails(event))); } /** * Implementation handles creation of region by extracting the details from the given event object * and sending the {@link SystemMemberJmx#NOTIF_REGION_CREATED} notification to the connected JMX * Clients. Region Path is set as User Data in Notification. * * @param event event object corresponding to the creation of a region */ public void handleRegionCreate(SystemMemberRegionEvent event) { Notification notification = new Notification(NOTIF_REGION_CREATED, this.modelMBean, Helper.getNextNotificationSequenceNumber(), Helper.getRegionEventDetails(event)); notification.setUserData(event.getRegionPath()); Helper.sendNotification(this, notification); } /** * Implementation should handle loss of region by extracting the details from the given event * object and sending the {@link SystemMemberJmx#NOTIF_REGION_LOST} notification to the connected * JMX Clients. Region Path is set as User Data in Notification. Additionally, it also clears the * ManagedResources created for the region that is lost. * * @param event event object corresponding to the loss of a region */ public void handleRegionLoss(SystemMemberRegionEvent event) { SystemMemberCacheJmxImpl cacheResource = this.managedSystemMemberCache; if (cacheResource != null) { ManagedResource cleanedUp = cacheResource.cleanupRegionResources(event.getRegionPath()); if (cleanedUp != null) { MBeanUtil.unregisterMBean(cleanedUp); } } Notification notification = new Notification(NOTIF_REGION_LOST, this.modelMBean, Helper.getNextNotificationSequenceNumber(), Helper.getRegionEventDetails(event)); notification.setUserData(event.getRegionPath()); Helper.sendNotification(this, notification); } }