blob: 67346663797cdc17f1269064609f8cef2419388f [file] [log] [blame]
/*
* 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.distributed;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.join;
import static org.apache.commons.lang3.StringUtils.lowerCase;
import static org.apache.geode.distributed.ConfigurationProperties.NAME;
import static org.apache.geode.internal.lang.ClassUtils.forName;
import static org.apache.geode.internal.lang.StringUtils.defaultString;
import static org.apache.geode.internal.lang.SystemUtils.CURRENT_DIRECTORY;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.geode.annotations.Immutable;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.distributed.internal.unsafe.RegisterSignalHandlerSupport;
import org.apache.geode.internal.AvailablePort;
import org.apache.geode.internal.GemFireVersion;
import org.apache.geode.internal.process.PidUnavailableException;
import org.apache.geode.internal.process.ProcessUtils;
import org.apache.geode.internal.util.ArgumentRedactor;
import org.apache.geode.internal.util.SunAPINotFoundException;
import org.apache.geode.logging.internal.OSProcess;
import org.apache.geode.util.internal.GeodeGlossary;
/**
* The AbstractLauncher class is a base class for implementing various launchers to construct and
* run different GemFire processes, like Cache Servers, Locators, Managers, HTTP servers and so on.
*
* @see java.lang.Comparable
* @see java.lang.Runnable
* @see org.apache.geode.lang.Identifiable
* @since GemFire 7.0
*/
public abstract class AbstractLauncher<T extends Comparable<T>> implements Runnable {
public static final String MEMBER_NAME_ERROR_MESSAGE =
"The member name of the %s must be provided as an argument to the launcher, or a path to gemfire.properties must be specified, which assumes the %s member name will be set using the name property.";
public static final String WORKING_DIRECTORY_OPTION_NOT_VALID_ERROR_MESSAGE =
"Specifying the --dir option is not valid when starting a %s with the %sLauncher.";
public static final String WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE =
"The working directory for the %s could not be found.";
@Immutable
protected static final Boolean DEFAULT_FORCE = Boolean.FALSE;
/**
* @deprecated This timeout is no longer needed.
*/
@Deprecated
protected static final long READ_PID_FILE_TIMEOUT_MILLIS = 2 * 1000;
public static final String DEFAULT_WORKING_DIRECTORY = CURRENT_DIRECTORY;
public static final String SIGNAL_HANDLER_REGISTRATION_SYSTEM_PROPERTY =
GeodeGlossary.GEMFIRE_PREFIX + "launcher.registerSignalHandlers";
protected static final String OPTION_PREFIX = "-";
private static final String SUN_SIGNAL_API_CLASS_NAME = "sun.misc.Signal";
private volatile boolean debug;
protected final transient AtomicBoolean running = new AtomicBoolean(false);
/**
* @deprecated Please use Log4J 2 instead.
*/
@Deprecated
protected Logger logger = Logger.getLogger(getClass().getName());
public AbstractLauncher() {
try {
if (Boolean.getBoolean(SIGNAL_HANDLER_REGISTRATION_SYSTEM_PROPERTY)) {
forName(SUN_SIGNAL_API_CLASS_NAME, new SunAPINotFoundException(
"WARNING!!! Not running a Sun JVM. Could not find the sun.misc.Signal class; Signal handling disabled."));
RegisterSignalHandlerSupport.registerSignalHandlers();
}
} catch (SunAPINotFoundException handled) {
info(handled.getMessage());
}
}
/**
* Asserts that the specified port is available on all network interfaces on this local system.
*
* @param port an integer indicating the network port to listen for client network requests.
* @throws BindException if the network port is not available.
*/
protected static void assertPortAvailable(final int port) throws BindException {
assertPortAvailable(null, port);
}
/**
* Asserts that the specified port is available on the specified network interface, indicated by
* it's assigned IP address, on this local system.
*
* @param bindAddress an InetAddress indicating the bounded network interface to determine whether
* the service port is available or not.
* @param port an integer indicating the network port to listen for client network requests.
* @throws BindException if the network address and port are not available. Address defaults to
* localhost (or all network interfaces on the local system) if null.
* @see org.apache.geode.internal.AvailablePort
*/
protected static void assertPortAvailable(final InetAddress bindAddress, final int port)
throws BindException {
if (!AvailablePort.isPortAvailable(port, AvailablePort.SOCKET, bindAddress)) {
throw new BindException(
String.format("Network is unreachable; port (%1$d) is not available on %2$s.", port,
bindAddress != null ? bindAddress.getCanonicalHostName() : "localhost"));
}
}
/**
* Determines whether the specified property with name is set to a value in the referenced
* Properties. The property is considered "set" if the String value of the property is not
* non-null, non-empty and non-blank. Therefore, the Properties may "have" a property with name,
* but having no value as determined by this method.
*
* @param properties the Properties used in determining whether the given property is set.
* @param propertyName a String indicating the name of the property to check if set.
* @return a boolean indicating whether the specified property with name has been given a value in
* the referenced Properties.
* @see java.util.Properties
*/
protected static boolean isSet(final Properties properties, final String propertyName) {
return isNotBlank(properties.getProperty(propertyName));
}
/**
* Loads the GemFire properties at the specified URL.
*
* @param url the URL to the gemfire.properties to load.
* @return a Properties instance populated with the gemfire.properties.
* @see java.net.URL
*/
protected static Properties loadGemFireProperties(final URL url) {
if (url == null) {
return new Properties();
}
Properties properties = new Properties();
try (FileReader reader = new FileReader(new File(url.toURI()))) {
properties.load(reader);
} catch (IOException | URISyntaxException handled) {
// not in the file system, try the classpath
loadGemFirePropertiesFromClassPath(properties);
}
return properties;
}
private static void loadGemFirePropertiesFromClassPath(Properties properties) {
try {
properties
.load(AbstractLauncher.class.getResourceAsStream(DistributedSystem.getPropertiesFile()));
} catch (IOException | NullPointerException handled) {
// leave the properties empty
}
}
/**
* Determines whether the Locator launcher is in debug mode.
*
* @return a boolean to indicate whether the Locator launcher is in debug mode.
* @see #setDebug(boolean)
*/
public boolean isDebugging() {
return this.debug;
}
/**
* Sets the debug mode of the GemFire launcher class. This mutable property of the launcher
* enables the user to turn the debug mode on and off programmatically.
*
* @param debug a boolean used to enable or disable debug mode.
* @see #isDebugging()
*/
public void setDebug(final boolean debug) {
this.debug = debug;
}
/**
* Determines whether the Locator referenced by this launcher is running.
*
* @return a boolean valued indicating if the referenced Locator is running.
*/
public boolean isRunning() {
return this.running.get();
}
/**
* Creates a Properties object with configuration settings that the launcher has that should take
* precedence over anything the user has defined in their gemfire properties file.
*
* @return a Properties object with GemFire properties that the launcher has defined.
* @see #getDistributedSystemProperties(java.util.Properties)
* @see java.util.Properties
*/
protected Properties getDistributedSystemProperties() {
return getDistributedSystemProperties(null);
}
/**
* Creates a Properties object with configuration settings that the launcher has that should take
* precedence over anything the user has defined in their gemfire properties file.
*
* @param defaults default GemFire Distributed System properties as configured in the Builder.
* @return a Properties object with GemFire properties that the launcher has defined.
* @see java.util.Properties
*/
protected Properties getDistributedSystemProperties(final Properties defaults) {
final Properties distributedSystemProperties = new Properties();
if (defaults != null) {
distributedSystemProperties.putAll(defaults);
}
if (isNotBlank(getMemberName())) {
distributedSystemProperties.setProperty(NAME, getMemberName());
}
return distributedSystemProperties;
}
/**
* Gets a File reference with the path to the log file for the process.
*
* @return a File reference to the path of the log file for the process.
*/
protected File getLogFile() {
return new File(getWorkingDirectory(), getLogFileName());
}
/**
* Gets the fully qualified canonical path of the log file for the process.
*
* @return a String value indicating the canonical path of the log file for the process.
*/
protected String getLogFileCanonicalPath() {
try {
return getLogFile().getCanonicalPath();
} catch (IOException handled) {
return getLogFileName();
}
}
/**
* Gets the name of the log file used to log information about this GemFire service.
*
* @return a String value indicating the name of this GemFire service's log file.
*/
public abstract String getLogFileName();
/**
* Gets the name or ID of the member in the GemFire distributed system. This method prefers name
* if specified, otherwise the ID is returned. If name was not specified to the Builder that
* created this Launcher and this call is not in-process, then null is returned.
*
* @return a String value indicating the member's name if specified, otherwise the member's ID is
* returned if this call is made in-process, or finally, null is returned if neither name
* name was specified or the call is out-of-process.
* @see #getMemberName()
* @see #getMemberId()
*/
public String getMember() {
if (isNotBlank(getMemberName())) {
return getMemberName();
}
if (isNotBlank(getMemberId())) {
return getMemberId();
}
return null;
}
/**
* Gets the ID of the member in the GemFire distributed system as determined and assigned by
* GemFire when the member process joins the distributed system. Note, this call only works if the
* API is used in-process.
*
* @return a String value indicating the ID of the member in the GemFire distributed system.
*/
public String getMemberId() {
final InternalDistributedSystem distributedSystem =
InternalDistributedSystem.getConnectedInstance();
return distributedSystem != null ? distributedSystem.getMemberId() : null;
}
/**
* Gets the name of the member in the GemFire distributed system as determined by the 'name'
* GemFire property. Note, this call only works if the API is used in-process.
*
* @return a String value indicating the name of the member in the GemFire distributed system.
*/
public String getMemberName() {
final InternalDistributedSystem distributedSystem =
InternalDistributedSystem.getConnectedInstance();
return distributedSystem != null ? distributedSystem.getConfig().getName() : null;
}
/**
* Gets the user-specified process ID (PID) of the running GemFire service that AbstractLauncher
* implementations can use to determine status, or stop the service.
*
* @return an Integer value indicating the process ID (PID) of the running GemFire service.
*/
public abstract Integer getPid();
/**
* Gets the name of the GemFire service.
*
* @return a String indicating the name of the GemFire service.
*/
public abstract String getServiceName();
/**
* Gets the working directory pathname in which the process will be run.
*
* @return a String value indicating the pathname of the Server's working directory.
*/
public String getWorkingDirectory() {
return DEFAULT_WORKING_DIRECTORY;
}
/**
* Prints the specified debug message to standard err, replacing any placeholder values with the
* specified arguments on output, if debugging has been enabled.
*
* @param message the String value written to standard err.
* @param args an Object array containing arguments to replace the placeholder values in the
* message.
* @see java.lang.System#err
* @see #isDebugging()
* @see #debug(Throwable)
* @see #info(Object, Object...)
*/
protected void debug(final String message, final Object... args) {
if (isDebugging()) {
if (args != null && args.length > 0) {
System.err.printf(message, args);
} else {
System.err.print(message);
}
}
}
/**
* Prints the stack trace of the given Throwable to standard err if debugging has been enabled.
*
* @param t the Throwable who's stack trace is printed to standard err.
* @see java.lang.System#err
* @see #isDebugging()
* @see #debug(String, Object...)
*/
protected void debug(final Throwable t) {
if (isDebugging()) {
t.printStackTrace(System.err);
}
}
/**
* Prints the specified informational message to standard err, replacing any placeholder values
* with the specified arguments on output.
*
* @param message the String value written to standard err.
* @param args an Object array containing arguments to replace the placeholder values in the
* message.
* @see java.lang.System#err
* @see #debug(String, Object...)
*/
protected void info(final Object message, final Object... args) {
if (args != null && args.length > 0) {
System.err.printf(message.toString(), args);
} else {
System.err.print(message);
}
}
/**
* Redirects the standard out and standard err to the configured log file as specified in the
* GemFire distributed system properties.
*
* @param distributedSystem the GemFire model for a distributed system.
* @throws IOException if the standard out and err redirection was unsuccessful.
*/
protected void redirectOutput(final DistributedSystem distributedSystem) throws IOException {
if (distributedSystem instanceof InternalDistributedSystem) {
OSProcess
.redirectOutput(((InternalDistributedSystem) distributedSystem).getConfig().getLogFile());
}
}
/**
* Gets the version of GemFire currently running.
*
* @return a String representation of GemFire's version.
*/
public String version() {
return GemFireVersion.getGemFireVersion();
}
int identifyPid() throws PidUnavailableException {
return ProcessUtils.identifyPid();
}
int identifyPidOrNot() {
try {
return identifyPid();
} catch (PidUnavailableException handled) {
return -1;
}
}
boolean isPidInProcess() {
Integer pid = getPid();
return pid != null && pid == identifyPidOrNot();
}
/**
* The ServiceState is an immutable type representing the state of the specified Locator at any
* given moment in time. The ServiceState associates the Locator with it's state at the exact
* moment an instance of this class is constructed.
*/
public abstract static class ServiceState<T extends Comparable<T>> {
protected static final String JSON_CLASSPATH = "classpath";
protected static final String JSON_GEMFIREVERSION = "gemFireVersion";
protected static final String JSON_HOST = "bindAddress";
protected static final String JSON_JAVAVERSION = "javaVersion";
protected static final String JSON_JVMARGUMENTS = "jvmArguments";
protected static final String JSON_LOCATION = "location";
protected static final String JSON_LOGFILE = "logFileName";
protected static final String JSON_MEMBERNAME = "memberName";
protected static final String JSON_PID = "pid";
protected static final String JSON_PORT = "port";
protected static final String JSON_STATUS = "status";
protected static final String JSON_STATUSMESSAGE = "statusMessage";
protected static final String JSON_TIMESTAMP = "timestamp";
protected static final String JSON_UPTIME = "uptime";
protected static final String JSON_WORKINGDIRECTORY = "workingDirectory";
static final String TO_STRING_PROCESS_ID = "Process ID: ";
static final String TO_STRING_JAVA_VERSION = "Java Version: ";
static final String TO_STRING_LOG_FILE = "Log File: ";
static final String TO_STRING_JVM_ARGUMENTS = "JVM Arguments: ";
static final String TO_STRING_CLASS_PATH = "Class-Path: ";
static final String TO_STRING_UPTIME = "Uptime: ";
static final String TO_STRING_GEODE_VERSION = "Geode Version: ";
private static final String DATE_TIME_FORMAT_PATTERN = "MM/dd/yyyy hh:mm a";
private final Integer pid;
// NOTE the mutable non-Thread safe List is guarded by a call to Collections.unmodifiableList on
// initialization
private final List<String> jvmArguments;
private final Long uptime;
private final Status status;
private final String classpath;
private final String gemfireVersion;
private final String host;
private final String javaVersion;
private final String logFile;
private final String memberName;
private final String port;
private final String serviceLocation;
private final String statusMessage;
private final String workingDirectory;
private final Timestamp timestamp;
protected static String format(final Date timestamp) {
return timestamp == null ? ""
: new SimpleDateFormat(DATE_TIME_FORMAT_PATTERN).format(timestamp);
}
protected static Integer identifyPid() {
try {
return ProcessUtils.identifyPid();
} catch (PidUnavailableException handled) {
return null;
}
}
protected static String toDaysHoursMinutesSeconds(final Long milliseconds) {
final StringBuilder buffer = new StringBuilder();
if (milliseconds != null) {
long millisecondsRemaining = milliseconds;
final long days = TimeUnit.MILLISECONDS.toDays(millisecondsRemaining);
millisecondsRemaining -= TimeUnit.DAYS.toMillis(days);
final long hours = TimeUnit.MILLISECONDS.toHours(millisecondsRemaining);
millisecondsRemaining -= TimeUnit.HOURS.toMillis(hours);
final long minutes = TimeUnit.MILLISECONDS.toMinutes(millisecondsRemaining);
millisecondsRemaining -= TimeUnit.MINUTES.toMillis(minutes);
final long seconds = TimeUnit.MILLISECONDS.toSeconds(millisecondsRemaining);
if (days > 0) {
buffer.append(days).append(days > 1 ? " days " : " day ");
}
if (hours > 0) {
buffer.append(hours).append(hours > 1 ? " hours " : " hour ");
}
if (minutes > 0) {
buffer.append(minutes).append(minutes > 1 ? " minutes " : " minute ");
}
buffer.append(seconds).append(seconds == 0 || seconds > 1 ? " seconds" : " second");
}
return buffer.toString();
}
@SuppressWarnings("unchecked")
protected ServiceState(final Status status, final String statusMessage, final long timestamp,
final String serviceLocation, final Integer pid, final Long uptime,
final String workingDirectory, final List<String> jvmArguments, final String classpath,
final String gemfireVersion, final String javaVersion, final String logFile,
final String host, final String port, final String memberName) {
assert status != null : "The status of the GemFire service cannot be null!";
this.status = status;
this.statusMessage = statusMessage;
this.timestamp = new Timestamp(timestamp);
this.serviceLocation = serviceLocation;
this.pid = pid;
this.uptime = uptime;
this.workingDirectory = workingDirectory;
this.jvmArguments = Collections.unmodifiableList(jvmArguments);
this.classpath = classpath;
this.gemfireVersion = gemfireVersion;
this.javaVersion = javaVersion;
this.logFile = logFile;
this.host = host;
this.port = port;
this.memberName = memberName;
}
/**
* Marshals this state object into a JSON String.
*
* @return a String value containing the JSON representation of this state object.
*/
public String toJson() {
final Map<String, Object> map = new HashMap<>();
map.put(JSON_CLASSPATH, getClasspath());
map.put(JSON_GEMFIREVERSION, getGemFireVersion());
map.put(JSON_HOST, getHost());
map.put(JSON_JAVAVERSION, getJavaVersion());
map.put(JSON_JVMARGUMENTS, getJvmArguments());
map.put(JSON_LOCATION, getServiceLocation());
map.put(JSON_LOGFILE, getLogFile());
map.put(JSON_MEMBERNAME, getMemberName());
map.put(JSON_PID, getPid());
map.put(JSON_PORT, getPort());
map.put(JSON_STATUS, getStatus().getDescription());
map.put(JSON_STATUSMESSAGE, getStatusMessage());
map.put(JSON_TIMESTAMP, getTimestamp().getTime());
map.put(JSON_UPTIME, getUptime());
map.put(JSON_WORKINGDIRECTORY, getWorkingDirectory());
String jsonStatus = null;
try {
jsonStatus = new ObjectMapper().writeValueAsString(map);
} catch (JsonProcessingException e) {
// Ignored
}
return jsonStatus;
}
public static boolean isStartingNotRespondingOrNull(final ServiceState serviceState) {
return serviceState == null || serviceState.isStartingOrNotResponding();
}
public boolean isStartingOrNotResponding() {
return Status.NOT_RESPONDING == getStatus() || Status.STARTING == getStatus();
}
public boolean isVmWithProcessIdRunning() {
// note: this will use JNA if available or return false
return ProcessUtils.isProcessAlive(this.getPid());
}
/**
* Gets the Java classpath used when launching the GemFire service.
*
* @return a String value indicating the Java classpath used when launching the GemFire service.
* @see java.lang.System#getProperty(String) with 'java.class.path'
*/
public String getClasspath() {
return classpath;
}
/**
* Gets the version of GemFire used to launch and run the GemFire service.
*
* @return a String indicating the version of GemFire used in the running GemFire service.
*/
public String getGemFireVersion() {
return gemfireVersion;
}
/**
* Gets the version of Java used to launch and run the GemFire service.
*
* @return a String indicating the version of the Java runtime used in the running GemFire
* service.
* @see java.lang.System#getProperty(String) with 'java.verson'
*/
public String getJavaVersion() {
return javaVersion;
}
/**
* Gets the arguments passed to the JVM process that is running the GemFire service.
*
* @return a List of String value each representing an argument passed to the JVM of the GemFire
* service.
* @see java.lang.management.RuntimeMXBean#getInputArguments()
*/
public List<String> getJvmArguments() {
return jvmArguments;
}
/**
* Gets GemFire member's name for the process.
*
* @return a String indicating the GemFire member's name for the process.
*/
public String getMemberName() {
return this.memberName;
}
/**
* Gets the process ID of the running GemFire service if known, otherwise returns null.
*
* @return a integer value indicating the process ID (PID) of the running GemFire service, or
* null if the PID cannot be determined.
*/
public Integer getPid() {
return pid;
}
/**
* Gets the location of the GemFire service (usually the host in combination with the port).
*
* @return a String indication the location (such as host/port) of the GemFire service.
*/
public String getServiceLocation() {
return this.serviceLocation;
}
/**
* Gets the name of the GemFire service.
*
* @return a String indicating the name of the GemFire service.
*/
protected abstract String getServiceName();
/**
* Gets the state of the GemFire service.
*
* @return a Status enumerated type representing the state of the GemFire service.
* @see org.apache.geode.distributed.AbstractLauncher.Status
*/
public Status getStatus() {
return status;
}
/**
* Gets description of the the service's current state.
*
* @return a String describing the service's current state.
*/
public String getStatusMessage() {
return statusMessage;
}
/**
* The date and time the GemFire service was last in this state.
*
* @return a Timestamp signifying the last date and time the GemFire service was in this state.
* @see java.sql.Timestamp
*/
public Timestamp getTimestamp() {
return (Timestamp) timestamp.clone();
}
/**
* Gets the amount of time in milliseconds that the JVM process with the GemFire service has
* been running.
*
* @return a long value indicating the number of milliseconds that the GemFire service JVM has
* been running.
* @see java.lang.management.RuntimeMXBean#getUptime()
*/
public Long getUptime() {
return uptime;
}
/**
* Gets the directory in which the GemFire service is running. This is also the location where
* all GemFire service files (log files, the PID file, and so on) are written.
*
* @return a String value indicating the GemFire service's working (running) directory.
*/
public String getWorkingDirectory() {
return workingDirectory != null ? workingDirectory : DEFAULT_WORKING_DIRECTORY;
}
/**
* Gets the path of the log file for the process.
*
* @return a String value indicating the path of the log file for the process.
*/
public String getLogFile() {
return this.logFile;
}
/**
* Gets the host or IP address for the process and its service.
*
* @return a String value representing the host or IP address for the process and its service.
*/
public String getHost() {
return this.host;
}
/**
* Gets the port for the process and its service.
*
* @return an Integer value indicating the port for the process and its service.
*/
public String getPort() {
return this.port;
}
/**
* Gets a String describing the state of the GemFire service.
*
* @return a String describing the state of the GemFire service.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
switch (getStatus()) {
case STARTING:
sb.append("Starting %s in %s on %s as %s at %s").append(System.lineSeparator());
sb.append(TO_STRING_PROCESS_ID).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_JAVA_VERSION).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_LOG_FILE).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_JVM_ARGUMENTS).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_CLASS_PATH).append("%s");
return String.format(sb.toString(),
getServiceName(), getWorkingDirectory(), getServiceLocation(), getMemberName(),
toString(getTimestamp()), toString(getPid()), toString(getGemFireVersion()),
toString(getJavaVersion()), getLogFile(), ArgumentRedactor.redact(getJvmArguments()),
toString(getClasspath()));
case ONLINE:
sb.append("%s in %s on %s as %s is currently %s.").append(System.lineSeparator());
sb.append(TO_STRING_PROCESS_ID).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_UPTIME).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_GEODE_VERSION).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_JAVA_VERSION).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_LOG_FILE).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_JVM_ARGUMENTS).append("%s").append(System.lineSeparator());
sb.append(TO_STRING_CLASS_PATH).append("%s");
return String.format(sb.toString(),
getServiceName(), getWorkingDirectory(), getServiceLocation(), getMemberName(),
getStatus(), toString(getPid()), toDaysHoursMinutesSeconds(getUptime()),
toString(getGemFireVersion()), toString(getJavaVersion()), getLogFile(),
ArgumentRedactor.redact(getJvmArguments()), toString(getClasspath()));
case STOPPED:
sb.append("%s in %s on %s has been requested to stop.");
return String.format(sb.toString(),
getServiceName(), getWorkingDirectory(), getServiceLocation());
default: // NOT_RESPONDING
sb.append("%s in %s on %s is currently %s.");
return String.format(sb.toString(), getServiceName(),
getWorkingDirectory(), getServiceLocation(), getStatus());
}
}
// a timestamp (date/time) formatted as MM/dd/yyyy hh:mm a
protected String toString(final Date dateTime) {
return format(dateTime);
}
// the value of a Number as a String, or "" if null
protected String toString(final Number value) {
return defaultString(value);
}
// a String concatenation of all values separated by " "
protected String toString(final Object... values) {
return values == null ? "" : join(values, " ");
}
// the value of the String, or "" if value is null
protected String toString(final String value) {
return value != null ? value : "";
}
}
/**
* The Status enumerated type represents the various lifecycle states of a GemFire service (such
* as a Cache Server, a Locator or a Manager).
*/
public enum Status {
NOT_RESPONDING("not responding"),
ONLINE("online"),
STARTING("starting"),
STOPPED("stopped");
private final String description;
Status(final String description) {
assert isNotBlank(description) : "The Status description must be specified!";
this.description = lowerCase(description);
}
/**
* Looks up the Status enum type by description. The lookup operation is case-insensitive.
*
* @param description a String value describing the Locator's status.
* @return a Status enumerated type matching the description.
*/
public static Status valueOfDescription(final String description) {
for (Status status : values()) {
if (status.getDescription().equalsIgnoreCase(description)) {
return status;
}
}
return null;
}
/**
* Gets the description of the Status enum type.
*
* @return a String describing the Status enum type.
*/
public String getDescription() {
return description;
}
/**
* Gets a String representation of the Status enum type.
*
* @return a String representing the Status enum type.
* @see #getDescription()
*/
@Override
public String toString() {
return getDescription();
}
}
}