You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/10/18 18:57:47 UTC
[17/42] incubator-geode git commit: Move Admin API to internal
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/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..aae8b2f
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/EnabledManagedEntityController.java
@@ -0,0 +1,411 @@
+/*
+ * 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/c0221bed/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..323248a
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupRequest.java
@@ -0,0 +1,171 @@
+/*
+ * 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/c0221bed/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..f2da628
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FinishBackupResponse.java
@@ -0,0 +1,78 @@
+/*
+ * 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/c0221bed/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..9eec442
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskRequest.java
@@ -0,0 +1,97 @@
+/*
+ * 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/c0221bed/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..e044932
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/FlushToDiskResponse.java
@@ -0,0 +1,45 @@
+/*
+ * 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/c0221bed/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..d3a6a84
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthConfigImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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/c0221bed/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..74590b7
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthEvaluator.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 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();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthImpl.java
new file mode 100644
index 0000000..e2e3f47
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/GemFireHealthImpl.java
@@ -0,0 +1,538 @@
+/*
+ * 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.CancelException;
+import org.apache.geode.internal.Assert;
+import org.apache.geode.internal.admin.*;
+import org.apache.geode.internal.admin.api.AdminDistributedSystem;
+import org.apache.geode.internal.admin.api.DistributedSystemHealthConfig;
+import org.apache.geode.internal.admin.api.GemFireHealth;
+import org.apache.geode.internal.admin.api.GemFireHealthConfig;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.*;
+
+/**
+ * Provides the implementation of the <code>GemFireHealth</code>
+ * administration API. This class is responsible for {@linkplain
+ * GemFireVM#addHealthListener sending} the {@link
+ * GemFireHealthConfig}s to the remote member VM in which the health
+ * is calcualted.
+ *
+ *
+ * @since GemFire 3.5
+ */
+public class GemFireHealthImpl
+ implements GemFireHealth, JoinLeaveListener, HealthListener {
+
+ /** The distributed system whose health is being monitored */
+ private final GfManagerAgent agent;
+
+ /** The default configuration for checking GemFire health */
+ protected GemFireHealthConfig defaultConfig;
+
+ /** Maps the name of a host to its <code>GemFireHealthConfig</code>.
+ * Note that the mappings are created lazily. */
+ private final Map hostConfigs;
+
+ /** Maps the name of a host to all of the members
+ * (<code>GemFireVM</code>s) that run on that host. */
+ private final Map hostMembers;
+
+ /** The members that are known to be in {@link #OKAY_HEALTH}. */
+ private Collection okayHealth;
+
+ /** The members that are known to be in {@link #POOR_HEALTH}. */
+ private Collection poorHealth;
+
+ /** The overall health of GemFire */
+ private GemFireHealth.Health overallHealth;
+
+ /** Is this GemFireHealthImpl closed? */
+ private boolean isClosed;
+
+ /** The configuration specifying how the health of the distributed
+ * system should be computed. */
+ protected volatile DistributedSystemHealthConfig dsHealthConfig;
+
+ /** Monitors the health of the entire distributed system */
+ private DistributedSystemHealthMonitor dsHealthMonitor = null;
+
+ /** The distributed system whose health is monitored by this
+ * <Code>GemFireHealth</code>. */
+ private final AdminDistributedSystem system;
+
+
+ /////////////////////// Constructors ///////////////////////
+
+ /**
+ * Creates a new <code>GemFireHealthImpl</code> that monitors the
+ * health of member of the given distributed system.
+ */
+ protected GemFireHealthImpl(GfManagerAgent agent,
+ AdminDistributedSystem system) {
+// agent.getDM().getLogger().info("Creating GemFireHealthImpl",
+// new Exception("Stack trace"));
+
+ this.agent = agent;
+ this.system = system;
+
+ this.hostConfigs = new HashMap();
+ this.hostMembers = new HashMap();
+ this.okayHealth = new HashSet();
+ this.poorHealth = new HashSet();
+ this.overallHealth = GOOD_HEALTH;
+ this.isClosed = false;
+
+ GemFireVM[] apps = this.agent.listApplications();
+ for (int i = 0; i < apps.length; i++) {
+ GemFireVM member = apps[i];
+ this.noteNewMember(member);
+ }
+
+ agent.addJoinLeaveListener(this);
+ setDefaultGemFireHealthConfig(createGemFireHealthConfig(null));
+ setDistributedSystemHealthConfig(createDistributedSystemHealthConfig());
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("closed=" + isClosed);
+ sb.append("; hostMembers=" + hostMembers);
+ sb.append("; okayHealth=" + okayHealth);
+ sb.append("; poorHealth=" + poorHealth);
+ sb.append("; overallHealth=" + overallHealth);
+ sb.append("; diagnosis=" + getDiagnosis());
+ return sb.toString();
+ }
+ ////////////////////// Instance Methods //////////////////////
+
+ /**
+ * Returns the <code>DistributedSystem</code> whose health this
+ * <code>GemFireHealth</code> monitors.
+ */
+ public AdminDistributedSystem getDistributedSystem() {
+ return this.system;
+ }
+
+ /**
+ * A "template factory" method for creating a
+ * <code>DistributedSystemHealthConfig</code>. It can be overridden
+ * by subclasses to produce instances of different
+ * <code>DistributedSystemHealthConfig</code> implementations.
+ */
+ protected DistributedSystemHealthConfig
+ createDistributedSystemHealthConfig() {
+
+ return new DistributedSystemHealthConfigImpl();
+ }
+
+ /**
+ * A "template factory" method for creating a
+ * <code>GemFireHealthConfig</code>. It can be overridden by
+ * subclasses to produce instances of different
+ * <code>GemFireHealthConfig</code> implementations.
+ *
+ * @param hostName
+ * The host whose health we are configuring
+ */
+ protected GemFireHealthConfig
+ createGemFireHealthConfig(String hostName) {
+
+ return new GemFireHealthConfigImpl(hostName);
+ }
+
+ /**
+ * Throws an {@link IllegalStateException} if this
+ * <code>GemFireHealthImpl</code> is closed.
+ */
+ private void checkClosed() {
+ if (this.isClosed) {
+ throw new IllegalStateException(LocalizedStrings.GemFireHealthImpl_CANNOT_ACCESS_A_CLOSED_GEMFIREHEALTH_INSTANCE.toLocalizedString());
+ }
+ }
+
+ /**
+ * Returns the overall health of GemFire. Note that this method
+ * does not contact any of the member VMs. Instead, it relies on
+ * the members to alert it of changes in its health via a {@link
+ * HealthListener}.
+ */
+ public GemFireHealth.Health getHealth() {
+ checkClosed();
+ return this.overallHealth;
+ }
+
+ /**
+ * Resets the overall health to be {@link #GOOD_HEALTH}. It also
+ * resets the health in the member VMs.
+ *
+ * @see GemFireVM#resetHealthStatus
+ */
+ public void resetHealth() {
+ checkClosed();
+
+ this.overallHealth = GOOD_HEALTH;
+ this.okayHealth.clear();
+ this.poorHealth.clear();
+
+ synchronized (this) {
+ for (Iterator iter = hostMembers.values().iterator();
+ iter.hasNext(); ) {
+ List members = (List) iter.next();
+ for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
+ GemFireVM member = (GemFireVM) iter2.next();
+ member.resetHealthStatus();
+ }
+ }
+ }
+ }
+
+ /**
+ * Aggregates the diagnoses from all members of the distributed
+ * system.
+ */
+ public String getDiagnosis() {
+ checkClosed();
+
+ StringBuffer sb = new StringBuffer();
+
+ synchronized (this) {
+ for (Iterator iter = hostMembers.values().iterator();
+ iter.hasNext(); ) {
+ List members = (List) iter.next();
+ for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
+ GemFireVM member = (GemFireVM) iter2.next();
+ String[] diagnoses =
+ member.getHealthDiagnosis(this.overallHealth);
+ for (int i = 0; i < diagnoses.length; i++) {
+ sb.append(diagnoses[i]).append("\n");;
+ }
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Starts a new {@link DistributedSystemHealthMonitor}
+ */
+ public void setDistributedSystemHealthConfig(DistributedSystemHealthConfig
+ config) {
+ synchronized (this.hostConfigs) {
+ // If too many threads are changing the health config, then we
+ // will might get an OutOfMemoryError trying to start a new
+ // health monitor thread.
+
+ if (this.dsHealthMonitor != null) {
+ this.dsHealthMonitor.stop();
+ }
+
+ this.dsHealthConfig = config;
+
+ DistributedSystemHealthEvaluator eval =
+ new DistributedSystemHealthEvaluator(config, this.agent.getDM());
+ int interval =
+ this.getDefaultGemFireHealthConfig().getHealthEvaluationInterval();
+ this.dsHealthMonitor =
+ new DistributedSystemHealthMonitor(eval, this, interval);
+ this.dsHealthMonitor.start();
+ }
+ }
+
+ public DistributedSystemHealthConfig
+ getDistributedSystemHealthConfig() {
+
+ checkClosed();
+ return this.dsHealthConfig;
+ }
+
+ public GemFireHealthConfig getDefaultGemFireHealthConfig() {
+ checkClosed();
+ return this.defaultConfig;
+ }
+
+ public void setDefaultGemFireHealthConfig(GemFireHealthConfig config) {
+ checkClosed();
+
+ if (config.getHostName() != null) {
+ throw new IllegalArgumentException(LocalizedStrings.GemFireHealthImpl_THE_GEMFIREHEALTHCONFIG_FOR_FOR_0_CANNOT_SERVE_AS_THE_DEFAULT_HEALTH_CONFIG.toLocalizedString(config.getHostName()));
+ }
+
+ this.defaultConfig = config;
+
+ synchronized (this) {
+ for (Iterator iter = this.hostMembers.entrySet().iterator();
+ iter.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ InetAddress hostIpAddress = (InetAddress) entry.getKey();
+ List members = (List) entry.getValue();
+
+ GemFireHealthConfig hostConfig =
+ (GemFireHealthConfig) hostConfigs.get(hostIpAddress);
+ if (hostConfig == null) {
+ hostConfig = config;
+ }
+
+ for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
+ GemFireVM member = (GemFireVM) iter2.next();
+ Assert.assertTrue(member.getHost().equals(hostIpAddress));
+ member.addHealthListener(this, hostConfig);
+ }
+ }
+ }
+
+ // We only need to do this if the health monitoring interval has
+ // change. This is probably not the most efficient way of doing
+ // things.
+ if (this.dsHealthConfig != null) {
+ setDistributedSystemHealthConfig(this.dsHealthConfig);
+ }
+ }
+
+ /**
+ * Returns the GemFireHealthConfig object for the given host name.
+ *
+ * @param hostName
+ * host name for which the GemFire Health Config is needed
+ *
+ * @throws IllegalArgumentException
+ * if host with given name could not be found
+ */
+ public synchronized GemFireHealthConfig
+ getGemFireHealthConfig(String hostName){
+
+ checkClosed();
+
+ InetAddress hostIpAddress = null;
+ try {
+ hostIpAddress = InetAddress.getByName(hostName);
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException(
+ LocalizedStrings.GemFireHealthImpl_COULD_NOT_FIND_A_HOST_WITH_NAME_0
+ .toLocalizedString(hostName), e);
+ }
+
+ GemFireHealthConfig config =
+ (GemFireHealthConfig) this.hostConfigs.get(hostIpAddress);
+ if (config == null) {
+ config = createGemFireHealthConfig(hostName);
+ this.hostConfigs.put(hostIpAddress, config);
+ }
+
+ return config;
+ }
+
+ /**
+ * Sets the GemFireHealthConfig object for the given host name.
+ *
+ * @param hostName
+ * host name for which the GemFire Health Config is needed
+ * @param config
+ * GemFireHealthConfig object to set
+ *
+ * @throws IllegalArgumentException
+ * if (1) given host name & the host name in the given config do not
+ * match OR (2) host with given name could not be found OR (3) there
+ * are no GemFire components running on the given host
+ */
+ public void setGemFireHealthConfig(String hostName,
+ GemFireHealthConfig config) {
+ checkClosed();
+
+ synchronized (this) {
+ String configHost = config.getHostName();
+ if (configHost == null || !configHost.equals(hostName)) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("The GemFireHealthConfig configures ");
+ if (configHost == null) {
+ sb.append("the default host ");
+
+ } else {
+ sb.append("host \"");
+ sb.append(config.getHostName());
+ sb.append("\" ");
+ }
+ sb.append("not \"" + hostName + "\"");
+ throw new IllegalArgumentException(sb.toString());
+ }
+ InetAddress hostIpAddress = null;
+ try {
+ hostIpAddress = InetAddress.getByName(hostName);
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException(
+ LocalizedStrings.GemFireHealthImpl_COULD_NOT_FIND_A_HOST_WITH_NAME_0
+ .toLocalizedString(hostName), e);
+ }
+
+ List members = (List) this.hostMembers.get(hostIpAddress);
+ if (members == null || members.isEmpty()) {
+ throw new IllegalArgumentException(
+ LocalizedStrings.GemFireHealthImpl_THERE_ARE_NO_GEMFIRE_COMPONENTS_ON_HOST_0
+ .toLocalizedString(hostName));
+ }
+
+ for (Iterator iter = members.iterator(); iter.hasNext(); ) {
+ GemFireVM member = (GemFireVM) iter.next();
+ member.addHealthListener(this, config);
+ }
+ }
+ }
+
+ /**
+ * Tells the members of the distributed system that we are no longer
+ * interested in monitoring their health.
+ *
+ * @see GemFireVM#removeHealthListener
+ */
+ public void close(){
+ this.agent.removeJoinLeaveListener(this);
+
+ synchronized (this) {
+ if (this.isClosed) {
+ return;
+ }
+
+ this.isClosed = true;
+
+ if (this.dsHealthMonitor != null) {
+ this.dsHealthMonitor.stop();
+ this.dsHealthMonitor = null;
+ }
+
+ try {
+ for (Iterator iter = hostMembers.values().iterator();
+ iter.hasNext(); ) {
+ List members = (List) iter.next();
+ for (Iterator iter2 = members.iterator(); iter2.hasNext(); ) {
+ GemFireVM member = (GemFireVM) iter2.next();
+ member.removeHealthListener();
+ }
+ }
+ } catch (CancelException e) {
+ // if the DS is disconnected, stop trying to distribute to other members
+ }
+
+ hostConfigs.clear();
+ hostMembers.clear();
+ okayHealth.clear();
+ poorHealth.clear();
+ }
+ }
+
+ public boolean isClosed() {
+ return this.isClosed;
+ }
+
+ /**
+ * Makes note of the newly-joined member
+ */
+ private void noteNewMember(GemFireVM member) {
+ InetAddress hostIpAddress = member.getHost();
+ List members = (List) this.hostMembers.get(hostIpAddress);
+ if (members == null) {
+ members = new ArrayList();
+ this.hostMembers.put(hostIpAddress, members);
+ }
+ members.add(member);
+
+ }
+
+ public synchronized void nodeJoined(GfManagerAgent source,
+ GemFireVM joined) {
+ noteNewMember(joined);
+
+ InetAddress hostIpAddress = joined.getHost();
+
+ GemFireHealthConfig config =
+ (GemFireHealthConfig) this.hostConfigs.get(hostIpAddress);
+ if (config == null) {
+ config = this.getDefaultGemFireHealthConfig();
+ }
+ joined.addHealthListener(this, config);
+ }
+
+ /**
+ * Makes note of the newly-left member
+ */
+ public synchronized void nodeLeft(GfManagerAgent source,
+ GemFireVM left) {
+ InetAddress hostIpAddress = left.getHost();
+ List members = (List) this.hostMembers.get(hostIpAddress);
+ if (members != null) {
+ members.remove(left);
+ if (members.isEmpty()) {
+ // No more members on the host
+ this.hostConfigs.remove(hostIpAddress);
+ this.hostMembers.remove(hostIpAddress);
+ }
+ }
+
+ this.okayHealth.remove(left);
+ this.poorHealth.remove(left);
+
+ reevaluateHealth();
+ }
+
+ /**
+ * Does the same thing as {@link #nodeLeft}
+ */
+ public void nodeCrashed(GfManagerAgent source, GemFireVM crashed) {
+ nodeLeft(source, crashed);
+ }
+
+ /**
+ * Re-evaluates the overall health of GemFire
+ */
+ private void reevaluateHealth() {
+ if (!this.poorHealth.isEmpty()) {
+ this.overallHealth = POOR_HEALTH;
+
+ } else if (!this.okayHealth.isEmpty()) {
+ this.overallHealth = OKAY_HEALTH;
+
+ } else {
+ this.overallHealth = GOOD_HEALTH;
+ }
+ }
+
+ public void healthChanged(GemFireVM member, GemFireHealth.Health status) {
+ if (status == GOOD_HEALTH) {
+ this.okayHealth.remove(member);
+ this.poorHealth.remove(member);
+
+ } else if (status == OKAY_HEALTH) {
+ this.okayHealth.add(member);
+ this.poorHealth.remove(member);
+
+ } else if (status == POOR_HEALTH) {
+ this.okayHealth.remove(member);
+ this.poorHealth.add(member);
+
+ } else {
+ Assert.assertTrue(false, "Unknown health code: " + status);
+ }
+
+ reevaluateHealth();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InetAddressUtil.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InetAddressUtil.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InetAddressUtil.java
new file mode 100755
index 0000000..d5f76cf
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InetAddressUtil.java
@@ -0,0 +1,209 @@
+/*
+ * 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.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.GemFireIOException;
+import org.apache.geode.internal.Assert;
+import org.apache.geode.internal.net.SocketCreator;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+
+
+/**
+ * Provides static utilities for manipulating, validating, and converting
+ * InetAddresses and host strings.
+ *
+ * @since GemFire 3.5
+ */
+@Deprecated
+public class InetAddressUtil {
+
+ private static final Logger logger = LogService.getLogger();
+
+ /** InetAddress instance representing the local host */
+ public static final InetAddress LOCALHOST = createLocalHost();
+
+ public static final String LOOPBACK_ADDRESS =
+ SocketCreator.preferIPv6Addresses() ? "::1" : "127.0.0.1";
+
+ public static final InetAddress LOOPBACK =
+ InetAddressUtil.toInetAddress(LOOPBACK_ADDRESS);
+
+ /** Disallows InetAddressUtil instantiation. */
+ private InetAddressUtil() {}
+
+ /**
+ * Returns a string version of InetAddress which can be converted back to an
+ * InetAddress later. Essentially any leading slash is trimmed.
+ *
+ * @param val the InetAddress or String to return a formatted string of
+ * @return string version the InetAddress minus any leading slash
+ */
+ public static String toString(Object val) {
+ if (val instanceof String) {
+ return trimLeadingSlash((String) val);
+
+ } else if (val instanceof InetAddress) {
+ return ((InetAddress) val).getHostAddress();
+
+ } else {
+ return trimLeadingSlash(val.toString());
+ }
+ }
+
+ /**
+ * Converts the string host to an instance of InetAddress. Returns null if
+ * the string is empty. Fails Assertion if the conversion would result in
+ * <code>java.lang.UnknownHostException</code>.
+ * <p>
+ * Any leading slashes on host will be ignored.
+ *
+ * @param host string version the InetAddress
+ * @return the host converted to InetAddress instance
+ */
+ public static InetAddress toInetAddress(String host) {
+ if (host == null || host.length() == 0) {
+ return null;
+ }
+ try {
+ if (host.indexOf("/") > -1) {
+ return InetAddress.getByName(host.substring(host.indexOf("/") + 1));
+ }
+ else {
+ return InetAddress.getByName(host);
+ }
+ } catch (java.net.UnknownHostException e) {
+ logStackTrace(e);
+ Assert.assertTrue(false, "Failed to get InetAddress: " + host);
+ return null; // will never happen since the Assert will fail
+ }
+ }
+
+ /**
+ * Creates an InetAddress representing the local host. The checked exception
+ * <code>java.lang.UnknownHostException</code> is captured and results in
+ * an Assertion failure instead.
+ *
+ * @return InetAddress instance representing the local host
+ */
+ public static InetAddress createLocalHost() {
+ try {
+ return SocketCreator.getLocalHost();
+ } catch (java.net.UnknownHostException e) {
+ logStackTrace(e);
+ Assert.assertTrue(false, "Failed to get local host");
+ return null; // will never happen
+ }
+ }
+
+ /**
+ * Validates the host by making sure it can successfully be used to get an
+ * instance of InetAddress. If the host string is null, empty or would result
+ * in <code>java.lang.UnknownHostException</code> then null is returned.
+ * <p>
+ * Any leading slashes on host will be ignored.
+ *
+ * @param host string version the InetAddress
+ * @return the host converted to InetAddress instance
+ */
+ public static String validateHost(String host) {
+ if (host == null || host.length() == 0) {
+ return null;
+ }
+ try {
+ InetAddress.getByName(trimLeadingSlash(host));
+ return host;
+ } catch (java.net.UnknownHostException e) {
+ logStackTrace(e);
+ return null;
+ }
+ }
+
+ /** Returns true if host matches the LOCALHOST. */
+ public static boolean isLocalHost(Object host) {
+ if (host instanceof InetAddress) {
+ if (LOCALHOST.equals(host)) {
+ return true;
+ }
+ else {
+// InetAddress hostAddr = (InetAddress)host;
+ try {
+ Enumeration en=NetworkInterface.getNetworkInterfaces();
+ while(en.hasMoreElements()) {
+ NetworkInterface i=(NetworkInterface)en.nextElement();
+ for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) {
+ InetAddress addr=(InetAddress)en2.nextElement();
+ if (host.equals(addr)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ catch (SocketException e) {
+ throw new GemFireIOException(LocalizedStrings.InetAddressUtil_UNABLE_TO_QUERY_NETWORK_INTERFACE.toLocalizedString(), e);
+ }
+ }
+ }
+ else {
+ return isLocalHost(InetAddressUtil.toInetAddress(host.toString()));
+ }
+ }
+
+ /** Returns true if host matches the LOOPBACK (127.0.0.1). */
+ public static boolean isLoopback(Object host) {
+ if (host instanceof InetAddress) {
+ return LOOPBACK.equals(host);
+ }
+ else {
+ return isLoopback(InetAddressUtil.toInetAddress(host.toString()));
+ }
+ }
+
+ /** Returns a version of the value after removing any leading slashes */
+ private static String trimLeadingSlash(String value) {
+ if (value == null) return "";
+ while (value.indexOf("/") > -1) {
+ value = value.substring(value.indexOf("/") + 1);
+ }
+ return value;
+ }
+
+ /**
+ * Logs the stack trace for the given Throwable if logger is initialized else
+ * prints the stack trace using System.out. If logged the logs are logged at
+ * WARNING level.
+ *
+ * @param throwable
+ * Throwable to log stack trace for
+ */
+ private static void logStackTrace(Throwable throwable) {
+ AdminDistributedSystemImpl adminDS =
+ AdminDistributedSystemImpl.getConnectedInstance();
+
+ logger.warn(throwable.getMessage(), throwable);
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InternalManagedEntity.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InternalManagedEntity.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InternalManagedEntity.java
new file mode 100644
index 0000000..44e55e9
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/InternalManagedEntity.java
@@ -0,0 +1,105 @@
+/*
+ * 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.ManagedEntity;
+import org.apache.geode.internal.admin.api.ManagedEntityConfig;
+
+/**
+ * Provides internal-only functionality that is expected of all
+ * <code>ManagedEntity<code>s. This functionality is used by the
+ * {@link ManagedEntityController} to manage the entity.
+ *
+ * @since GemFire 4.0
+ */
+public interface InternalManagedEntity extends ManagedEntity {
+
+ /** The state of a managed entity is unknown. */
+ public static final int UNKNOWN = 10;
+
+ /** A managed entity is stopped */
+ public static final int STOPPED = 11;
+
+ /** A managed entity is stopping (being stopped) */
+ public static final int STOPPING = 12;
+
+ /** A managed entity is starting */
+ public static final int STARTING = 13;
+
+ /** A managed entity is running (is started) */
+ public static final int RUNNING = 14;
+
+ ////////////////////// Instance Methods //////////////////////
+
+ /**
+ * Returns the <code>ManagedEntityConfig</code> for this
+ * <code>ManagedEntity</code>.
+ */
+ public ManagedEntityConfig getEntityConfig();
+
+ /**
+ * Returns a brief description (such as "locator") of this managed
+ * entity.
+ */
+ public String getEntityType();
+
+ /**
+ * Returns the (local) command to execute in order to start this
+ * managed entity. The command includes the full path to the
+ * executable (include <code>$GEMFIRE/bin</code>) and any
+ * command-line arguments. It does not take the {@linkplain
+ * ManagedEntityConfig#getRemoteCommand remote command} into account.
+ */
+ public String getStartCommand();
+
+ /**
+ * Returns the (local) command to execute in order to stop this
+ * managed entity.
+ */
+ public String getStopCommand();
+
+ /**
+ * Returns the (local) command to execute in order to determine
+ * whether or not this managed entity is runing.
+ */
+ public String getIsRunningCommand();
+
+ /**
+ * Returns a descriptive, one-word, unique id for a newly-created
+ * <code>ManagedEntity</code>. This ensures that we do not have
+ * collisions in the ids of entities.
+ */
+ public String getNewId();
+
+ /**
+ * Returns the distributed system to which this managed entity
+ * belongs.
+ */
+ public AdminDistributedSystem getDistributedSystem();
+
+ /**
+ * Sets the state of this managed entity and informs threads that
+ * are waiting for a state change. See bug 32455.
+ *
+ * @return The previous state of this managed entity.
+ *
+ * @see #RUNNING
+ */
+ public int setState(int state);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/LogCollator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/LogCollator.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/LogCollator.java
new file mode 100755
index 0000000..f51e634
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/LogCollator.java
@@ -0,0 +1,137 @@
+/*
+ * 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.GfManagerAgent;
+import org.apache.geode.internal.admin.GemFireVM;
+import org.apache.geode.internal.admin.ApplicationVM;
+import org.apache.geode.internal.logging.MergeLogFiles;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class LogCollator {
+
+ private GfManagerAgent system;
+ private List logTails;
+
+ public LogCollator() {
+ }
+
+ public String collateLogs(GfManagerAgent system) {
+ try {
+ if (system == null) {
+ return "";
+ }
+ this.system = system;
+ this.logTails = new ArrayList();
+ gatherActiveLogs();
+ gatherInactiveLogs();
+ return mergeLogs();
+ }
+ finally {
+ this.system = null;
+ this.logTails = null;
+ }
+ }
+
+ // -------------------------------------------------------------------------
+
+ private String mergeLogs() {
+ // combine logs...
+ InputStream[] logFiles = new InputStream[this.logTails.size()];
+ String[] logFileNames = new String[logFiles.length];
+ for (int i = 0; i < this.logTails.size(); i++) {
+ Loglet loglet = (Loglet) this.logTails.get(i);
+ logFiles[i] = new ByteArrayInputStream(loglet.tail.getBytes());
+ logFileNames[i] = loglet.name;
+ }
+
+ // delegate to MergeLogFiles...
+ StringWriter writer = new StringWriter();
+ PrintWriter mergedLog = new PrintWriter(writer);
+ if (!MergeLogFiles.mergeLogFiles(logFiles, logFileNames, mergedLog)) {
+ return writer.toString();
+ }
+ else {
+ return "";
+ }
+ }
+
+ private void gatherActiveLogs() {
+ ApplicationVM[] runningsApps = this.system.listApplications();
+ for (int i = 0; i < runningsApps.length; i++) {
+ addLogFrom(runningsApps[i]);
+ }
+ }
+
+ private void gatherInactiveLogs() {
+ /* not yet supported....
+ if (useStopped) {
+ LogViewHelper helper = new LogViewHelper();
+ for (Iterator iter = stoppedNodes.iterator(); iter.hasNext(); ) {
+ Object adminEntity = iter.next();
+ helper.setAdminEntity(adminEntity);
+ try {
+ if (helper.logViewAvailable()) {
+ String[] logs = helper.getSystemLogs();
+ addTail(allTails, logs, adminEntity.toString());
+ }
+ } catch (Exception e) {
+ Service.getService().reportSystemError(e);
+ }
+ }
+ }
+ */
+ }
+
+ private void addLogFrom(GemFireVM vm) {
+ String name = null;
+ name = vm.toString();
+ String[] logs = vm.getSystemLogs();
+ addTail(name, logs);
+ }
+
+ private void addTail(String logName, String[] logs) {
+ if (logs.length > 0) {
+ String tail = (logs.length > 1) ? logs[1] : logs[0];
+ this.logTails.add(new Loglet(logName, tail));
+ }
+ }
+
+ /*
+ public void setUseStoppedManagers(boolean useStopped) {
+ this.useStopped = useStopped;
+ }
+ */
+
+ private static class Loglet {
+ String name;
+ String tail;
+ Loglet(String name, String tail) {
+ this.name = name;
+ this.tail = tail;
+ }
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ManagedEntityConfigImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ManagedEntityConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ManagedEntityConfigImpl.java
new file mode 100644
index 0000000..9dcf802
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ManagedEntityConfigImpl.java
@@ -0,0 +1,260 @@
+/*
+ * 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.ManagedEntityConfig;
+import org.apache.geode.internal.admin.GemFireVM;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.GemFireVersion;
+import org.apache.geode.internal.net.SocketCreator;
+
+import java.io.File;
+import java.net.*;
+
+/**
+ * The abstract superclass of objects that configure a managed entity
+ * such as a GemFire cache server or a distribution locator.
+ * It contains configuration state and behavior common to all managed
+ * entities.
+ *
+ * @since GemFire 4.0
+ */
+public abstract class ManagedEntityConfigImpl
+ implements ManagedEntityConfig {
+
+ /** The name of the host on which the managed entity runs */
+ private String host;
+
+ /** Directory in which the locator runs */
+ private String workingDirectory;
+
+ /** The directory in which GemFire is installed */
+ private String productDirectory;
+
+ /** Command used to launch locator on remote machine */
+ private String remoteCommand;
+
+ /** The managed entity configured by this object.
+ *
+ * @see #isReadOnly */
+ private InternalManagedEntity entity = null;
+
+ ///////////////////// Static Methods /////////////////////
+
+ /**
+ * Returns the {@linkplain InetAddress#getCanonicalHostName
+ * canonical name} of the local machine.
+ */
+ protected static String getLocalHostName() {
+ try {
+ return SocketCreator.getLocalHost().getCanonicalHostName();
+
+ } catch (UnknownHostException ex) {
+ IllegalStateException ex2 = new IllegalStateException(LocalizedStrings.ManagedEntityConfigImpl_COULD_NOT_DETERMINE_LOCALHOST.toLocalizedString());
+ ex2.initCause(ex);
+ throw ex2;
+ }
+ }
+
+ /**
+ * Returns the current working directory for this VM.
+ */
+ private static File getCurrentWorkingDirectory() {
+ File cwd = new File(System.getProperty("user.dir"));
+ return cwd.getAbsoluteFile();
+ }
+
+ /**
+ * Returns the location of the GemFire product installation. This
+ * is determined by finding the location of the gemfire jar
+ * and working backwards.
+ */
+ private static File getGemFireInstallation() {
+ URL url = GemFireVersion.getJarURL();
+ if (url == null) {
+ throw new IllegalStateException(LocalizedStrings.ManagedEntityConfigImpl_COULD_NOT_FIND_GEMFIREJAR.toLocalizedString());
+ }
+
+ File gemfireJar = new File(url.getPath());
+ File lib = gemfireJar.getParentFile();
+ File product = lib.getParentFile();
+
+ return product;
+ }
+
+ ////////////////////// Constructors //////////////////////
+
+ /**
+ * Creates a <code>ManagedEntityConfigImpl</code> with the default
+ * configuration.
+ */
+ protected ManagedEntityConfigImpl() {
+ this.host = getLocalHostName();
+ this.workingDirectory =
+ getCurrentWorkingDirectory().getAbsolutePath();
+ this.productDirectory =
+ getGemFireInstallation().getAbsolutePath();
+ this.remoteCommand = null; // Delegate to AdminDistributedSystem
+ }
+
+ /**
+ * Creates a new <code>ManagedEntityConfigImpl</code> based on the
+ * configuration of a running <code>GemFireVM</code>
+ */
+ protected ManagedEntityConfigImpl(GemFireVM vm) {
+ this.host = SocketCreator.getHostName(vm.getHost());
+ this.workingDirectory = vm.getWorkingDirectory().getAbsolutePath();
+ this.productDirectory = vm.getGemFireDir().getAbsolutePath();
+ this.remoteCommand = null;
+ }
+
+ /**
+ * A copy constructor that creates a new
+ * <code>ManagedEntityConfigImpl</code> with the same configuration
+ * as another <code>ManagedEntityConfig</code>.
+ */
+ protected ManagedEntityConfigImpl(ManagedEntityConfig other) {
+ this.host = other.getHost();
+ this.workingDirectory = other.getWorkingDirectory();
+ this.productDirectory = other.getProductDirectory();
+ this.remoteCommand = other.getRemoteCommand();
+ }
+
+ //////////////////// Instance Methods ////////////////////
+
+ /**
+ * Checks to see if this config object is "read only". If it is,
+ * then an {@link IllegalStateException} is thrown. It should be
+ * called by every setter method.
+ *
+ * @see #isReadOnly
+ */
+ public void checkReadOnly() {
+ if (this.isReadOnly()) {
+ throw new IllegalStateException(LocalizedStrings.ManagedEntityConfigImpl_THIS_CONFIGURATION_CANNOT_BE_MODIFIED_WHILE_ITS_MANAGED_ENTITY_IS_RUNNING.toLocalizedString());
+ }
+ }
+
+ /**
+ * Returns whether or not this <code>ManagedEntityConfigImpl</code>
+ * is read-only (can be modified).
+ */
+ protected boolean isReadOnly() {
+ return this.entity != null && this.entity.isRunning();
+ }
+
+ /**
+ * Sets the entity that is configured by this config object. Once
+ * the entity is running, the config object cannot be modified.
+ *
+ * @see #checkReadOnly
+ */
+ public void setManagedEntity(InternalManagedEntity entity) {
+ this.entity = entity;
+ }
+
+ /**
+ * Notifies any configuration listeners that this configuration has
+ * changed.
+ */
+ protected abstract void configChanged();
+
+ public String getHost() {
+ return this.host;
+ }
+
+ public void setHost(String host) {
+ checkReadOnly();
+ this.host = host;
+ configChanged();
+ }
+
+ public String getWorkingDirectory() {
+ String dir = this.workingDirectory;
+ return dir;
+ }
+
+ public void setWorkingDirectory(String workingDirectory) {
+ checkReadOnly();
+ this.workingDirectory = workingDirectory;
+ configChanged();
+ }
+
+ public String getProductDirectory() {
+ return this.productDirectory;
+ }
+
+ public void setProductDirectory(String productDirectory) {
+ checkReadOnly();
+ this.productDirectory = productDirectory;
+ configChanged();
+ }
+
+ public String getRemoteCommand() {
+ return this.remoteCommand;
+ }
+
+ public void setRemoteCommand(String remoteCommand) {
+ checkReadOnly();
+ this.remoteCommand = remoteCommand;
+ configChanged();
+ }
+
+ /**
+ * Validates this configuration.
+ *
+ * @throws IllegalStateException
+ * If this config is not valid
+ */
+ public void validate() {
+ if (InetAddressUtil.validateHost(this.host) == null) {
+ throw new IllegalStateException(LocalizedStrings.ManagedEntityConfigImpl_INVALID_HOST_0.toLocalizedString(this.host));
+ }
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ // Since all fields are immutable objects, no deep cloning is
+ // necessary.
+ ManagedEntityConfigImpl clone =
+ (ManagedEntityConfigImpl) super.clone();
+ clone.entity = null;
+ return clone;
+ }
+
+ @Override
+ public String toString() {
+ String className = this.getClass().getName();
+ int index = className.lastIndexOf('.');
+ className = className.substring(index + 1);
+
+ StringBuffer sb = new StringBuffer();
+ sb.append(className);
+
+ sb.append(" host=");
+ sb.append(this.getHost());
+ sb.append(" workingDirectory=");
+ sb.append(this.getWorkingDirectory());
+ sb.append(" productDirectory=");
+ sb.append(this.getProductDirectory());
+ sb.append(" remoteCommand=\"");
+ sb.append(this.getRemoteCommand());
+ sb.append("\"");
+
+ return sb.toString();
+ }
+}