http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java new file mode 100644 index 0000000..d38d5cb --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthConfigImpl.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.DistributedSystemHealthConfig; + +/** + * The implementation of <code>DistributedSystemHealthConfig</code>. Note that because it never + * leaves the management VM, it is not <code>Serializable</code> and is not part of the + * {@link GemFireHealthConfigImpl} class hierarchy. + * + * + * @since GemFire 3.5 + */ +public class DistributedSystemHealthConfigImpl implements DistributedSystemHealthConfig { + + /** + * The maximum number of application members that can unexceptedly leave a healthy the distributed + * system. + */ + private long maxDepartedApplications = DEFAULT_MAX_DEPARTED_APPLICATIONS; + + ////////////////////// Constructors ////////////////////// + + /** + * Creates a new <code>DistributedSystemHealthConfigImpl</code> with the default configuration. + */ + protected DistributedSystemHealthConfigImpl() { + + } + + ///////////////////// Instance Methods ///////////////////// + + public long getMaxDepartedApplications() { + return this.maxDepartedApplications; + } + + public void setMaxDepartedApplications(long maxDepartedApplications) { + this.maxDepartedApplications = maxDepartedApplications; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java new file mode 100644 index 0000000..5087933 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthEvaluator.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.DistributedSystemHealthConfig; +import org.apache.geode.distributed.internal.DM; +import org.apache.geode.distributed.internal.DistributionConfig; +import org.apache.geode.distributed.internal.DistributionManager; +import org.apache.geode.distributed.internal.MembershipListener; +import org.apache.geode.distributed.internal.membership.InternalDistributedMember; +import org.apache.geode.internal.i18n.LocalizedStrings; + +import java.util.List; +import java.util.Set; + +/** + * Contains the logic for evaluating the health of an entire GemFire distributed system according to + * the thresholds provided in a {@link DistributedSystemHealthConfig}. + * + * <P> + * + * Note that unlike other evaluators, the <code>DistributedSystemHealthEvaluator</code> resides in + * the "administrator" VM and not in the member VMs. This is because there only needs to be one + * <code>DistributedSystemHealthEvaluator</code> per distributed system. + * + * + * @since GemFire 3.5 + */ +class DistributedSystemHealthEvaluator extends AbstractHealthEvaluator + implements MembershipListener { + + /** The config from which we get the evaluation criteria */ + private DistributedSystemHealthConfig config; + + /** + * The distribution manager with which this MembershipListener is registered + */ + private DM dm; + + /** The description of the distributed system being evaluated */ + private String description; + + /** + * The number of application members that have unexpectedly left since the previous evaluation + */ + private int crashedApplications; + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new <code>DistributedSystemHealthEvaluator</code> + */ + DistributedSystemHealthEvaluator(DistributedSystemHealthConfig config, DM dm) { + super(null, dm); + + this.config = config; + this.dm = dm; + this.dm.addMembershipListener(this); + + StringBuffer sb = new StringBuffer(); + sb.append("Distributed System "); + + String desc = null; + if (dm instanceof DistributionManager) { + desc = ((DistributionManager) dm).getDistributionConfigDescription(); + } + + if (desc != null) { + sb.append(desc); + + } else { + DistributionConfig dsc = dm.getSystem().getConfig(); + String locators = dsc.getLocators(); + if (locators == null || locators.equals("")) { + sb.append("using multicast "); + sb.append(dsc.getMcastAddress()); + sb.append(":"); + sb.append(dsc.getMcastPort()); + + } else { + sb.append("using locators "); + sb.append(locators); + } + } + + this.description = sb.toString(); + } + + //////////////////// Instance Methods //////////////////// + + @Override + protected String getDescription() { + return this.description; + } + + /** + * Checks to make sure that the number of application members of the distributed system that have + * left unexpected since the last evaluation is less than the + * {@linkplain DistributedSystemHealthConfig#getMaxDepartedApplications threshold}. If not, the + * status is "poor" health. + */ + void checkDepartedApplications(List status) { + synchronized (this) { + long threshold = this.config.getMaxDepartedApplications(); + if (this.crashedApplications > threshold) { + String s = + LocalizedStrings.DistributedSystemHealth_THE_NUMBER_OF_APPLICATIONS_THAT_HAVE_LEFT_THE_DISTRIBUTED_SYSTEM_0_EXCEEDS_THE_THRESHOLD_1 + .toLocalizedString( + new Object[] {Long.valueOf(this.crashedApplications), Long.valueOf(threshold)}); + status.add(poorHealth(s)); + } + this.crashedApplications = 0; + } + } + + @Override + protected void check(List status) { + checkDepartedApplications(status); + } + + @Override + void close() { + this.dm.removeMembershipListener(this); + } + + public void memberJoined(InternalDistributedMember id) { + + } + + /** + * Keeps track of which members depart unexpectedly + */ + public void memberDeparted(InternalDistributedMember id, boolean crashed) { + if (!crashed) + return; + synchronized (this) { + int kind = id.getVmKind(); + switch (kind) { + case DistributionManager.LOCATOR_DM_TYPE: + case DistributionManager.NORMAL_DM_TYPE: + this.crashedApplications++; + break; + default: + break; + } + } // synchronized + } + + public void quorumLost(Set<InternalDistributedMember> failures, + List<InternalDistributedMember> remaining) {} + + public void memberSuspect(InternalDistributedMember id, InternalDistributedMember whoSuspected, + String reason) {} + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java new file mode 100644 index 0000000..5a6e660 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemHealthMonitor.java @@ -0,0 +1,461 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +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.internal.admin.api.AdminException; +import org.apache.geode.internal.admin.api.GemFireHealth; +import org.apache.geode.internal.admin.api.GemFireHealthConfig; +import org.apache.geode.internal.admin.api.GemFireMemberStatus; +import org.apache.geode.internal.admin.api.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.net.SocketCreator; +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.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.internal.logging.LoggingThreadGroup; +import org.apache.geode.internal.logging.log4j.LocalizedMessage; + +/** + * 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 okayDiagnoses; + + /** + * The most recent <code>POOR_HEALTH</code> diagnoses of the GemFire system + */ + private List 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(); + + ThreadGroup group = LoggingThreadGroup.createThreadGroup( + LocalizedStrings.DistributedSystemHealthMonitor_HEALTH_MONITORS.toLocalizedString(), + logger); + String name = LocalizedStrings.DistributedSystemHealthMonitor_HEALTH_MONITOR_FOR_0 + .toLocalizedString(eval.getDescription()); + this.thread = new Thread(group, this, name); + this.thread.setDaemon(true); + } + + /** + * Does the work of monitoring the health of the distributed system. + */ + 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 * 1000); + 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( + LocalizedMessage.create( + LocalizedStrings.DistributedSystemHealthMonitor_INTERRUPTED_WHILE_STOPPING_HEALTH_MONITOR_THREAD), + ex); + } + } + } + + ////////////////////// GemFireVM Methods ////////////////////// + + public java.net.InetAddress getHost() { + try { + return SocketCreator.getLocalHost(); + + } catch (Exception ex) { + throw new org.apache.geode.InternalGemFireException( + LocalizedStrings.DistributedSystemHealthMonitor_COULD_NOT_GET_LOCALHOST + .toLocalizedString()); + } + } + + public String getName() { + // return getId().toString(); + throw new UnsupportedOperationException("Not a real GemFireVM"); + } + + public java.io.File getWorkingDirectory() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public java.io.File getGemFireDir() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public java.util.Date getBirthDate() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public Properties getLicenseInfo() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public GemFireMemberStatus getSnapshot() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public RegionSubRegionSnapshot getRegionSnapshot() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public StatResource[] getStats(String statisticsTypeName) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public StatResource[] getAllStats() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public DLockInfo[] getDistributedLockInfo() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public void addStatListener(StatListener observer, StatResource observedResource, + Stat observedStat) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public void removeStatListener(StatListener observer) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public void addHealthListener(HealthListener observer, GemFireHealthConfig cfg) { + + } + + public void removeHealthListener() { + + } + + public void resetHealthStatus() { + this.prevHealth = GemFireHealth.GOOD_HEALTH; + } + + 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; + } + } + + public Config getConfig() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public void setConfig(Config cfg) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public GfManagerAgent getManagerAgent() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public String[] getSystemLogs() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public void setInspectionClasspath(String classpath) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public String getInspectionClasspath() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public Region[] getRootRegions() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public Region getRegion(CacheInfo c, String path) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public Region createVMRootRegion(CacheInfo c, String name, RegionAttributes attrs) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public Region createSubregion(CacheInfo c, String parentPath, String name, + RegionAttributes attrs) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public void setCacheInspectionMode(int mode) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public int getCacheInspectionMode() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public void takeRegionSnapshot(String regionName, int snapshotId) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public InternalDistributedMember getId() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public CacheInfo getCacheInfo() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public String getVersionInfo() { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public CacheInfo setCacheLockTimeout(CacheInfo c, int v) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public CacheInfo setCacheLockLease(CacheInfo c, int v) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public CacheInfo setCacheSearchTimeout(CacheInfo c, int v) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public AdminBridgeServer addCacheServer(CacheInfo cache) throws AdminException { + + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public AdminBridgeServer getBridgeInfo(CacheInfo cache, int id) throws AdminException { + + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public AdminBridgeServer startBridgeServer(CacheInfo cache, AdminBridgeServer bridge) + throws AdminException { + + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + public AdminBridgeServer stopBridgeServer(CacheInfo cache, AdminBridgeServer bridge) + throws AdminException { + + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + /** + * This operation is not supported for this object. Will throw UnsupportedOperationException if + * invoked. + */ + public void setAlertsManager(StatAlertDefinition[] alertDefs, long refreshInterval, + boolean setRemotely) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + /** + * This operation is not supported for this object. Will throw UnsupportedOperationException if + * invoked. + */ + public void setRefreshInterval(long refreshInterval) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } + + /** + * This operation is not supported for this object. Will throw UnsupportedOperationException if + * invoked. + */ + public void updateAlertDefinitions(StatAlertDefinition[] alertDefs, int actionCode) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributedSystemHealthMonitor_NOT_A_REAL_GEMFIREVM.toLocalizedString()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java new file mode 100644 index 0000000..9dcd16f --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorConfigImpl.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.DistributionLocator; +import org.apache.geode.internal.admin.api.DistributionLocatorConfig; +import org.apache.geode.distributed.internal.tcpserver.*; +import org.apache.geode.internal.i18n.LocalizedStrings; + +import java.net.InetAddress; +import java.util.Properties; + +import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; + +/** + * Provides an implementation of <code>DistributionLocatorConfig</code>. + * + * @since GemFire 4.0 + */ +public class DistributionLocatorConfigImpl extends ManagedEntityConfigImpl + implements DistributionLocatorConfig { + + /** The minimum networking port (0) */ + public static final int MIN_PORT = 0; + + /** The maximum networking port (65535) */ + public static final int MAX_PORT = 65535; + + ////////////////////// Instance Fields ////////////////////// + + /** The port on which this locator listens */ + private int port; + + /** The address to bind to on a multi-homed host */ + private String bindAddress; + + /** + * The properties used to configure the DistributionLocator's DistributedSystem + */ + private Properties dsProperties; + + /** The DistributionLocator that was created with this config */ + private DistributionLocator locator; + + ////////////////////// Static Methods ////////////////////// + + /** + * Contacts a distribution locator on the given host and port and creates a + * <code>DistributionLocatorConfig</code> for it. + * + * @see TcpClient#getLocatorInfo + * + * @return <code>null</code> if the locator cannot be contacted + */ + static DistributionLocatorConfig createConfigFor(String host, int port, InetAddress bindAddress) { + TcpClient client = new TcpClient(); + String[] info = null; + if (bindAddress != null) { + info = client.getInfo(bindAddress, port); + } else { + info = client.getInfo(InetAddressUtil.toInetAddress(host), port); + } + if (info == null) { + return null; + } + + DistributionLocatorConfigImpl config = new DistributionLocatorConfigImpl(); + config.setHost(host); + config.setPort(port); + if (bindAddress != null) { + config.setBindAddress(bindAddress.getHostAddress()); + } + config.setWorkingDirectory(info[0]); + config.setProductDirectory(info[1]); + + return config; + } + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new <code>DistributionLocatorConfigImpl</code> with the default settings. + */ + public DistributionLocatorConfigImpl() { + this.port = 0; + this.bindAddress = null; + this.locator = null; + this.dsProperties = new java.util.Properties(); + this.dsProperties.setProperty(MCAST_PORT, "0"); + } + + ///////////////////// Instance Methods ///////////////////// + + /** + * Sets the locator that was configured with this <Code>DistributionLocatorConfigImpl</code>. + */ + void setLocator(DistributionLocator locator) { + this.locator = locator; + } + + @Override + protected boolean isReadOnly() { + return this.locator != null && this.locator.isRunning(); + } + + public int getPort() { + return this.port; + } + + public void setPort(int port) { + checkReadOnly(); + this.port = port; + configChanged(); + } + + public String getBindAddress() { + return this.bindAddress; + } + + public void setBindAddress(String bindAddress) { + checkReadOnly(); + this.bindAddress = bindAddress; + configChanged(); + } + + public void setDistributedSystemProperties(Properties props) { + this.dsProperties = props; + } + + public Properties getDistributedSystemProperties() { + return this.dsProperties; + } + + @Override + public void validate() { + super.validate(); + + if (port < MIN_PORT || port > MAX_PORT) { + throw new IllegalArgumentException( + LocalizedStrings.DistributionLocatorConfigImpl_PORT_0_MUST_BE_AN_INTEGER_BETWEEN_1_AND_2 + .toLocalizedString(new Object[] {Integer.valueOf(port), Integer.valueOf(MIN_PORT), + Integer.valueOf(MAX_PORT)})); + } + + if (this.bindAddress != null && InetAddressUtil.validateHost(this.bindAddress) == null) { + throw new IllegalArgumentException( + LocalizedStrings.DistributionLocatorConfigImpl_INVALID_HOST_0 + .toLocalizedString(this.bindAddress)); + } + } + + /** + * Currently, listeners are not supported on the locator config. + */ + @Override + protected void configChanged() { + + } + + @Override + public Object clone() throws CloneNotSupportedException { + DistributionLocatorConfigImpl clone = (DistributionLocatorConfigImpl) super.clone(); + clone.locator = null; + return clone; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("DistributionLocatorConfig: host=").append(getHost()); + sb.append(", bindAddress=").append(getBindAddress()); + sb.append(", port=").append(getPort()); + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java new file mode 100755 index 0000000..ccfbcac --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributionLocatorImpl.java @@ -0,0 +1,329 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.geode.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.AdminDistributedSystem; +import org.apache.geode.internal.admin.api.DistributionLocator; +import org.apache.geode.internal.admin.api.DistributionLocatorConfig; +import org.apache.geode.internal.admin.api.ManagedEntityConfig; +import org.apache.geode.distributed.internal.DM; +import org.apache.geode.distributed.internal.DistributionConfig; +import org.apache.geode.distributed.internal.membership.InternalDistributedMember; +import org.apache.geode.internal.admin.remote.DistributionLocatorId; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.internal.logging.log4j.LocalizedMessage; +import org.apache.logging.log4j.Logger; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; + +/** + * Default administrative implementation of a DistributionLocator. + * + * @since GemFire 3.5 + */ +public class DistributionLocatorImpl implements DistributionLocator, InternalManagedEntity { + + private static final Logger logger = LogService.getLogger(); + + /** + * How many new <code>DistributionLocator</code>s have been created? + */ + private static int newLocators = 0; + + //////////////////// Instance Fields //////////////////// + + /** + * The configuration object for this locator + */ + private final DistributionLocatorConfigImpl config; + + /** + * The id of this distribution locator + */ + private final String id; + + /** + * Used to control the actual DistributionLocator service + */ + private ManagedEntityController controller; + + /** + * The system that this locator is a part of + */ + private AdminDistributedSystemImpl system; + + // ------------------------------------------------------------------------- + // constructor(s)... + // ------------------------------------------------------------------------- + + /** + * Constructs new instance of <code>DistributionLocatorImpl</code> that is a member of the given + * distributed system. + */ + public DistributionLocatorImpl(DistributionLocatorConfig config, + AdminDistributedSystemImpl system) { + this.config = (DistributionLocatorConfigImpl) config; + this.config.validate(); + this.config.setManagedEntity(this); + this.id = getNewId(); + this.controller = system.getEntityController(); + this.system = system; + } + + // ------------------------------------------------------------------------- + // Attribute accessors/mutators... + // ------------------------------------------------------------------------- + + public String getId() { + return this.id; + } + + public String getNewId() { + synchronized (DistributionLocatorImpl.class) { + return "Locator" + (++newLocators); + } + } + + /** + * Returns the configuration object for this locator. + * + * @since GemFire 4.0 + */ + public DistributionLocatorConfig getConfig() { + return this.config; + } + + public AdminDistributedSystem getDistributedSystem() { + return this.system; + } + + /** + * Unfortunately, it doesn't make much sense to maintain the state of a locator. The admin API + * does not receive notification when the locator actually starts and stops. If we try to guess, + * we'll just end up with race conditions galore. So, we can't fix bug 32455 for locators. + */ + public int setState(int state) { + throw new UnsupportedOperationException( + LocalizedStrings.DistributionLocatorImpl_CAN_NOT_SET_THE_STATE_OF_A_LOCATOR + .toLocalizedString()); + } + + // ------------------------------------------------------------------------- + // Operations... + // ------------------------------------------------------------------------- + + /** + * Polls to determine whether or not this managed entity has started. + */ + public boolean waitToStart(long timeout) throws InterruptedException { + + if (Thread.interrupted()) + throw new InterruptedException(); + + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < timeout) { + if (this.isRunning()) { + return true; + + } else { + Thread.sleep(100); + } + } + + logger.info( + LocalizedMessage.create(LocalizedStrings.DistributionLocatorImpl_DONE_WAITING_FOR_LOCATOR)); + return this.isRunning(); + } + + /** + * Polls to determine whether or not this managed entity has stopped. + */ + public boolean waitToStop(long timeout) throws InterruptedException { + + if (Thread.interrupted()) + throw new InterruptedException(); + + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < timeout) { + if (!this.isRunning()) { + return true; + + } else { + Thread.sleep(100); + } + } + + return !this.isRunning(); + } + + public boolean isRunning() { + DM dm = ((AdminDistributedSystemImpl) getDistributedSystem()).getDistributionManager(); + if (dm == null) { + try { + return this.controller.isRunning(this); + } catch (IllegalStateException e) { + return false; + } + } + + String host = getConfig().getHost(); + int port = getConfig().getPort(); + String bindAddress = getConfig().getBindAddress(); + + boolean found = false; + Map<InternalDistributedMember, Collection<String>> hostedLocators = dm.getAllHostedLocators(); + for (Iterator<InternalDistributedMember> memberIter = + hostedLocators.keySet().iterator(); memberIter.hasNext();) { + for (Iterator<String> locatorIter = + hostedLocators.get(memberIter.next()).iterator(); locatorIter.hasNext();) { + DistributionLocatorId locator = new DistributionLocatorId(locatorIter.next()); + found = found || locator.getHost().getHostAddress().equals(host); + found = found || locator.getHost().getHostName().equals(host); + if (!found && !host.contains(".")) { + try { + InetAddress inetAddr = InetAddress.getByName(host); + found = locator.getHost().getHostName().equals(inetAddr.getHostName()); + if (!found) { + found = locator.getHost().getHostAddress().equals(inetAddr.getHostAddress()); + } + } catch (UnknownHostException e) { + // try config host as if it is an IP address instead of host name + } + } + if (locator.getBindAddress() != null && !locator.getBindAddress().isEmpty() + && bindAddress != null && !bindAddress.isEmpty()) { + found = found && locator.getBindAddress().equals(bindAddress); + } + found = found && locator.getPort() == port; + if (found) { + return true; + } + } + } + return found; + } + + public void start() { + this.config.validate(); + this.controller.start(this); + this.config.setLocator(this); + this.system.updateLocatorsString(); + } + + public void stop() { + this.controller.stop(this); + this.config.setLocator(null); + } + + public String getLog() { + return this.controller.getLog(this); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object + */ + @Override + public String toString() { + return "DistributionLocator " + getId(); + } + + //////////////////////// Command execution //////////////////////// + + public ManagedEntityConfig getEntityConfig() { + return this.getConfig(); + } + + public String getEntityType() { + return "Locator"; + } + + public String getStartCommand() { + StringBuffer sb = new StringBuffer(); + sb.append(this.controller.getProductExecutable(this, "gemfire")); + sb.append(" start-locator -q -dir="); + sb.append(this.getConfig().getWorkingDirectory()); + sb.append(" -port="); + sb.append(this.getConfig().getPort()); + Properties props = config.getDistributedSystemProperties(); + Enumeration en = props.propertyNames(); + while (en.hasMoreElements()) { + String pn = (String) en.nextElement(); + sb.append(" -D" + DistributionConfig.GEMFIRE_PREFIX + "" + pn + "=" + props.getProperty(pn)); + } + + String bindAddress = this.getConfig().getBindAddress(); + if (bindAddress != null && bindAddress.length() > 0) { + sb.append(" -address="); + sb.append(this.getConfig().getBindAddress()); + } + sb.append(" "); + + String sslArgs = this.controller.buildSSLArguments(this.system.getConfig()); + if (sslArgs != null) { + sb.append(sslArgs); + } + + return sb.toString().trim(); + } + + public String getStopCommand() { + StringBuffer sb = new StringBuffer(); + sb.append(this.controller.getProductExecutable(this, "gemfire")); + sb.append(" stop-locator -q -dir="); + sb.append(this.getConfig().getWorkingDirectory()); + sb.append(" -port="); + sb.append(this.getConfig().getPort()); + + String bindAddress = this.getConfig().getBindAddress(); + if (bindAddress != null && bindAddress.length() > 0) { + sb.append(" -address="); + sb.append(this.getConfig().getBindAddress()); + } + sb.append(" "); + + String sslArgs = this.controller.buildSSLArguments(this.system.getConfig()); + if (sslArgs != null) { + sb.append(sslArgs); + } + + return sb.toString().trim(); + } + + public String getIsRunningCommand() { + StringBuffer sb = new StringBuffer(); + sb.append(this.controller.getProductExecutable(this, "gemfire")); + sb.append(" status-locator -dir="); + sb.append(this.getConfig().getWorkingDirectory()); + + return sb.toString().trim(); + } + + public String getLogCommand() { + StringBuffer sb = new StringBuffer(); + sb.append(this.controller.getProductExecutable(this, "gemfire")); + sb.append(" tail-locator-log -dir="); + sb.append(this.getConfig().getWorkingDirectory()); + + return sb.toString().trim(); + } + +} + http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java new file mode 100755 index 0000000..554d160 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.AdminDistributedSystem; +import org.apache.geode.internal.admin.api.DistributedSystemConfig; +import org.apache.geode.internal.admin.api.ManagedEntity; +import org.apache.geode.internal.admin.api.ManagedEntityConfig; +import org.apache.geode.distributed.internal.DistributionConfig; +import org.apache.geode.internal.ProcessOutputReader; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.internal.logging.LoggingThreadGroup; +import org.apache.geode.internal.logging.log4j.LocalizedMessage; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.util.Iterator; +import java.util.Properties; + +import static org.apache.geode.distributed.ConfigurationProperties.*; + +/** + * Implements the actual administration (starting, stopping, etc.) of GemFire + * {@link ManagedEntity}s. It {@link Runtime#exec(java.lang.String) executes} commands to administer + * the entities based on information provided by the {@link InternalManagedEntity} object. Note that + * it does not use <code>SystemAdmin</code> to manage "local" entities; it always execs the scripts. + * + * <P> + * + * This class is a refactoring of <code>Systemcontroller</code>, <code>RemoteCommand</code>, and + * <code>LocatorRemoteCommand</code>. + * + * @since GemFire 4.0 + */ +class EnabledManagedEntityController implements ManagedEntityController { + private static final Logger logger = LogService.getLogger(); + + // /** A lock to ensure that only entity is managed at a time. See bug + // * 31374. */ + // private static Object startStopLock = new Object(); + + /** Known strings found in output indicating error. */ + private static final String[] ERROR_OUTPUTS = new String[] {"No such file or directory", + "The system cannot find the file specified.", "Access is denied.", "cannot open", "ERROR"}; + + /** Token in command prefix to be replaced with actual HOST */ + private static final String HOST = "{HOST}"; + + /** Token in command prefix to be replaced with actual execution CMD */ + private static final String CMD = "{CMD}"; + + ////////////////////// Instance Fields ////////////////////// + + /** + * The thread group in which threads launched by this system controller reside. + */ + private final ThreadGroup threadGroup; + + /** System to which the managed entities belong */ + private final AdminDistributedSystem system; + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new <code>ManagedEntityController</code> for entities in the given distributed + * system. + */ + EnabledManagedEntityController(AdminDistributedSystem system) { + this.system = system; + this.threadGroup = + LoggingThreadGroup.createThreadGroup("ManagedEntityController threads", logger); + } + + ///////////////////// Instance Methods ///////////////////// + + /** + * Returns <code>true</code> if the <code>output</code> string contains a known error message. + */ + private boolean outputIsError(String output) { + if (output == null) + return false; + boolean error = false; + for (int i = 0; i < ERROR_OUTPUTS.length; i++) { + error = output.indexOf(ERROR_OUTPUTS[i]) > -1; + if (error) + return error; + } + return error; + } + + /** + * Executes a command using {@link Runtime#exec(java.lang.String)}. + * + * @param command The full command to remotely execute + * + * @return Output from the command that was executed or <code>null</code> if the executing the + * command failed. + */ + protected String execute(String command, InternalManagedEntity entity) { + /* + * TODO: this is getting ugly... clients of this method really need to have the ability to do + * their own parsing/checking of 'output' + */ + if (command == null || command.length() == 0) { + throw new IllegalArgumentException( + LocalizedStrings.ManagedEntityController_EXECUTION_COMMAND_IS_EMPTY.toLocalizedString()); + } + + File workingDir = new File(entity.getEntityConfig().getWorkingDirectory()); + logger.info(LocalizedMessage.create( + LocalizedStrings.ManagedEntityController_EXECUTING_REMOTE_COMMAND_0_IN_DIRECTORY_1, + new Object[] {command, workingDir})); + Process p = null; + try { + p = Runtime.getRuntime().exec(command, null /* env */, workingDir); + + } catch (java.io.IOException e) { + logger.fatal(LocalizedMessage + .create(LocalizedStrings.ManagedEntityController_WHILE_EXECUTING_0, command), e); + return null; + } + + final ProcessOutputReader pos = new ProcessOutputReader(p); + int retCode = pos.getExitCode(); + final String output = pos.getOutput(); + logger.info( + LocalizedMessage.create(LocalizedStrings.ManagedEntityController_RESULT_OF_EXECUTING_0_IS_1, + new Object[] {command, Integer.valueOf(retCode)})); + logger.info(LocalizedMessage.create(LocalizedStrings.ManagedEntityController_OUTPUT_OF_0_IS_1, + new Object[] {command, output})); + + if (retCode != 0 || outputIsError(output)) { + logger.warn(LocalizedMessage + .create(LocalizedStrings.ManagedEntityController_REMOTE_EXECUTION_OF_0_FAILED, command)); + return null; + } + + return output; + } + + /** Returns true if the path ends with a path separator. */ + private boolean endsWithSeparator(String path) { + return path.endsWith("/") || path.endsWith("\\"); + } + + /** Translates the path between Windows and UNIX. */ + private String getOSPath(String path) { + if (pathIsWindows(path)) { + return path.replace('/', '\\'); + } else { + return path.replace('\\', '/'); + } + } + + // /** Returns true if the path is on Windows. */ + // private boolean pathIsWindows(File path) { + // return pathIsWindows(path.toString()); + // } + + /** Returns true if the path is on Windows. */ + private boolean pathIsWindows(String path) { + if (path != null && path.length() > 1) { + return (Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') + || (path.startsWith("//") || path.startsWith("\\\\")); + } + return false; + } + + /** + * If the managed entity resides on a remote host, then <code>command</code> is munged to take the + * remote command into account. + * + * @throws IllegalStateException If a remote command is required, but one has not been specified. + */ + private String arrangeRemoteCommand(InternalManagedEntity entity, String cmd) { + + String host = entity.getEntityConfig().getHost(); + if (InetAddressUtil.isLocalHost(host)) { + // No arranging necessary + return cmd; + } + + String prefix = entity.getEntityConfig().getRemoteCommand(); + if (prefix == null || prefix.length() <= 0) { + prefix = entity.getDistributedSystem().getRemoteCommand(); + } + + if (prefix == null || prefix.length() <= 0) { + throw new IllegalStateException( + LocalizedStrings.ManagedEntityController_A_REMOTE_COMMAND_MUST_BE_SPECIFIED_TO_OPERATE_ON_A_MANAGED_ENTITY_ON_HOST_0 + .toLocalizedString(host)); + } + + int hostIdx = prefix.indexOf(HOST); + int cmdIdx = prefix.indexOf(CMD); + if (hostIdx == -1 && cmdIdx == -1) { + return prefix + " " + host + " " + cmd; + } + + if (hostIdx >= 0) { + String start = prefix.substring(0, hostIdx); + String end = null; + if (hostIdx + HOST.length() >= prefix.length()) { + end = ""; + } else { + end = prefix.substring(hostIdx + HOST.length()); + } + prefix = start + host + end; + cmdIdx = prefix.indexOf(CMD); // recalculate; + } + + if (cmdIdx >= 0) { + String start = prefix.substring(0, cmdIdx); + String end = null; + if (cmdIdx + CMD.length() >= prefix.length()) { + end = ""; + } else { + end = prefix.substring(cmdIdx + CMD.length()); + } + prefix = start + cmd + end; + } + return prefix; + } + + /** + * Returns the full path to the executable in <code>$GEMFIRE/bin</code> taking into account the + * {@linkplain ManagedEntityConfig#getProductDirectory product directory} and the platform's file + * separator. + * + * <P> + * + * Note: we should probably do a better job of determine whether or not the machine on which the + * entity runs is Windows or Linux. + * + * @param executable The name of the executable that resides in <code>$GEMFIRE/bin</code>. + */ + public String getProductExecutable(InternalManagedEntity entity, String executable) { + String productDirectory = entity.getEntityConfig().getProductDirectory(); + String path = null; + File productDir = new File(productDirectory); + // if (productDir != null) (cannot be null) + { + path = productDir.getPath(); + if (!endsWithSeparator(path)) { + path += File.separator; + } + path += "bin" + File.separator; + } + // else { + // path = ""; + // } + + String bat = ""; + if (pathIsWindows(path)) { + bat = ".bat"; + } + return getOSPath(path) + executable + bat; + } + + /** + * Builds optional SSL command-line arguments. Returns null if SSL is not enabled for the + * distributed system. + */ + public String buildSSLArguments(DistributedSystemConfig config) { + Properties sslProps = buildSSLProperties(config, true); + if (sslProps == null) + return null; + + StringBuffer sb = new StringBuffer(); + for (Iterator iter = sslProps.keySet().iterator(); iter.hasNext();) { + String key = (String) iter.next(); + String value = sslProps.getProperty(key); + sb.append(" -J-D" + key + "=" + value); + } + + return sb.toString(); + } + + /** + * Builds optional SSL properties for DistributionLocator. Returns null if SSL is not enabled for + * the distributed system. + * + * @param forCommandLine true indicates that {@link DistributionConfig#GEMFIRE_PREFIX} should be + * prepended so the argument will become -Dgemfire.xxxx + */ + private Properties buildSSLProperties(DistributedSystemConfig config, boolean forCommandLine) { + if (!config.isSSLEnabled()) + return null; + + String prefix = ""; + if (forCommandLine) + prefix = DistributionConfig.GEMFIRE_PREFIX; + + Properties sslProps = (Properties) config.getSSLProperties().clone(); + // add ssl-enabled, etc... + sslProps.setProperty(prefix + MCAST_PORT, "0"); + sslProps.setProperty(prefix + CLUSTER_SSL_ENABLED, String.valueOf(config.isSSLEnabled())); + sslProps.setProperty(prefix + CLUSTER_SSL_CIPHERS, config.getSSLCiphers()); + sslProps.setProperty(prefix + CLUSTER_SSL_PROTOCOLS, config.getSSLProtocols()); + sslProps.setProperty(prefix + CLUSTER_SSL_REQUIRE_AUTHENTICATION, + String.valueOf(config.isSSLAuthenticationRequired())); + return sslProps; + } + + + /** + * Starts a managed entity. + */ + public void start(final InternalManagedEntity entity) { + final String command = arrangeRemoteCommand(entity, entity.getStartCommand()); + Thread start = new Thread(this.threadGroup, new Runnable() { + public void run() { + execute(command, entity); + } + }, "Start " + entity.getEntityType()); + start.start(); + } + + /** + * Stops a managed entity. + */ + public void stop(final InternalManagedEntity entity) { + final String command = arrangeRemoteCommand(entity, entity.getStopCommand()); + Thread stop = new Thread(this.threadGroup, new Runnable() { + public void run() { + execute(command, entity); + } + }, "Stop " + entity.getEntityType()); + stop.start(); + } + + /** + * Returns whether or not a managed entity is running + */ + public boolean isRunning(InternalManagedEntity entity) { + final String command = arrangeRemoteCommand(entity, entity.getIsRunningCommand()); + String output = execute(command, entity); + + if (output == null || (output.indexOf("stop" /* "ing" "ped" */) != -1) + || (output.indexOf("killed") != -1) || (output.indexOf("starting") != -1)) { + return false; + + } else if (output.indexOf("running") != -1) { + return true; + + } else { + throw new IllegalStateException( + LocalizedStrings.ManagedEntityController_COULD_NOT_DETERMINE_IF_MANAGED_ENTITY_WAS_RUNNING_0 + .toLocalizedString(output)); + } + } + + /** + * Returns the contents of a locator's log file. Other APIs are used to get the log file of + * managed entities that are also system members. + */ + public String getLog(DistributionLocatorImpl locator) { + String command = arrangeRemoteCommand(locator, locator.getLogCommand()); + return execute(command, locator); + } + + /** + * Returns the contents of the given directory using the given managed entity to determine the + * host and remote command. + */ + private String listDirectory(InternalManagedEntity entity, String dir) { + ManagedEntityConfig config = entity.getEntityConfig(); + String listFile = pathIsWindows(config.getProductDirectory()) ? "dir " : "ls "; + String command = arrangeRemoteCommand(entity, listFile + dir); + return execute(command, entity); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java new file mode 100644 index 0000000..dd0d0dc --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.logging.log4j.Logger; + +import org.apache.geode.CancelException; +import org.apache.geode.DataSerializer; +import org.apache.geode.cache.persistence.PersistentID; +import org.apache.geode.distributed.DistributedMember; +import org.apache.geode.distributed.internal.DM; +import org.apache.geode.distributed.internal.DistributionManager; +import org.apache.geode.distributed.internal.DistributionMessage; +import org.apache.geode.distributed.internal.ReplyException; +import org.apache.geode.internal.admin.remote.AdminFailureResponse; +import org.apache.geode.internal.admin.remote.AdminMultipleReplyProcessor; +import org.apache.geode.internal.admin.remote.AdminResponse; +import org.apache.geode.internal.admin.remote.CliLegacyMessage; +import org.apache.geode.internal.cache.GemFireCacheImpl; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.internal.logging.log4j.LocalizedMessage; + +/** + * A request send from an admin VM to all of the peers to indicate that that should complete the + * backup operation. + * + * + */ +public class FinishBackupRequest extends CliLegacyMessage { + private static final Logger logger = LogService.getLogger(); + + private File targetDir; + private File baselineDir; + private boolean abort; + + public FinishBackupRequest() { + super(); + } + + public FinishBackupRequest(File targetDir, File baselineDir, boolean abort) { + this.targetDir = targetDir; + this.baselineDir = baselineDir; + this.abort = abort; + } + + public static Map<DistributedMember, Set<PersistentID>> send(DM dm, Set recipients, + File targetDir, File baselineDir, boolean abort) { + FinishBackupRequest request = new FinishBackupRequest(targetDir, baselineDir, abort); + request.setRecipients(recipients); + + FinishBackupReplyProcessor replyProcessor = new FinishBackupReplyProcessor(dm, recipients); + request.msgId = replyProcessor.getProcessorId(); + dm.putOutgoing(request); + try { + replyProcessor.waitForReplies(); + } catch (ReplyException e) { + if (!(e.getCause() instanceof CancelException)) { + throw e; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + AdminResponse response = request.createResponse((DistributionManager) dm); + response.setSender(dm.getDistributionManagerId()); + replyProcessor.process(response); + return replyProcessor.results; + } + + @Override + protected AdminResponse createResponse(DistributionManager dm) { + GemFireCacheImpl cache = GemFireCacheImpl.getInstance(); + HashSet<PersistentID> persistentIds; + if (cache == null || cache.getBackupManager() == null) { + persistentIds = new HashSet<PersistentID>(); + } else { + try { + persistentIds = cache.getBackupManager().finishBackup(targetDir, baselineDir, abort); + } catch (IOException e) { + logger.error( + LocalizedMessage.create(LocalizedStrings.CliLegacyMessage_ERROR, this.getClass()), e); + return AdminFailureResponse.create(dm, getSender(), e); + } + } + + return new FinishBackupResponse(this.getSender(), persistentIds); + } + + public int getDSFID() { + return FINISH_BACKUP_REQUEST; + } + + @Override + public void fromData(DataInput in) throws IOException, ClassNotFoundException { + super.fromData(in); + targetDir = DataSerializer.readFile(in); + baselineDir = DataSerializer.readFile(in); + abort = DataSerializer.readBoolean(in); + } + + @Override + public void toData(DataOutput out) throws IOException { + super.toData(out); + DataSerializer.writeFile(targetDir, out); + DataSerializer.writeFile(baselineDir, out); + DataSerializer.writeBoolean(abort, out); + } + + private static class FinishBackupReplyProcessor extends AdminMultipleReplyProcessor { + Map<DistributedMember, Set<PersistentID>> results = + Collections.synchronizedMap(new HashMap<DistributedMember, Set<PersistentID>>()); + + public FinishBackupReplyProcessor(DM dm, Collection initMembers) { + super(dm, initMembers); + } + + @Override + protected boolean stopBecauseOfExceptions() { + return false; + } + + + + @Override + protected int getAckWaitThreshold() { + // Disable the 15 second warning if the backup is taking a long time + return 0; + } + + @Override + public long getAckSevereAlertThresholdMS() { + // Don't log severe alerts for backups either + return Long.MAX_VALUE; + } + + @Override + protected void process(DistributionMessage msg, boolean warn) { + if (msg instanceof FinishBackupResponse) { + final HashSet<PersistentID> persistentIds = ((FinishBackupResponse) msg).getPersistentIds(); + if (persistentIds != null && !persistentIds.isEmpty()) { + results.put(msg.getSender(), persistentIds); + } + } + super.process(msg, warn); + } + + + + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java new file mode 100644 index 0000000..ad68f97 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.HashSet; + +import org.apache.geode.DataSerializer; +import org.apache.geode.cache.persistence.PersistentID; +import org.apache.geode.distributed.internal.membership.InternalDistributedMember; +import org.apache.geode.internal.admin.remote.AdminResponse; + +/** + * The reply for a {@link FinishBackupRequest}. The reply contains the persistent ids of the disk + * stores that were backed up on this member. + * + * + */ +public class FinishBackupResponse extends AdminResponse { + + private HashSet<PersistentID> persistentIds; + + public FinishBackupResponse() { + super(); + } + + public FinishBackupResponse(InternalDistributedMember sender, + HashSet<PersistentID> persistentIds) { + this.setRecipient(sender); + this.persistentIds = persistentIds; + } + + public HashSet<PersistentID> getPersistentIds() { + return persistentIds; + } + + @Override + public void fromData(DataInput in) throws IOException, ClassNotFoundException { + super.fromData(in); + persistentIds = DataSerializer.readHashSet(in); + } + + @Override + public void toData(DataOutput out) throws IOException { + super.toData(out); + DataSerializer.writeHashSet(persistentIds, out); + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + public int getDSFID() { + return FINISH_BACKUP_RESPONSE; + } + + @Override + public String toString() { + return getClass().getName() + ": " + persistentIds; + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java new file mode 100644 index 0000000..c780d1d --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.geode.CancelException; +import org.apache.geode.cache.persistence.PersistentID; +import org.apache.geode.distributed.internal.DM; +import org.apache.geode.distributed.internal.DistributionManager; +import org.apache.geode.distributed.internal.ReplyException; +import org.apache.geode.internal.admin.remote.AdminMultipleReplyProcessor; +import org.apache.geode.internal.admin.remote.AdminResponse; +import org.apache.geode.internal.admin.remote.CliLegacyMessage; +import org.apache.geode.internal.cache.DiskStoreImpl; +import org.apache.geode.internal.cache.GemFireCacheImpl; + +/** + * A request to from an admin VM to all non admin members to start a backup. In the prepare phase of + * the backup, the members will suspend bucket destroys to make sure buckets aren't missed during + * the backup. + * + * + */ +public class FlushToDiskRequest extends CliLegacyMessage { + + public FlushToDiskRequest() { + + } + + public static void send(DM dm, Set recipients) { + FlushToDiskRequest request = new FlushToDiskRequest(); + request.setRecipients(recipients); + + FlushToDiskProcessor replyProcessor = new FlushToDiskProcessor(dm, recipients); + request.msgId = replyProcessor.getProcessorId(); + dm.putOutgoing(request); + try { + replyProcessor.waitForReplies(); + } catch (ReplyException e) { + if (!(e.getCause() instanceof CancelException)) { + throw e; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + AdminResponse response = request.createResponse((DistributionManager) dm); + response.setSender(dm.getDistributionManagerId()); + replyProcessor.process(response); + } + + @Override + protected AdminResponse createResponse(DistributionManager dm) { + GemFireCacheImpl cache = GemFireCacheImpl.getInstance(); + HashSet<PersistentID> persistentIds; + if (cache != null) { + Collection<DiskStoreImpl> diskStores = cache.listDiskStoresIncludingRegionOwned(); + for (DiskStoreImpl store : diskStores) { + store.flush(); + } + } + + return new FlushToDiskResponse(this.getSender()); + } + + public int getDSFID() { + return FLUSH_TO_DISK_REQUEST; + } + + private static class FlushToDiskProcessor extends AdminMultipleReplyProcessor { + public FlushToDiskProcessor(DM dm, Collection initMembers) { + super(dm, initMembers); + } + + @Override + protected boolean stopBecauseOfExceptions() { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java new file mode 100644 index 0000000..869d56b --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import org.apache.geode.distributed.internal.membership.InternalDistributedMember; +import org.apache.geode.internal.admin.remote.AdminResponse; + +/** + * The response to the {@link FlushToDiskRequest} + * + * + */ +public class FlushToDiskResponse extends AdminResponse { + + public FlushToDiskResponse() { + super(); + } + + public FlushToDiskResponse(InternalDistributedMember sender) { + this.setRecipient(sender); + } + + public int getDSFID() { + return FLUSH_TO_DISK_RESPONSE; + } + + @Override + public String toString() { + return getClass().getName(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java new file mode 100644 index 0000000..16b1d79 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.GemFireHealthConfig; +import org.apache.geode.internal.i18n.LocalizedStrings; + +// @todo davidw Delegate to a "parent" config for properties that are not overridden. +// This will be made easier with a special <code>HealthConfigAttribute</code> class. +/** + * The implementation of <code>GemFireHealthConfig</code> + * + * + * + * @since GemFire 3.5 + */ +public class GemFireHealthConfigImpl extends CacheHealthConfigImpl implements GemFireHealthConfig { + + private static final long serialVersionUID = -6797673296902808018L; + + /** The name of the host to which this configuration applies. */ + private String hostName; + + /** + * The number of seconds to wait between evaluating the health of GemFire. + */ + private int interval = DEFAULT_HEALTH_EVALUATION_INTERVAL; + + //////////////////////// Constructors //////////////////////// + + /** + * Creates a new <code>GemFireHealthConfigImpl</code> that applies to the host with the given + * name. + * + * @param hostName The name of the host to which this configuration applies. If <code>null</code>, + * then this is the "default" configuration. + */ + public GemFireHealthConfigImpl(String hostName) { + this.hostName = hostName; + } + + /////////////////////// Instance Methods /////////////////////// + + public String getHostName() { + return this.hostName; + } + + public void setHealthEvaluationInterval(int interval) { + this.interval = interval; + } + + public int getHealthEvaluationInterval() { + return this.interval; + } + + @Override + public String toString() { + if (this.hostName == null) { + return LocalizedStrings.GemFireHealthConfigImpl_DEFAULT_GEMFIRE_HEALTH_CONFIGURATION + .toLocalizedString(); + + } else { + return LocalizedStrings.GemFireHealthConfigImpl_GEMFIRE_HEALTH_CONFIGURATION_FOR_HOST_0 + .toLocalizedString(this.hostName); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java new file mode 100644 index 0000000..d35a94c --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.logging.log4j.Logger; + +import org.apache.geode.internal.admin.api.GemFireHealth; +import org.apache.geode.internal.admin.api.GemFireHealthConfig; +import org.apache.geode.distributed.internal.DistributionManager; +import org.apache.geode.internal.Assert; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; + +/** + * Evaluates the health of various GemFire components in the VM according to a + * {@link GemFireHealthConfig}. + * + * <P> + * + * Note that evaluators never reside in the administration VM, they only in member VMs. They are not + * <code>Serializable</code> and aren't meant to be. + * + * @see MemberHealthEvaluator + * @see CacheHealthEvaluator + * + * + * @since GemFire 3.5 + */ +public class GemFireHealthEvaluator { + + private static final Logger logger = LogService.getLogger(); + + /** Determines how the health of GemFire is determined */ + private GemFireHealthConfig config; + + /** Evaluates the health of this member of the distributed system */ + private MemberHealthEvaluator memberHealth; + + /** Evaluates the health of the Cache hosted in this VM */ + private CacheHealthEvaluator cacheHealth; + + /** + * The most recent <code>OKAY_HEALTH</code> diagnoses of the GemFire system + */ + private List okayDiagnoses; + + /** + * The most recent <code>POOR_HEALTH</code> diagnoses of the GemFire system + */ + private List poorDiagnoses; + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new <code>GemFireHealthEvaluator</code> + * + * @param config The configuration that determines whether or GemFire is healthy + * @param dm The distribution manager + */ + public GemFireHealthEvaluator(GemFireHealthConfig config, DistributionManager dm) { + if (config == null) { + throw new NullPointerException( + LocalizedStrings.GemFireHealthEvaluator_NULL_GEMFIREHEALTHCONFIG.toLocalizedString()); + } + + this.config = config; + this.memberHealth = new MemberHealthEvaluator(config, dm); + this.cacheHealth = new CacheHealthEvaluator(config, dm); + this.okayDiagnoses = new ArrayList(); + this.poorDiagnoses = new ArrayList(); + } + + ////////////////////// Instance Methods ////////////////////// + + /** + * Evaluates the health of the GemFire components in this VM. + * + * @return The aggregate health code (such as {@link GemFireHealth#OKAY_HEALTH}) of the GemFire + * components. + */ + public GemFireHealth.Health evaluate() { + List status = new ArrayList(); + this.memberHealth.evaluate(status); + this.cacheHealth.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()); + } + } + + if (logger.isDebugEnabled()) { + logger.debug("Evaluated health to be {}", overallHealth); + } + return overallHealth; + } + + /** + * Returns detailed information explaining the current health status. Each array element is a + * different cause for the current status. An empty array will be returned if the current status + * is {@link GemFireHealth#GOOD_HEALTH}. + */ + public String[] getDiagnosis(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; + } + } + + /** + * Resets the state of this evaluator + */ + public void reset() { + this.okayDiagnoses.clear(); + this.poorDiagnoses.clear(); + } + + /** + * Returns the heath evaluation interval, in seconds. + * + * @see GemFireHealthConfig#getHealthEvaluationInterval + */ + public int getEvaluationInterval() { + return this.config.getHealthEvaluationInterval(); + } + + /** + * Closes this evaluator and releases all of its resources + */ + public void close() { + this.memberHealth.close(); + this.cacheHealth.close(); + } + +}
