/* * 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.management.internal; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.ObjectName; import org.apache.logging.log4j.Logger; import org.apache.geode.CancelException; import org.apache.geode.GemFireException; import org.apache.geode.SystemFailure; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionExistsException; import org.apache.geode.cache.Scope; import org.apache.geode.distributed.internal.InternalDistributedSystem; import org.apache.geode.internal.cache.CachePerfStats; import org.apache.geode.internal.cache.HasCachePerfStats; import org.apache.geode.internal.cache.InternalRegionArguments; import org.apache.geode.internal.logging.LogService; import org.apache.geode.internal.logging.LoggingThreadGroup; import org.apache.geode.management.ManagementException; /** * DistributionHelper solves the following problems * * a) Handles proxy creation when Management node comes up b) Handles proxy creation when a member * joins c) Remove proxies when a member leaves or node stops being management node. d) Takes care * to create resources like hidden regions for MBean and notification federation. * * * */ public class LocalManager extends Manager { private static final Logger logger = LogService.getLogger(); /** * Management Task pushes data to the admin regions */ private ManagementTask managementTask; /** * This service will be responsible for executing ManagementTasks and periodically push data to * localMonitoringRegion */ protected ScheduledExecutorService singleThreadFederationScheduler; /** * This map holds all the components which are eligible for federation. Although filters might * prevent any of the component from getting federated. */ private Map<ObjectName, FederationComponent> federatedComponentMap; private Object lock = new Object(); private SystemManagementService service; /** * Public constructor * * @param repo management resource repo * @param system internal distributed system */ public LocalManager(ManagementResourceRepo repo, InternalDistributedSystem system, SystemManagementService service, Cache cache) { super(repo, system, cache); this.service = service; this.federatedComponentMap = new ConcurrentHashMap<ObjectName, FederationComponent>(); } /** * Managed Node side resources are created * * Management Region : its a Replicated NO_ACK region Notification Region : its a Replicated Proxy * NO_ACK region * */ private void startLocalManagement(Map<ObjectName, FederationComponent> federatedComponentMap) { synchronized (this) { if (repo.getLocalMonitoringRegion() != null) { return; } else { ThreadFactory tf = new ThreadFactory() { public Thread newThread(final Runnable command) { final Runnable r = new Runnable() { public void run() { command.run(); } }; final ThreadGroup group = LoggingThreadGroup.createThreadGroup( ManagementStrings.MANAGEMENT_TASK_THREAD_GROUP.toLocalizedString(), logger); Thread thread = new Thread(group, r, ManagementStrings.MANAGEMENT_TASK.toLocalizedString()); thread.setDaemon(true); return thread; } }; singleThreadFederationScheduler = Executors.newSingleThreadScheduledExecutor(tf); if (logger.isDebugEnabled()) { logger.debug("Creating Management Region :"); } /** * Sharing the same Internal Argument for both notification region and monitoring region **/ InternalRegionArguments internalArgs = new InternalRegionArguments(); internalArgs.setIsUsedForMetaRegion(true); // Create anonymous stats holder for Management Regions final HasCachePerfStats monitoringRegionStats = new HasCachePerfStats() { public CachePerfStats getCachePerfStats() { return new CachePerfStats(cache.getDistributedSystem(), "managementRegionStats"); } }; internalArgs.setCachePerfStatsHolder(monitoringRegionStats); AttributesFactory<String, Object> monitorRegionAttributeFactory = new AttributesFactory<String, Object>(); monitorRegionAttributeFactory.setScope(Scope.DISTRIBUTED_NO_ACK); monitorRegionAttributeFactory.setDataPolicy(DataPolicy.REPLICATE); monitorRegionAttributeFactory.setConcurrencyChecksEnabled(false); MonitoringRegionCacheListener localListener = new MonitoringRegionCacheListener(service); monitorRegionAttributeFactory.addCacheListener(localListener); RegionAttributes<String, Object> monitoringRegionAttrs = monitorRegionAttributeFactory.create(); AttributesFactory<NotificationKey, Notification> notificationRegionAttributeFactory = new AttributesFactory<NotificationKey, Notification>(); notificationRegionAttributeFactory.setScope(Scope.DISTRIBUTED_NO_ACK); notificationRegionAttributeFactory.setDataPolicy(DataPolicy.EMPTY); notificationRegionAttributeFactory.setConcurrencyChecksEnabled(false); RegionAttributes<NotificationKey, Notification> notifRegionAttrs = notificationRegionAttributeFactory.create(); String appender = MBeanJMXAdapter .getUniqueIDForMember(cache.getDistributedSystem().getDistributedMember()); boolean monitoringRegionCreated = false; boolean notifRegionCreated = false; try { repo.setLocalMonitoringRegion( cache.createVMRegion(ManagementConstants.MONITORING_REGION + "_" + appender, monitoringRegionAttrs, internalArgs)); monitoringRegionCreated = true; } catch (org.apache.geode.cache.TimeoutException e) { throw new ManagementException(e); } catch (RegionExistsException e) { throw new ManagementException(e); } catch (IOException e) { throw new ManagementException(e); } catch (ClassNotFoundException e) { throw new ManagementException(e); } try { repo.setLocalNotificationRegion( cache.createVMRegion(ManagementConstants.NOTIFICATION_REGION + "_" + appender, notifRegionAttrs, internalArgs)); notifRegionCreated = true; } catch (org.apache.geode.cache.TimeoutException e) { throw new ManagementException(e); } catch (RegionExistsException e) { throw new ManagementException(e); } catch (IOException e) { throw new ManagementException(e); } catch (ClassNotFoundException e) { throw new ManagementException(e); } finally { if (!notifRegionCreated && monitoringRegionCreated) { repo.getLocalMonitoringRegion().localDestroyRegion(); } } managementTask = new ManagementTask(federatedComponentMap); // call run to get us initialized immediately with a sync call managementTask.run(); // All local resources are created for the ManagementTask // Now Management tasks can proceed. int updateRate = cache.getDistributedSystem().getConfig().getJmxManagerUpdateRate(); singleThreadFederationScheduler.scheduleAtFixedRate(managementTask, updateRate, updateRate, TimeUnit.MILLISECONDS); if (logger.isDebugEnabled()) { logger.debug("Management Region created with Name : {}", repo.getLocalMonitoringRegion().getName()); logger.debug("Notification Region created with Name : {}", repo.getLocalNotificationRegion().getName()); } } } } public void markForFederation(ObjectName objName, FederationComponent fedComp) { federatedComponentMap.put(objName, fedComp); } public void unMarkForFederation(ObjectName objName) { synchronized (lock) { if (federatedComponentMap.get(objName) != null) { federatedComponentMap.remove(objName); } if (repo.getLocalMonitoringRegion() != null && repo.getLocalMonitoringRegion().get(objName.toString()) != null) { // To delete an entry from the region repo.getLocalMonitoringRegion().remove(objName.toString()); } } } /** * This method will shutdown various tasks running for management */ private void shutdownTasks() { // No need of pooledGIIExecutor as this node wont do GII again // so better to release resources if (this.singleThreadFederationScheduler != null) { List<Runnable> l = this.singleThreadFederationScheduler.shutdownNow(); } } /** * Clean up management artifacts */ private void cleanUpResources() { // InternalDistributedSystem.getConnectedInstance will return an instance if // there is at least one connected instance and is not in the process of // being disconnected. if (!cache.isClosed() && InternalDistributedSystem.getConnectedInstance() != null) { if (repo.getLocalMonitoringRegion() != null) { // To delete an entry from the region for (String name : repo.getLocalMonitoringRegion().keySet()) { ObjectName objName = null; try { objName = ObjectName.getInstance(name); unMarkForFederation(objName); } catch (MalformedObjectNameException e) { if (logger.isDebugEnabled()) { logger.debug("Unable to clean MBean: {} due to {}", objName, e.getMessage(), e); } } catch (NullPointerException e) { if (logger.isDebugEnabled()) { logger.debug("Unable to clean MBean: {} due to {}", objName, e.getMessage(), e); } } } repo.destroyLocalMonitoringRegion(); } if (repo.getLocalNotificationRegion() != null) { repo.destroyLocalNotifRegion(); } } } /** * For internal Use only */ public ScheduledExecutorService getFederationSheduler() { return singleThreadFederationScheduler; } /** * Internal testing hook.Not to be used from any where else. As soon as a mbean is created we can * push the data into local region which will make the data available at managing node site. */ public void runManagementTaskAdhoc() { managementTask.run(); } /** * This task is responsible for pushing data to the hidden region. It is executed in a single * thread from Executors.newSingleThreadScheduledExecutor(); Only one thread will be responsible * for pushing the data to the hidden region. * * (Note however that if this single thread terminates due to a failure during execution prior to * shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are * guaranteed to execute sequentially, and no more than one task will be active at any given time. * Unlike the otherwise equivalent <tt>newScheduledThreadPool(1)</tt> the returned executor is * guaranteed not to be reconfigurable to use additional threads. * * * */ private class ManagementTask implements Runnable { private Map<String, FederationComponent> replicaMap; public ManagementTask(Map<ObjectName, FederationComponent> federatedComponentMap) throws ManagementException { this.replicaMap = new HashMap<String, FederationComponent>(); } @Override public void run() { if (logger.isTraceEnabled()) { logger.trace("Federation started at managed node : "); } try { synchronized (lock) { replicaMap.clear(); Set<ObjectName> keySet = federatedComponentMap.keySet(); if (keySet.size() == 0) { return; } Iterator<ObjectName> it = keySet.iterator(); while (it.hasNext()) { ObjectName objectName = it.next(); FederationComponent fedCompInstance = federatedComponentMap.get(objectName); if (Thread.interrupted()) { replicaMap.clear(); return; } if (fedCompInstance != null) { boolean stateChanged = fedCompInstance.refreshObjectState(service.isManager()); if (!stopCacheOps) { String key = objectName.toString(); if (stateChanged || !repo.keyExistsInLocalMonitoringRegion(key)) { replicaMap.put(key, fedCompInstance); } } } } if (stopCacheOps) { return; } if (Thread.interrupted()) { replicaMap.clear(); return; } repo.putAllInLocalMonitoringRegion(replicaMap); } } catch (CancelException ex) { if (logger.isDebugEnabled()) logger.debug(ManagementStrings.MANAGEMENT_TASK_CANCELLED.toLocalizedString()); return; } catch (GemFireException ex) { if (!cache.isClosed() && logger.isDebugEnabled()) { logger.debug(ex.getMessage(), ex); } return; // Ignore Exception if cache is closing } catch (VirtualMachineError e) { SystemFailure.initiateFailure(e); throw e; } catch (Throwable th) { SystemFailure.checkFailure(); // Catching all run time exception and Errors to // pass to Thread group logger // why is this invoking uncaughtException directly?? Thread currentThread = Thread.currentThread(); ThreadGroup tg = currentThread.getThreadGroup(); tg.uncaughtException(currentThread, th); return; } if (logger.isTraceEnabled()) { logger.trace("Federation completed at managed node : "); } } } @Override public boolean isRunning() { return running; } @Override public void startManager() { startLocalManagement(federatedComponentMap); running = true; } @Override public void stopManager() { // Shutting down the GII executor as this node wont require // it anymore shutdownTasks(); // Clean up management Resources cleanUpResources(); running = false; } public void stopCacheOps() { stopCacheOps = true; } public void startCacheOps() { stopCacheOps = false; } public Map<ObjectName, FederationComponent> getFedComponents() { return federatedComponentMap; } }