| /* |
| * 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.internal; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Properties; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.SystemFailure; |
| import org.apache.geode.admin.AdminException; |
| import org.apache.geode.admin.GemFireHealth; |
| import org.apache.geode.admin.GemFireHealthConfig; |
| import org.apache.geode.admin.GemFireMemberStatus; |
| import org.apache.geode.admin.RegionSubRegionSnapshot; |
| import org.apache.geode.cache.Region; |
| import org.apache.geode.cache.RegionAttributes; |
| import org.apache.geode.distributed.internal.membership.InternalDistributedMember; |
| import org.apache.geode.internal.Assert; |
| import org.apache.geode.internal.Config; |
| import org.apache.geode.internal.admin.AdminBridgeServer; |
| import org.apache.geode.internal.admin.CacheInfo; |
| import org.apache.geode.internal.admin.DLockInfo; |
| import org.apache.geode.internal.admin.GemFireVM; |
| import org.apache.geode.internal.admin.GfManagerAgent; |
| import org.apache.geode.internal.admin.HealthListener; |
| import org.apache.geode.internal.admin.Stat; |
| import org.apache.geode.internal.admin.StatAlertDefinition; |
| import org.apache.geode.internal.admin.StatListener; |
| import org.apache.geode.internal.admin.StatResource; |
| import org.apache.geode.internal.net.SocketCreator; |
| import org.apache.geode.logging.internal.executors.LoggingThread; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| |
| /** |
| * A thread that monitors the health of the distributed system. It is kind of like a |
| * {@link org.apache.geode.distributed.internal.HealthMonitorImpl}. In order to get it to place nice |
| * with the rest of the health monitoring APIs, this class pretends that it is a |
| * <code>GemFireVM</code>. Kind of hokey, but it beats a bunch of special-case code. |
| * |
| * |
| * @since GemFire 3.5 |
| */ |
| class DistributedSystemHealthMonitor implements Runnable, GemFireVM { |
| |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** Evaluates the health of the distributed system */ |
| private DistributedSystemHealthEvaluator eval; |
| |
| /** Notified when the health of the distributed system changes */ |
| private GemFireHealthImpl healthImpl; |
| |
| /** The number of seconds between health checks */ |
| private int interval; |
| |
| /** The thread in which the monitoring occurs */ |
| private Thread thread; |
| |
| /** Has this monitor been asked to stop? */ |
| private volatile boolean stopRequested = false; |
| |
| /** The health of the distributed system the last time we checked. */ |
| private GemFireHealth.Health prevHealth = GemFireHealth.GOOD_HEALTH; |
| |
| /** |
| * The most recent <code>OKAY_HEALTH</code> diagnoses of the GemFire system |
| */ |
| private List<String> okayDiagnoses; |
| |
| /** |
| * The most recent <code>POOR_HEALTH</code> diagnoses of the GemFire system |
| */ |
| private List<String> poorDiagnoses; |
| |
| ////////////////////// Constructors ////////////////////// |
| |
| /** |
| * Creates a new <code>DistributedSystemHealthMonitor</code> that evaluates the health of the |
| * distributed system against the given thresholds once every <code>interval</code> seconds. |
| * |
| * @param eval Used to evaluate the health of the distributed system |
| * @param healthImpl Receives callbacks when the health of the distributed system changes |
| * @param interval How often the health is checked |
| */ |
| DistributedSystemHealthMonitor(DistributedSystemHealthEvaluator eval, |
| GemFireHealthImpl healthImpl, int interval) { |
| this.eval = eval; |
| this.healthImpl = healthImpl; |
| this.interval = interval; |
| this.okayDiagnoses = new ArrayList<>(); |
| this.poorDiagnoses = new ArrayList<>(); |
| |
| String name = String.format("Health monitor for %s", |
| eval.getDescription()); |
| this.thread = new LoggingThread(name, this); |
| } |
| |
| /** |
| * Does the work of monitoring the health of the distributed system. |
| */ |
| @Override |
| public void run() { |
| if (logger.isDebugEnabled()) { |
| logger.debug("Monitoring health of {} every {} seconds", this.eval.getDescription(), |
| interval); |
| } |
| |
| while (!this.stopRequested) { |
| SystemFailure.checkFailure(); |
| try { |
| Thread.sleep(interval * 1000L); |
| List status = new ArrayList(); |
| eval.evaluate(status); |
| |
| GemFireHealth.Health overallHealth = GemFireHealth.GOOD_HEALTH; |
| this.okayDiagnoses.clear(); |
| this.poorDiagnoses.clear(); |
| |
| for (Iterator iter = status.iterator(); iter.hasNext();) { |
| AbstractHealthEvaluator.HealthStatus health = |
| (AbstractHealthEvaluator.HealthStatus) iter.next(); |
| if (overallHealth == GemFireHealth.GOOD_HEALTH) { |
| if ((health.getHealthCode() != GemFireHealth.GOOD_HEALTH)) { |
| overallHealth = health.getHealthCode(); |
| } |
| |
| } else if (overallHealth == GemFireHealth.OKAY_HEALTH) { |
| if (health.getHealthCode() == GemFireHealth.POOR_HEALTH) { |
| overallHealth = GemFireHealth.POOR_HEALTH; |
| } |
| } |
| |
| GemFireHealth.Health healthCode = health.getHealthCode(); |
| if (healthCode == GemFireHealth.OKAY_HEALTH) { |
| this.okayDiagnoses.add(health.getDiagnosis()); |
| |
| } else if (healthCode == GemFireHealth.POOR_HEALTH) { |
| this.poorDiagnoses.add(health.getDiagnosis()); |
| break; |
| } |
| } |
| |
| if (overallHealth != prevHealth) { |
| healthImpl.healthChanged(this, overallHealth); |
| this.prevHealth = overallHealth; |
| } |
| |
| } catch (InterruptedException ex) { |
| // We're all done |
| // No need to reset the interrupted flag, since we're going to exit. |
| break; |
| } |
| } |
| |
| eval.close(); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Stopped checking for distributed system health"); |
| } |
| } |
| |
| /** |
| * Starts this <code>DistributedSystemHealthMonitor</code> |
| */ |
| void start() { |
| this.thread.start(); |
| } |
| |
| /** |
| * Stops this <code>DistributedSystemHealthMonitor</code> |
| */ |
| void stop() { |
| if (this.thread.isAlive()) { |
| this.stopRequested = true; |
| this.thread.interrupt(); |
| this.healthImpl.nodeLeft(null, this); |
| |
| try { |
| this.thread.join(); |
| } catch (InterruptedException ex) { |
| Thread.currentThread().interrupt(); |
| logger.warn("Interrupted while stopping health monitor thread", |
| ex); |
| } |
| } |
| } |
| |
| ////////////////////// GemFireVM Methods ////////////////////// |
| |
| @Override |
| public java.net.InetAddress getHost() { |
| try { |
| return SocketCreator.getLocalHost(); |
| |
| } catch (Exception ex) { |
| throw new org.apache.geode.InternalGemFireException( |
| "Could not get localhost?!"); |
| } |
| } |
| |
| @Override |
| public String getName() { |
| throw new UnsupportedOperationException("Not a real GemFireVM"); |
| } |
| |
| @Override |
| public java.io.File getWorkingDirectory() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public java.io.File getGeodeHomeDir() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public java.util.Date getBirthDate() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| public Properties getLicenseInfo() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public GemFireMemberStatus getSnapshot() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public RegionSubRegionSnapshot getRegionSnapshot() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public StatResource[] getStats(String statisticsTypeName) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public StatResource[] getAllStats() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public DLockInfo[] getDistributedLockInfo() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public void addStatListener(StatListener observer, StatResource observedResource, |
| Stat observedStat) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public void removeStatListener(StatListener observer) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public void addHealthListener(HealthListener observer, GemFireHealthConfig cfg) { |
| |
| } |
| |
| @Override |
| public void removeHealthListener() { |
| |
| } |
| |
| @Override |
| public void resetHealthStatus() { |
| this.prevHealth = GemFireHealth.GOOD_HEALTH; |
| } |
| |
| @Override |
| public String[] getHealthDiagnosis(GemFireHealth.Health healthCode) { |
| if (healthCode == GemFireHealth.GOOD_HEALTH) { |
| return new String[0]; |
| |
| } else if (healthCode == GemFireHealth.OKAY_HEALTH) { |
| String[] array = new String[this.okayDiagnoses.size()]; |
| this.okayDiagnoses.toArray(array); |
| return array; |
| |
| } else { |
| Assert.assertTrue(healthCode == GemFireHealth.POOR_HEALTH); |
| String[] array = new String[this.poorDiagnoses.size()]; |
| this.poorDiagnoses.toArray(array); |
| return array; |
| } |
| } |
| |
| @Override |
| public Config getConfig() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public void setConfig(Config cfg) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public GfManagerAgent getManagerAgent() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public String[] getSystemLogs() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public void setInspectionClasspath(String classpath) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public String getInspectionClasspath() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public Region[] getRootRegions() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public Region getRegion(CacheInfo c, String path) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public Region createVMRootRegion(CacheInfo c, String name, RegionAttributes attrs) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public Region createSubregion(CacheInfo c, String parentPath, String name, |
| RegionAttributes attrs) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public void setCacheInspectionMode(int mode) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public int getCacheInspectionMode() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public void takeRegionSnapshot(String regionName, int snapshotId) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public InternalDistributedMember getId() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public CacheInfo getCacheInfo() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public String getVersionInfo() { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public CacheInfo setCacheLockTimeout(CacheInfo c, int v) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public CacheInfo setCacheLockLease(CacheInfo c, int v) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public CacheInfo setCacheSearchTimeout(CacheInfo c, int v) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public AdminBridgeServer addCacheServer(CacheInfo cache) throws AdminException { |
| |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public AdminBridgeServer getBridgeInfo(CacheInfo cache, int id) throws AdminException { |
| |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public AdminBridgeServer startBridgeServer(CacheInfo cache, AdminBridgeServer bridge) |
| throws AdminException { |
| |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| @Override |
| public AdminBridgeServer stopBridgeServer(CacheInfo cache, AdminBridgeServer bridge) |
| throws AdminException { |
| |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| /** |
| * This operation is not supported for this object. Will throw UnsupportedOperationException if |
| * invoked. |
| */ |
| @Override |
| public void setAlertsManager(StatAlertDefinition[] alertDefs, long refreshInterval, |
| boolean setRemotely) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| /** |
| * This operation is not supported for this object. Will throw UnsupportedOperationException if |
| * invoked. |
| */ |
| @Override |
| public void setRefreshInterval(long refreshInterval) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| |
| /** |
| * This operation is not supported for this object. Will throw UnsupportedOperationException if |
| * invoked. |
| */ |
| @Override |
| public void updateAlertDefinitions(StatAlertDefinition[] alertDefs, int actionCode) { |
| throw new UnsupportedOperationException( |
| "Not a real GemFireVM"); |
| } |
| } |