You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by up...@apache.org on 2016/02/20 01:00:01 UTC

[43/51] [abbrv] [partial] incubator-geode git commit: Merge remote-tracking branch 'origin/develop' into feature/GEODE-917

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/5beaaedc/geode-core/src/main/java/com/gemstone/gemfire/distributed/ServerLauncher.java
----------------------------------------------------------------------
diff --cc geode-core/src/main/java/com/gemstone/gemfire/distributed/ServerLauncher.java
index c991cc1,0000000..f13bb73
mode 100644,000000..100755
--- a/geode-core/src/main/java/com/gemstone/gemfire/distributed/ServerLauncher.java
+++ b/geode-core/src/main/java/com/gemstone/gemfire/distributed/ServerLauncher.java
@@@ -1,2605 -1,0 +1,2613 @@@
 +/*
 + * 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 com.gemstone.gemfire.distributed;
 +
 +import java.io.File;
 +import java.io.FileNotFoundException;
 +import java.io.IOException;
 +import java.lang.management.ManagementFactory;
 +import java.net.InetAddress;
 +import java.net.UnknownHostException;
 +import java.util.Arrays;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Properties;
 +import java.util.TreeMap;
 +import java.util.concurrent.TimeUnit;
 +import java.util.concurrent.TimeoutException;
 +import java.util.concurrent.atomic.AtomicBoolean;
 +import java.util.concurrent.atomic.AtomicReference;
 +
 +import javax.management.MalformedObjectNameException;
 +import javax.management.ObjectName;
 +
 +import com.gemstone.gemfire.SystemFailure;
 +import com.gemstone.gemfire.cache.Cache;
 +import com.gemstone.gemfire.cache.CacheFactory;
 +import com.gemstone.gemfire.cache.partition.PartitionRegionHelper;
 +import com.gemstone.gemfire.cache.server.CacheServer;
 +import com.gemstone.gemfire.distributed.AbstractLauncher.Status;
 +import com.gemstone.gemfire.distributed.internal.DistributionConfig;
 +import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
 +import com.gemstone.gemfire.internal.GemFireVersion;
 +import com.gemstone.gemfire.internal.SocketCreator;
 +import com.gemstone.gemfire.internal.cache.AbstractCacheServer;
 +import com.gemstone.gemfire.internal.cache.CacheConfig;
 +import com.gemstone.gemfire.internal.cache.CacheServerLauncher;
 +import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
 +import com.gemstone.gemfire.internal.cache.PartitionedRegion;
++import com.gemstone.gemfire.internal.cache.tier.sockets.CacheServerHelper;
 +import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
 +import com.gemstone.gemfire.internal.lang.ObjectUtils;
 +import com.gemstone.gemfire.internal.lang.StringUtils;
 +import com.gemstone.gemfire.internal.lang.SystemUtils;
 +import com.gemstone.gemfire.internal.process.ClusterConfigurationNotAvailableException;
 +import com.gemstone.gemfire.internal.process.ConnectionFailedException;
 +import com.gemstone.gemfire.internal.process.ControlNotificationHandler;
 +import com.gemstone.gemfire.internal.process.ControllableProcess;
 +import com.gemstone.gemfire.internal.process.FileAlreadyExistsException;
 +import com.gemstone.gemfire.internal.process.MBeanInvocationFailedException;
 +import com.gemstone.gemfire.internal.process.PidUnavailableException;
 +import com.gemstone.gemfire.internal.process.ProcessController;
 +import com.gemstone.gemfire.internal.process.ProcessControllerFactory;
 +import com.gemstone.gemfire.internal.process.ProcessControllerParameters;
 +import com.gemstone.gemfire.internal.process.ProcessLauncherContext;
 +import com.gemstone.gemfire.internal.process.ProcessType;
 +import com.gemstone.gemfire.internal.process.StartupStatusListener;
 +import com.gemstone.gemfire.internal.process.UnableToControlProcessException;
 +import com.gemstone.gemfire.internal.util.CollectionUtils;
 +import com.gemstone.gemfire.internal.util.IOUtils;
 +import com.gemstone.gemfire.lang.AttachAPINotFoundException;
 +import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings;
 +import com.gemstone.gemfire.management.internal.cli.json.GfJsonArray;
 +import com.gemstone.gemfire.management.internal.cli.json.GfJsonException;
 +import com.gemstone.gemfire.management.internal.cli.json.GfJsonObject;
 +import com.gemstone.gemfire.pdx.PdxSerializer;
 +
 +import joptsimple.OptionException;
 +import joptsimple.OptionParser;
 +import joptsimple.OptionSet;
 +
 +import org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer;
 +
 +/**
 + * The ServerLauncher class is a launcher class with main method to start a GemFire Server (implying a GemFire Cache
 + * Server process).
 + * 
 + * @author John Blum
 + * @author Kirk Lund
 + * @see com.gemstone.gemfire.distributed.AbstractLauncher
 + * @see com.gemstone.gemfire.distributed.LocatorLauncher
 + * @since 7.0
 + */
 +@SuppressWarnings({ "unused" })
 +public final class ServerLauncher extends AbstractLauncher<String> {
 +
 +  /**
 +   * @deprecated This is specific to the internal implementation and may go away in a future release.
 +   */
 +  protected static final Integer DEFAULT_SERVER_PORT = getDefaultServerPort();
 +
 +  private static final Map<String, String> helpMap = new HashMap<>();
 +
 +  static {
 +    helpMap.put("launcher", LocalizedStrings.ServerLauncher_SERVER_LAUNCHER_HELP.toLocalizedString());
 +    helpMap.put(Command.START.getName(), LocalizedStrings.ServerLauncher_START_SERVER_HELP.toLocalizedString(String.valueOf(getDefaultServerPort())));
 +    helpMap.put(Command.STATUS.getName(), LocalizedStrings.ServerLauncher_STATUS_SERVER_HELP.toLocalizedString());
 +    helpMap.put(Command.STOP.getName(), LocalizedStrings.ServerLauncher_STOP_SERVER_HELP.toLocalizedString());
 +    helpMap.put(Command.VERSION.getName(), LocalizedStrings.ServerLauncher_VERSION_SERVER_HELP.toLocalizedString());
 +    helpMap.put("assign-buckets", LocalizedStrings.ServerLauncher_SERVER_ASSIGN_BUCKETS_HELP.toLocalizedString());
 +    helpMap.put("debug", LocalizedStrings.ServerLauncher_SERVER_DEBUG_HELP.toLocalizedString());
 +    helpMap.put("dir", LocalizedStrings.ServerLauncher_SERVER_DIR_HELP.toLocalizedString());
 +    helpMap.put("disable-default-server", LocalizedStrings.ServerLauncher_SERVER_DISABLE_DEFAULT_SERVER_HELP.toLocalizedString());
 +    helpMap.put("force", LocalizedStrings.ServerLauncher_SERVER_FORCE_HELP.toLocalizedString());
 +    helpMap.put("help", LocalizedStrings.SystemAdmin_CAUSES_GEMFIRE_TO_PRINT_OUT_INFORMATION_INSTEAD_OF_PERFORMING_THE_COMMAND_THIS_OPTION_IS_SUPPORTED_BY_ALL_COMMANDS.toLocalizedString());
 +    helpMap.put("member", LocalizedStrings.ServerLauncher_SERVER_MEMBER_HELP.toLocalizedString());
 +    helpMap.put("pid", LocalizedStrings.ServerLauncher_SERVER_PID_HELP.toLocalizedString());
 +    helpMap.put("rebalance", LocalizedStrings.ServerLauncher_SERVER_REBALANCE_HELP.toLocalizedString());
 +    helpMap.put("redirect-output", LocalizedStrings.ServerLauncher_SERVER_REDIRECT_OUTPUT_HELP.toLocalizedString());
 +    helpMap.put("server-bind-address", LocalizedStrings.ServerLauncher_SERVER_BIND_ADDRESS_HELP.toLocalizedString());
 +    helpMap.put("hostname-for-clients", LocalizedStrings.ServerLauncher_SERVER_HOSTNAME_FOR_CLIENT_HELP.toLocalizedString());
 +    helpMap.put("server-port", LocalizedStrings.ServerLauncher_SERVER_PORT_HELP.toLocalizedString(String.valueOf(getDefaultServerPort())));
 +  }
 +
 +  private static final Map<Command, String> usageMap = new TreeMap<>();
 +
 +  static {
 +    usageMap.put(Command.START, "start <member-name> [--assign-buckets] [--disable-default-server] [--rebalance] [--server-bind-address=<IP-address>] [--server-port=<port>] [--force] [--debug] [--help]");
 +    usageMap.put(Command.STATUS, "status [--member=<member-ID/Name>] [--pid=<process-ID>] [--dir=<Server-working-directory>] [--debug] [--help]");
 +    usageMap.put(Command.STOP, "stop [--member=<member-ID/Name>] [--pid=<process-ID>] [--dir=<Server-working-directory>] [--debug] [--help]");
 +    usageMap.put(Command.VERSION, "version");
 +  }
 +
 +  /**
 +   * @deprecated This is specific to the internal implementation and may go away in a future release.
 +   */
 +  public static final String DEFAULT_SERVER_PID_FILE = "vf.gf.server.pid";
 +
 +  private static final String DEFAULT_SERVER_LOG_EXT = ".log";
 +  private static final String DEFAULT_SERVER_LOG_NAME = "gemfire";
 +  private static final String SERVER_SERVICE_NAME = "Server";
 +
 +  private static final AtomicReference<ServerLauncher> INSTANCE = new AtomicReference<>();
 +
 +  private volatile transient boolean debug;
 +
 +  private final transient ControlNotificationHandler controlHandler;
 +  
 +  private final AtomicBoolean starting = new AtomicBoolean(false);
 +
 +  private final boolean assignBuckets;
 +  private final boolean disableDefaultServer;
 +  private final boolean force;
 +  private final boolean help;
 +  private final boolean rebalance;
 +  private final boolean redirectOutput;
 +
 +  private volatile transient Cache cache;
 +
 +  private final transient CacheConfig cacheConfig;
 +
 +  private final Command command;
 +
 +  private final InetAddress serverBindAddress;
 +
 +  private final Integer pid;
 +  private final Integer serverPort;
 +
 +  private final Properties distributedSystemProperties;
 +
 +  private final String memberName;
 +  private final String springXmlLocation;
 +  private final String workingDirectory;
 +
 +  // NOTE in addition to debug, the other shared, mutable state
 +  private volatile transient String statusMessage;
 +  
 +  private final Float criticalHeapPercentage;
 +  private final Float evictionHeapPercentage;
 +  
 +  private final Float criticalOffHeapPercentage;
 +  private final Float evictionOffHeapPercentage;
 +  
 +  private final String hostNameForClients; 
 +  private final Integer maxConnections;
 +  private final Integer maxMessageCount;
 +  private final Integer messageTimeToLive;
 +  private final Integer socketBufferSize;
 +  
 +  private final Integer maxThreads;
 +
 +  private volatile transient ControllableProcess process;
 +
 +  private final transient ServerControllerParameters controllerParameters;
 +  
 +  /**
 +   * Launches a GemFire Server from the command-line configured with the given arguments.
 +   * 
 +   * @param args the command-line arguments used to configure the GemFire Server at runtime.
 +   */
 +  public static void main(final String... args) {
 +    try {
 +      new Builder(args).build().run();
 +    }
 +    catch (AttachAPINotFoundException e) {
 +      System.err.println(e.getMessage());
 +    }
 +  }
 +
 +  private static Integer getDefaultServerPort() {
 +    return Integer.getInteger(AbstractCacheServer.TEST_OVERRIDE_DEFAULT_PORT_PROPERTY, CacheServer.DEFAULT_PORT);
 +  }
 +
 +  /**
 +   * Gets the instance of the ServerLauncher used to launch the GemFire Cache Server, or null if this VM does not
 +   * have an instance of ServerLauncher indicating no GemFire Cache Server is running.
 +   * 
 +   * @return the instance of ServerLauncher used to launcher a GemFire Cache Server in this VM.
 +   */
 +  public static ServerLauncher getInstance() {
 +    return INSTANCE.get();
 +  }
 +
 +  /**
 +   * Gets the ServerState for this process or null if this process was not launched using this VM's
 +   * ServerLauncher reference .
 +   * 
 +   * @return the ServerState for this process or null.
 +   */
 +  public static ServerState getServerState() {
 +    return (getInstance() != null ? getInstance().status() : null);
 +  }
 +
 +  /**
 +   * Private constructor used to properly construct an immutable instance of the ServerLauncher using a Builder.
 +   * The Builder is used to configure a ServerLauncher instance.  The Builder can process user input from the
 +   * command-line or be used programmatically to properly construct an instance of the ServerLauncher using the API.
 +   * 
 +   * @param builder an instance of ServerLauncher.Builder for configuring and constructing an instance of the
 +   * ServerLauncher.
 +   * @see com.gemstone.gemfire.distributed.ServerLauncher.Builder
 +   */
 +  private ServerLauncher(final Builder builder) {
 +    this.cache = builder.getCache(); // testing
 +    this.cacheConfig = builder.getCacheConfig();
 +    this.command = builder.getCommand();
 +    this.assignBuckets = Boolean.TRUE.equals(builder.getAssignBuckets());
 +    setDebug(Boolean.TRUE.equals(builder.getDebug()));
 +    this.disableDefaultServer = Boolean.TRUE.equals(builder.getDisableDefaultServer());
 +    CacheServerLauncher.disableDefaultServer.set(this.disableDefaultServer);
 +    this.distributedSystemProperties = builder.getDistributedSystemProperties();
 +    this.force = Boolean.TRUE.equals(builder.getForce());
 +    this.help = Boolean.TRUE.equals(builder.getHelp());
 +    this.hostNameForClients = builder.getHostNameForClients();
 +    this.memberName = builder.getMemberName();
 +    // TODO:KIRK: set ThreadLocal for LogService with getLogFile or getLogFileName
 +    this.pid = builder.getPid();
 +    this.rebalance = Boolean.TRUE.equals(builder.getRebalance());
 +    this.redirectOutput = Boolean.TRUE.equals(builder.getRedirectOutput());
 +    this.serverBindAddress = builder.getServerBindAddress();
 +    if (builder.isServerBindAddressSetByUser() && this.serverBindAddress != null) {
 +      CacheServerLauncher.serverBindAddress.set(this.serverBindAddress.getHostAddress());
 +    }
 +    this.serverPort = builder.getServerPort();
 +    if (builder.isServerPortSetByUser() && this.serverPort != null) {
 +      CacheServerLauncher.serverPort.set(this.serverPort);
 +    }
 +    this.springXmlLocation = builder.getSpringXmlLocation();
 +    this.workingDirectory = builder.getWorkingDirectory();
 +    this.criticalHeapPercentage = builder.getCriticalHeapPercentage();
 +    this.evictionHeapPercentage = builder.getEvictionHeapPercentage();
 +    this.criticalOffHeapPercentage = builder.getCriticalOffHeapPercentage();
 +    this.evictionOffHeapPercentage = builder.getEvictionOffHeapPercentage();
 +    this.maxConnections = builder.getMaxConnections();
 +    this.maxMessageCount = builder.getMaxMessageCount();
 +    this.maxThreads = builder.getMaxThreads();
 +    this.messageTimeToLive = builder.getMessageTimeToLive();
 +    this.socketBufferSize = builder.getSocketBufferSize();
 +    this.controllerParameters = new ServerControllerParameters();
 +    this.controlHandler = new ControlNotificationHandler() {
 +      @Override
 +      public void handleStop() {
 +        if (isStoppable()) {
 +          stopInProcess();
 +        }
 +      }
 +      @Override
 +      public ServiceState<?> handleStatus() {
 +        return statusInProcess();
 +      }
 +    };
 +  }
 +
 +  /**
 +   * Gets a reference to the Cache that was created when the GemFire Server was started.
 +   * 
 +   * @return a reference to the Cache created by the GemFire Server start operation.
 +   * @see com.gemstone.gemfire.cache.Cache
 +   */
 +  final Cache getCache() {
 +    return this.cache;
 +  }
 +
 +  /**
 +   * Gets the CacheConfig object used to configure additional GemFire Cache components and features (e.g. PDX).
 +   *
 +   * @return a CacheConfig object with additional GemFire Cache configuration meta-data used on startup to configure
 +   * the Cache.
 +   */
 +  final CacheConfig getCacheConfig() {
 +    final CacheConfig copy = new CacheConfig();
 +    copy.setDeclarativeConfig(this.cacheConfig);
 +    return copy;
 +  }
 +
 +  /**
 +   * Gets an identifier that uniquely identifies and represents the Server associated with this launcher.
 +   * 
 +   * @return a String value identifier to uniquely identify the Server and it's launcher.
 +   * @see #getServerBindAddressAsString()
 +   * @see #getServerPortAsString()
 +   */
 +  public final String getId() {
 +    final StringBuilder buffer = new StringBuilder(ServerState.getServerBindAddressAsString(this));
 +    final String serverPort = ServerState.getServerPortAsString(this);
 +
 +    if (!StringUtils.isBlank(serverPort)) {
 +      buffer.append("[").append(serverPort).append("]");
 +    }
 +
 +    return buffer.toString();
 +  }
 +
 +  /**
 +   * Get the Server launcher command used to invoke the Server.
 +   * 
 +   * @return the Server launcher command used to invoke the Server.
 +   * @see com.gemstone.gemfire.distributed.ServerLauncher.Command
 +   */
 +  public Command getCommand() {
 +    return this.command;
 +  }
 +
 +  /**
 +   * Determines whether buckets should be assigned to partitioned regions in the cache upon Server start.
 +   * 
 +   * @return a boolean indicating if buckets should be assigned upon Server start.
 +   */
 +  public boolean isAssignBuckets() {
 +    return this.assignBuckets;
 +  }
 +
 +  /**
 +   * Determines whether a default cache server will be added when the GemFire Server comes online.
 +   * 
 +   * @return a boolean value indicating whether to add a default cache server.
 +   */
 +  public boolean isDisableDefaultServer() {
 +    return this.disableDefaultServer;
 +  }
 +
 +  /**
 +   * Determines whether the PID file is allowed to be overwritten when the Server is started and a PID file
 +   * already exists in the Server's specified working directory.
 +   * 
 +   * @return boolean indicating if force has been enabled.
 +   */
 +  public boolean isForcing() {
 +    return this.force;
 +  }
 +
 +  /**
 +   * Determines whether this launcher will be used to display help information.  If so, then none of the standard
 +   * Server launcher commands will be used to affect the state of the Server.  A launcher is said to be 'helping'
 +   * if the user entered the "--help" option (switch) on the command-line.
 +   * 
 +   * @return a boolean value indicating if this launcher is used for displaying help information.
 +   * @see com.gemstone.gemfire.distributed.ServerLauncher.Command
 +   */
 +  public boolean isHelping() {
 +    return this.help;
 +  }
 +
 +  /**
 +   * Determines whether a rebalance operation on the cache will occur upon starting the GemFire server using this
 +   * launcher.
 +   * 
 +   * @return a boolean indicating if the cache will be rebalance when the GemFire server starts.
 +   */
 +  public boolean isRebalancing() {
 +    return this.rebalance;
 +  }
 +
 +  /**
 +   * Determines whether this launcher will redirect output to system logs when
 +   * starting a new Locator process.
 +   * 
 +   * @return a boolean value indicating if this launcher will redirect output 
 +   * to system logs when starting a new Locator process
 +   */
 +  public boolean isRedirectingOutput() {
 +    return this.redirectOutput;
 +  }
 +
 +  /**
 +   * Gets the name of the log file used to log information about this Server.
 +   * 
 +   * @return a String value indicating the name of this Server's log file.
 +   */
 +  public String getLogFileName() {
 +    return StringUtils.defaultIfBlank(getMemberName(), DEFAULT_SERVER_LOG_NAME).concat(DEFAULT_SERVER_LOG_EXT);
 +  }
 +
 +  /**
 +   * Gets the name of this member (this Server) in the GemFire distributed system as determined by the 'name' GemFire
 +   * property.
 +   * 
 +   * @return a String indicating the name of the member (this Server) in the GemFire distributed system.
 +   * @see AbstractLauncher#getMemberName()
 +   */
 +  public String getMemberName() {
 +    return StringUtils.defaultIfBlank(this.memberName, super.getMemberName());
 +  }
 +
 +  /**
 +   * Gets the user-specified process ID (PID) of the running Server that ServerLauncher uses to issue status and
 +   * stop commands to the Server.
 +   * 
 +   * @return an Integer value indicating the process ID (PID) of the running Server.
 +   */
 +  @Override
 +  public Integer getPid() {
 +    return this.pid;
 +  }
 +
 +  /**
 +   * Gets the GemFire Distributed System (cluster) Properties.
 +   *
 +   * @return a Properties object containing the configuration settings for the GemFire Distributed System (cluster).
 +   * @see java.util.Properties
 +   */
 +  public Properties getProperties() {
 +    return (Properties) this.distributedSystemProperties.clone();
 +  }
 +
 +  /**
 +   * Gets the IP address to which the Server is bound listening for and accepting cache client connections.  This
 +   * property should not be confused with 'bindAddress' ServerLauncher property, which is the port for binding the
 +   * Server's ServerSocket used in distribution and messaging between the peers of the GemFire distributed system.
 +   * 
 +   * @return an InetAddress indicating the IP address that the Server is bound to listening for and accepting cache
 +   * client connections in a client/server topology.
 +   */
 +  public InetAddress getServerBindAddress() {
 +    return this.serverBindAddress;
 +  }
 +
 +  /**
 +   * Gets the host, as either hostname or IP address, on which the Server was bound and running.  An attempt is made
 +   * to get the canonical hostname for IP address to which the Server was bound for accepting client requests.  If
 +   * the server bind address is null or localhost is unknown, then a default String value of "localhost/127.0.0.1"
 +   * is returned.
 +   * 
 +   * Note, this information is purely information and should not be used to re-construct state or for
 +   * other purposes.
 +   * 
 +   * @return the hostname or IP address of the host running the Server, based on the bind-address, or
 +   * 'localhost/127.0.0.1' if the bind address is null and localhost is unknown.
 +   * @see java.net.InetAddress
 +   * @see #getServerBindAddress()
 +   */
 +  public String getServerBindAddressAsString() {
 +    try {
 +      if (getServerBindAddress() != null) {
 +        return getServerBindAddress().getCanonicalHostName();
 +      }
 +
 +      final InetAddress localhost = SocketCreator.getLocalHost();
 +
 +      return localhost.getCanonicalHostName();
 +    }
 +    catch (UnknownHostException ignore) {
 +      // TODO determine a better value for the host on which the Server is running to return here...
 +      // NOTE returning localhost/127.0.0.1 implies the serverBindAddress was null and no IP address for localhost
 +      // could be found
 +      return "localhost/127.0.0.1";
 +    }
 +  }
 +
 +  /**
 +   * Gets the port on which the Server is listening for cache client connections.  This property should not be confused
 +   * with the 'port' ServerLauncher property, which is used by the Server to set the 'tcp-port' distribution config
 +   * property and is used by the ServerSocket for peer distribution and messaging.
 +   * 
 +   * @return an Integer value indicating the port the Server is listening on for cache client connections in the
 +   * client/server topology.
 +   */
 +  public Integer getServerPort() {
 +    return this.serverPort;
 +  }
 +
 +  /**
 +   * Gets the server port on which the Server is listening for client requests represented as a String value.
 +   * 
 +   * @return a String representing the server port on which the Server is listening for client requests.
 +   * @see #getServerPort()
 +   */
 +  public String getServerPortAsString() {
 +    return ObjectUtils.defaultIfNull(getServerPort(), getDefaultServerPort()).toString();
 +  }
 +
 +  /**
 +   * Gets the name for a GemFire Server.
 +   * 
 +   * @return a String indicating the name for a GemFire Server.
 +   */
 +  public String getServiceName() {
 +    return SERVER_SERVICE_NAME;
 +  }
 +
 +  /**
 +   * Gets the location of the Spring XML configuration meta-data file used to bootstrap, configure and initialize
 +   * the GemFire Server on start.
 +   * <p>
 +   * @return a String indicating the location of the Spring XML configuration file.
 +   * @see com.gemstone.gemfire.distributed.ServerLauncher.Builder#getSpringXmlLocation()
 +   */
 +  public String getSpringXmlLocation() {
 +    return this.springXmlLocation;
 +  }
 +
 +  /**
 +   * Determines whether this GemFire Server was configured and initialized with Spring configuration meta-data.
 +   * <p>
 +   * @return a boolean value indicating whether this GemFire Server was configured with Spring configuration meta-data.
 +   */
 +  public boolean isSpringXmlLocationSpecified() {
 +    return !StringUtils.isBlank(this.springXmlLocation);
 +  }
 +
 +  /**
 +   * Gets the working directory pathname in which the Server will be run.
 +   * 
 +   * @return a String value indicating the pathname of the Server's working directory.
 +   */
 +  @Override
 +  public String getWorkingDirectory() {
 +    return this.workingDirectory;
 +  }
 +  
 +  public Float getCriticalHeapPercentage() {
 +    return this.criticalHeapPercentage;
 +  }
 +  
 +  public Float getEvictionHeapPercentage() {
 +    return this.evictionHeapPercentage;
 +  }
 +  
 +  public Float getCriticalOffHeapPercentage() {
 +    return this.criticalOffHeapPercentage;
 +  }
 +  
 +  public Float getEvictionOffHeapPercentage() {
 +    return this.evictionOffHeapPercentage;
 +  }
 +  
 +  public String getHostNameForClients() {
 +    return this.hostNameForClients;
 +  }
 +
 +  public Integer getMaxConnections() {
 +    return this.maxConnections;
 +  }
 +
 +  public Integer getMaxMessageCount() {
 +    return this.maxMessageCount;
 +  }
 +
 +  public Integer getMessageTimeToLive() {
 +    return this.messageTimeToLive;
 +  }
 +
 +  public Integer getMaxThreads() {
 +    return this.maxThreads;
 +  }
 +
 +  public Integer getSocketBufferSize() {
 +    return this.socketBufferSize;
 +  }
 +  
 +  /**
 +   * Displays help for the specified Server launcher command to standard err.  If the Server launcher command
 +   * is unspecified, then usage information is displayed instead.
 +   * 
 +   * @param command the Server launcher command in which to display help information.
 +   * @see #usage()
 +   */
 +  public void help(final Command command) {
 +    if (Command.isUnspecified(command)) {
 +      usage();
 +    }
 +    else {
 +      info(StringUtils.wrap(helpMap.get(command.getName()), 80, ""));
 +      info("\n\nusage: \n\n");
 +      info(StringUtils.wrap("> java ... " + getClass().getName() + " " + usageMap.get(command), 80, "\t\t"));
 +      info("\n\noptions: \n\n");
 +
 +      for (final String option : command.getOptions()) {
 +        info(StringUtils.wrap("--" + option + ": " + helpMap.get(option) + "\n", 80, "\t"));
 +      }
 +
 +      info("\n\n");
 +    }
 +  }
 +
 +  /**
 +   * Displays usage information on the proper invocation of the ServerLauncher from the command-line to standard err.
 +   * 
 +   * @see #help(com.gemstone.gemfire.distributed.ServerLauncher.Command)
 +   */
 +  public void usage() {
 +    info(StringUtils.wrap(helpMap.get("launcher"), 80, "\t"));
 +    info("\n\nSTART\n\n");
 +    help(Command.START);
 +    info("STATUS\n\n");
 +    help(Command.STATUS);
 +    info("STOP\n\n");
 +    help(Command.STOP);
 +  }
 +
 +  /**
 +   * A Runnable method used to invoke the GemFire server (cache server) with the specified command.  From run, a user
 +   * can invoke 'start', 'status', 'stop' and 'version'.  Note, that 'version' is also a command-line option, but can
 +   * be treated as a "command" as well.
 +   * 
 +   * @see java.lang.Runnable
 +   */
 +  @Override
 +  public void run() {
 +    if (!isHelping()) {
 +      switch (getCommand()) {
 +        case START:
 +          info(start());
 +          waitOnServer();
 +          break;
 +        case STATUS:
 +          info(status());
 +          break;
 +        case STOP:
 +          info(stop());
 +          break;
 +        case VERSION:
 +          info(version());
 +          break;
 +        default:
 +          usage();
 +      }
 +    }
 +    else {
 +      help(getCommand());
 +    }
 +  }
 +
 +  /**
 +   * Gets a File reference with the path to the PID file for the Server.
 +   * 
 +   * @return a File reference to the path of the Server's PID file.
 +   */
 +  protected File getServerPidFile() {
 +    return new File(getWorkingDirectory(), ProcessType.SERVER.getPidFileName());
 +  }
 +  
 +  /**
 +   * Determines whether a GemFire Cache Server can be started with this instance of ServerLauncher.
 +   *
 +   * @return a boolean indicating whether a GemFire Cache Server can be started with this instance of ServerLauncher,
 +   * which is true if the ServerLauncher has not already started a Server or a Server is not already running.
 +   * @see #start()
 +   */
 +  private boolean isStartable() {
 +    return (!isRunning() && this.starting.compareAndSet(false, true));
 +  }
 +
 +  /**
 +   * Invokes the 'start' command and operation to startup a GemFire server (a cache server).  Note, this method will
 +   * cause the JVM to block upon server start, providing the calling Thread is a non-daemon Thread.
 +   *
 +   * @see #run()
 +   */
 +  public ServerState start() {
 +    if (isStartable()) {
 +      INSTANCE.compareAndSet(null, this);
 +
 +      try {
 +        process = new ControllableProcess(this.controlHandler, new File(getWorkingDirectory()), ProcessType.SERVER, isForcing());
 +
 +        if (!isDisableDefaultServer()) {
 +          assertPortAvailable(getServerBindAddress(), getServerPort());
 +        }
 +
 +        SystemFailure.setExitOK(true);
 +
 +        ProcessLauncherContext.set(isRedirectingOutput(), getOverriddenDefaults(), new StartupStatusListener() {
 +          @Override
 +          public void setStatus(final String statusMessage) {
 +            debug("Callback setStatus(String) called with message (%1$s)...", statusMessage);
 +            ServerLauncher.this.statusMessage = statusMessage;
 +          }
 +        });
 +
 +        try {
 +          final Properties gemfireProperties = getDistributedSystemProperties(getProperties());
 +          this.cache = (isSpringXmlLocationSpecified() ? startWithSpring() : startWithGemFireApi(gemfireProperties));
 +          
 +          //Set the resource manager options
 +          if (this.criticalHeapPercentage != null) {
 +            this.cache.getResourceManager().setCriticalHeapPercentage(getCriticalHeapPercentage());
 +          } 
 +          if (this.evictionHeapPercentage != null) {
 +            this.cache.getResourceManager().setEvictionHeapPercentage(getEvictionHeapPercentage());
 +          }
 +          if (this.criticalOffHeapPercentage != null) {
 +            this.cache.getResourceManager().setCriticalOffHeapPercentage(getCriticalOffHeapPercentage());
 +          } 
 +          if (this.evictionOffHeapPercentage != null) {
 +            this.cache.getResourceManager().setEvictionOffHeapPercentage(getEvictionOffHeapPercentage());
 +          }
 +          
 +          this.cache.setIsServer(true);
 +          startCacheServer(this.cache);
 +          assignBuckets(this.cache);
 +          rebalance(this.cache);
 +        }
 +        finally {
 +          ProcessLauncherContext.remove();
 +        }
 +        
 +        debug("Running Server on (%1$s) in (%2$s) as (%2$s)...", getId(), getWorkingDirectory(), getMember());
 +        this.running.set(true);
 +
 +        return new ServerState(this, Status.ONLINE);
 +      }
 +      catch (IOException e) {
 +        failOnStart(e);
 +        throw new RuntimeException(LocalizedStrings.Launcher_Command_START_IO_ERROR_MESSAGE.toLocalizedString(
 +          getServiceName(), getWorkingDirectory(), getId(), e.getMessage()), e);
 +      }
 +      catch (FileAlreadyExistsException e) {
 +        failOnStart(e);
 +        throw new RuntimeException(LocalizedStrings.Launcher_Command_START_PID_FILE_ALREADY_EXISTS_ERROR_MESSAGE.
 +           toLocalizedString(getServiceName(), getWorkingDirectory(), getId()), e);
 +      }
 +      catch (PidUnavailableException e) {
 +        failOnStart(e);
 +        throw new RuntimeException(LocalizedStrings.Launcher_Command_START_PID_UNAVAILABLE_ERROR_MESSAGE
 +          .toLocalizedString(getServiceName(), getId(), getWorkingDirectory(), e.getMessage()), e);
 +      }
 +      catch (ClusterConfigurationNotAvailableException e) {
 +        failOnStart(e);
 +        throw e;
 +      }
 +      catch (RuntimeException e) {
 +        failOnStart(e);
 +        throw e;
 +      }
 +      catch (Exception e) {
 +        failOnStart(e);
 +        throw new RuntimeException(e);
 +      }
 +      catch (Error e) {
 +        failOnStart(e);
 +        throw e;
 +      }
 +      finally {
 +        this.starting.set(false);
 +      }
 +    }
 +    else {
 +      throw new IllegalStateException(LocalizedStrings.Launcher_Command_START_SERVICE_ALREADY_RUNNING_ERROR_MESSAGE
 +        .toLocalizedString(getServiceName(), getWorkingDirectory(), getId()));
 +    }
 +  }
 +
 +  private Cache startWithSpring() {
 +    System.setProperty(DistributionConfig.GEMFIRE_PREFIX + DistributionConfig.NAME_NAME, getMemberName());
 +
 +    new SpringContextBootstrappingInitializer().init(CollectionUtils.createProperties(Collections.singletonMap(
 +      SpringContextBootstrappingInitializer.CONTEXT_CONFIG_LOCATIONS_PARAMETER, getSpringXmlLocation())));
 +
 +    return SpringContextBootstrappingInitializer.getApplicationContext().getBean(Cache.class);
 +  }
 +
 +  private Cache startWithGemFireApi(final Properties gemfireProperties ) {
 +    final CacheConfig cacheConfig = getCacheConfig();
 +    final CacheFactory cacheFactory = new CacheFactory(gemfireProperties);
 +
 +    if (cacheConfig.pdxPersistentUserSet) {
 +      cacheFactory.setPdxPersistent(cacheConfig.isPdxPersistent());
 +    }
 +
 +    if (cacheConfig.pdxDiskStoreUserSet) {
 +      cacheFactory.setPdxDiskStore(cacheConfig.getPdxDiskStore());
 +    }
 +
 +    if (cacheConfig.pdxIgnoreUnreadFieldsUserSet) {
 +      cacheFactory.setPdxIgnoreUnreadFields(cacheConfig.getPdxIgnoreUnreadFields());
 +    }
 +
 +    if (cacheConfig.pdxReadSerializedUserSet) {
 +      cacheFactory.setPdxReadSerialized(cacheConfig.isPdxReadSerialized());
 +    }
 +
 +    if (cacheConfig.pdxSerializerUserSet) {
 +      cacheFactory.setPdxSerializer(cacheConfig.getPdxSerializer());
 +    }
 +
 +    return cacheFactory.create();
 +  }
 +
 +  /**
 +   * A helper method to ensure the same sequence of actions are taken when the Server fails to start
 +   * caused by some exception.
 +   * 
 +   * @param cause the Throwable thrown during the startup operation on the Server.
 +   */
 +  private void failOnStart(final Throwable cause) {
 +    if (this.cache != null) {
 +      this.cache.close();
 +      this.cache = null;
 +    }
 +    if (this.process != null) {
 +      this.process.stop();
 +      this.process = null;
 +    }
 +
 +    INSTANCE.compareAndSet(this, null);
 +
 +    this.running.set(false);
 +  }
 +
 +  /**
 +   * Determines whether the specified Cache has any CacheServers.
 +   * 
 +   * @param cache the Cache to check for existing CacheServers.
 +   * @return a boolean value indicating if any CacheServers were added to the Cache.
 +   */
 +  protected boolean isServing(final Cache cache) {
 +    return !cache.getCacheServers().isEmpty();
 +  }
 +
 +  /**
 +   * Determines whether to continue waiting and keep the GemFire non-Server data member running.
 +   * 
 +   * @param cache the Cache associated with this GemFire (non-Server) data member.
 +   * @return a boolean value indicating whether the GemFire data member should continue running, as determined
 +   * by the running flag and a connection to the distributed system (GemFire cluster).
 +   */
 +  final boolean isWaiting(final Cache cache) {
 +    //return (isRunning() && !getCache().isClosed());
 +    return (isRunning() && cache.getDistributedSystem().isConnected());
 +  }
 +
 +  /**
 +   * Causes the calling Thread to block until the GemFire Cache Server/Data Member stops.
 +   */
 +  public void waitOnServer() {
 +    assert getCache() != null : "The Cache Server must first be started with a call to start!";
 +
 +    if (!isServing(getCache())) {
 +      Throwable cause = null;
 +      try {
 +        while (isWaiting(getCache())) {
 +          try {
 +            synchronized (this) {
 +              wait(500l);
 +            }
 +          }
 +          catch (InterruptedException ignore) {
 +          }
 +        }
 +      }
 +      catch (RuntimeException e) {
 +        cause = e;
 +        throw e;
 +      }
 +      finally {
 +        failOnStart(cause);
 +      }
 +    }
 +  }
 +
 +  /**
 +   * Determines whether a default server (a cache server) should be created on startup as determined by the absence
 +   * of specifying the --disable-default-server command-line option (switch).  In addition, a default cache server
 +   * is started only if no cache servers have been added to the Cache by way of cache.xml.
 +   * 
 +   * @param cache the reference to the Cache to check for any existing cache servers.
 +   * @return a boolean indicating whether a default server should be added to the Cache.
 +   * @see #isDisableDefaultServer()
 +   */
 +  protected boolean isDefaultServerEnabled(final Cache cache) {
 +    return (cache.getCacheServers().isEmpty() && !isDisableDefaultServer());
 +  }
 +
 +  /**
 +   * If the default server (cache server) has not been disabled and no prior cache servers were added to the cache,
 +   * then this method will add a cache server to the Cache and start the server Thread on the specified bind address
 +   * and port.
 +   * 
 +   * @param cache the Cache to which the server will be added.
 +   * @throws IOException if the Cache server fails to start due to IO error.
 +   */
 +  final void startCacheServer(final Cache cache) throws IOException {
 +    if (isDefaultServerEnabled(cache)) {
 +      final String serverBindAddress = (getServerBindAddress() == null ? null : getServerBindAddress().getHostAddress());
 +      final Integer serverPort = getServerPort();
 +      CacheServerLauncher.serverBindAddress.set(serverBindAddress);
 +      CacheServerLauncher.serverPort.set(serverPort);
 +      final CacheServer cacheServer = cache.addCacheServer();
 +      cacheServer.setBindAddress(serverBindAddress);
 +      cacheServer.setPort(serverPort);
 +      
 +      if (getMaxThreads() != null) {
 +        cacheServer.setMaxThreads(getMaxThreads());
 +      }
 +      
 +      if (getMaxConnections() != null) {
 +        cacheServer.setMaxConnections(getMaxConnections());
 +      }
 +      
 +      if (getMaxMessageCount() != null) {
 +        cacheServer.setMaximumMessageCount(getMaxMessageCount());
 +      }
 +      
 +      if (getMessageTimeToLive() != null) {
 +        cacheServer.setMessageTimeToLive(getMessageTimeToLive());
 +      }
 +      
 +      if (getSocketBufferSize() != null) {
 +        cacheServer.setSocketBufferSize(getSocketBufferSize());
 +      }
 +
 +      if (getHostNameForClients() != null) {
 +        cacheServer.setHostnameForClients(getHostNameForClients());
 +      }
++      
++      CacheServerHelper.setIsDefaultServer(cacheServer);
 +
 +      cacheServer.start();
 +    }
 +  }
 +
 +  /**
 +   * Causes a rebalance operation to occur on the given Cache.
 +   * 
 +   * @param cache the reference to the Cache to rebalance.
 +   * @see com.gemstone.gemfire.cache.control.ResourceManager#createRebalanceFactory()
 +   */
 +  private void rebalance(final Cache cache) {
 +    if (isRebalancing()) {
 +      cache.getResourceManager().createRebalanceFactory().start();
 +    }
 +  }
 +
 +  /**
 +   * Determines whether the user indicated that buckets should be assigned on cache server start using the
 +   * --assign-buckets command-line option (switch) at the command-line as well as whether the option is technically
 +   * allowed.  The option is only allowed if the instance of the Cache is the internal GemFireCacheImpl at present.
 +   * @param cache the Cache reference to check for instance type.
 +   * @return a boolean indicating if bucket assignment is both enabled and allowed.
 +   * @see #isAssignBuckets()
 +   */
 +  protected boolean isAssignBucketsAllowed(final Cache cache) {
 +    return (isAssignBuckets() && (cache instanceof GemFireCacheImpl));
 +  }
 +
 +  /**
 +   * Assigns buckets to individual Partitioned Regions of the Cache.
 +   * 
 +   * @param cache the Cache who's Partitioned Regions are accessed to assign buckets to.
 +   * @see PartitionRegionHelper#assignBucketsToPartitions(com.gemstone.gemfire.cache.Region)
 +   */
 +  final void assignBuckets(final Cache cache) {
 +    if (isAssignBucketsAllowed(cache)) {
 +      for (PartitionedRegion region : ((GemFireCacheImpl) cache).getPartitionedRegions()) {
 +        PartitionRegionHelper.assignBucketsToPartitions(region);
 +      }
 +    }
 +  }
 +
 +  /**
 +   * Determines whether the Server is the process of starting or is already running.
 +   * 
 +   * @return a boolean indicating if the Server is starting or is already running.
 +   */
 +  protected boolean isStartingOrRunning() {
 +    return (this.starting.get() || isRunning());
 +  }
 +
 +  /**
 +   * Invokes the 'status' command and operation to check the status of a GemFire server (a cache server).
 +   */
 +  public ServerState status() {
 +    final ServerLauncher launcher = getInstance();
 +    // if this instance is running then return local status
 +    if (isStartingOrRunning()) {
 +      debug("Getting status from the ServerLauncher instance that actually launched the GemFire Cache Server.%n");
 +      return new ServerState(this, (isRunning() ? Status.ONLINE : Status.STARTING));
 +    }
 +    else if (isPidInProcess() && launcher != null) {
 +      return launcher.statusInProcess();
 +    }
 +    else if (getPid() != null) {
 +      debug("Getting Server status using process ID (%1$s)%n", getPid());
 +      return statusWithPid();
 +    }
 +    // attempt to get status using workingDirectory
 +    else if (getWorkingDirectory() != null) {
 +      debug("Getting Server status using working directory (%1$s)%n", getWorkingDirectory());
 +      return statusWithWorkingDirectory();
 +    }
 +
 +    debug("This ServerLauncher was not the instance used to launch the GemFire Cache Server, and neither PID "
 +      .concat("nor working directory were specified; the Server's state is unknown.%n"));
 +
 +    return new ServerState(this, Status.NOT_RESPONDING);
 +  }
 +  
 +  private ServerState statusInProcess() {
 +    if (isStartingOrRunning()) {
 +      debug("Getting status from the ServerLauncher instance that actually launched the GemFire Cache Server.%n");
 +      return new ServerState(this, (isRunning() ? Status.ONLINE : Status.STARTING));
 +    } else {
 +      return new ServerState(this, Status.NOT_RESPONDING);
 +    }
 +  }
 +  
 +  private ServerState statusWithPid() {
 +    try {
 +      final ProcessController controller = new ProcessControllerFactory().createProcessController(this.controllerParameters, getPid());
 +      controller.checkPidSupport();
 +      final String statusJson = controller.status();
 +      return ServerState.fromJson(statusJson);
 +    }
 +//    catch (NoClassDefFoundError error) {
 +//      if (isAttachAPINotFound(error)) {
 +//        throw new AttachAPINotFoundException(LocalizedStrings.Launcher_ATTACH_API_NOT_FOUND_ERROR_MESSAGE
 +//          .toLocalizedString(), error);
 +//      }
 +//
 +//      throw error;
 +//    }
 +    catch (ConnectionFailedException e) {
 +      // failed to attach to server JVM
 +      return createNoResponseState(e, "Failed to connect to server with process id " + getPid());
 +    } 
 +    catch (IOException e) {
 +      // failed to open or read file or dir
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    } 
 +//    catch (MalformedObjectNameException e) { // impossible
 +//      // JMX object name is bad
 +//      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +//    } 
 +    catch (MBeanInvocationFailedException e) {
 +      // MBean either doesn't exist or method or attribute don't exist
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    } 
 +//    catch (PidUnavailableException e) {
 +//      // couldn't determine pid from within server JVM
 +//      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +//    } 
 +    catch (UnableToControlProcessException e) {
 +      // TODO comment me
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    } 
 +    catch (InterruptedException e) {
 +      // TODO comment me
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    } 
 +    catch (TimeoutException e) {
 +      // TODO comment me
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    }
 +  }
 +
 +  private ServerState statusWithWorkingDirectory() {
 +    int parsedPid = 0;
 +    try {
 +      final ProcessController controller = new ProcessControllerFactory().createProcessController(this.controllerParameters, new File(getWorkingDirectory()), ProcessType.SERVER.getPidFileName(), READ_PID_FILE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
 +      parsedPid = controller.getProcessId();
 +      
 +      // note: in-process request will go infinite loop unless we do the following
 +      if (parsedPid == identifyPid()) {
 +        final ServerLauncher runningLauncher = getInstance();
 +        if (runningLauncher != null) {
 +          return runningLauncher.statusInProcess();
 +        }
 +      }
 +
 +      final String statusJson = controller.status();
 +      return ServerState.fromJson(statusJson);
 +    }
 +    catch (ConnectionFailedException e) {
 +      // failed to attach to server JVM
 +      return createNoResponseState(e, "Failed to connect to server with process id " + parsedPid);
 +    } 
 +    catch (FileNotFoundException e) {
 +      // could not find pid file
 +      return createNoResponseState(e, "Failed to find process file " + ProcessType.SERVER.getPidFileName() + " in " + getWorkingDirectory());
 +    } 
 +    catch (IOException e) {
 +      // failed to open or read file or dir
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + parsedPid);
 +    } 
 +    catch (InterruptedException e) {
 +      Thread.currentThread().interrupt();
 +      return createNoResponseState(e, "Interrupted while trying to communicate with server with process id " + parsedPid);
 +    } 
 +    catch (MBeanInvocationFailedException e) {
 +      // MBean either doesn't exist or method or attribute don't exist
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + parsedPid);
 +    } 
 +    catch (PidUnavailableException e) {
 +      // couldn't determine pid from within server JVM
 +      return createNoResponseState(e, "Failed to find usable process id within file " + ProcessType.SERVER.getPidFileName() + " in " + getWorkingDirectory());
 +    } 
 +    catch (UnableToControlProcessException e) {
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + parsedPid);
 +    } 
 +    catch (TimeoutException e) {
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + parsedPid);
 +    }
 +  }
 +
 +  /**
 +   * Determines whether the Server can be stopped in-process, such as when a Server is embedded in an application
 +   * and the ServerLauncher API is being used.
 +   * 
 +   * @return a boolean indicating whether the Server can be stopped in-process (the application's process with
 +   * an embedded Server).
 +   */
 +  private boolean isStoppable() {
 +    return (isRunning() && getCache() != null);
 +  }
 +
 +  /**
 +   * Invokes the 'stop' command and operation to stop a GemFire server (a cache server).
 +   */
 +  public ServerState stop() {
 +    final ServerLauncher launcher = getInstance();
 +    // if this instance is running then stop it
 +    if (isStoppable()) {
 +      return stopInProcess();
 +    }
 +    // if in-process but difference instance of ServerLauncher
 +    else if (isPidInProcess() && launcher != null) {
 +      return launcher.stopInProcess();
 +    }
 +    // attempt to stop using pid if provided
 +    else if (getPid() != null) {
 +      return stopWithPid();
 +    }
 +    // attempt to stop using workingDirectory
 +    else if (getWorkingDirectory() != null) {
 +      return stopWithWorkingDirectory();
 +    }
 +
 +    // TODO give user detailed error message?
 +    return new ServerState(this, Status.NOT_RESPONDING);
 +  }
 +  
 +  private ServerState stopInProcess() {
 +    if (isStoppable()) {
 +      this.cache.close();
 +      this.cache = null;
 +      this.process.stop();
 +      this.process = null;
 +      INSTANCE.compareAndSet(this, null); // note: other thread may return Status.NOT_RESPONDING now
 +      this.running.set(false);
 +      return new ServerState(this, Status.STOPPED);
 +    } else {
 +      return new ServerState(this, Status.NOT_RESPONDING);
 +    }
 +  }
 +
 +  private ServerState stopWithPid() {
 +    try {
 +      final ProcessController controller = new ProcessControllerFactory().createProcessController(this.controllerParameters, getPid());
 +      controller.checkPidSupport();
 +      controller.stop();
 +      return new ServerState(this, Status.STOPPED);
 +    }
 +//    catch (NoClassDefFoundError error) {
 +//      if (isAttachAPINotFound(error)) {
 +//        throw new AttachAPINotFoundException(LocalizedStrings.Launcher_ATTACH_API_NOT_FOUND_ERROR_MESSAGE
 +//          .toLocalizedString(), error);
 +//      }
 +//
 +//      throw error;
 +//    }
 +    catch (ConnectionFailedException e) {
 +      // failed to attach to server JVM
 +      return createNoResponseState(e, "Failed to connect to server with process id " + getPid());
 +    } 
 +    catch (IOException e) {
 +      // failed to open or read file or dir
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    } 
 +//    catch (MalformedObjectNameException e) { // impossible
 +//      // JMX object name is bad
 +//      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +//    } 
 +    catch (MBeanInvocationFailedException e) {
 +      // MBean either doesn't exist or method or attribute don't exist
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    } 
 +//    catch (PidUnavailableException e) {
 +//      // couldn't determine pid from within server JVM
 +//      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +//    } 
 +    catch (UnableToControlProcessException e) {
 +      // TODO comment me
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + getPid());
 +    }
 +  }
 +
 +  private ServerState stopWithWorkingDirectory() {
 +    int parsedPid = 0;
 +    try {
 +      final ProcessController controller = new ProcessControllerFactory().createProcessController(this.controllerParameters, new File(getWorkingDirectory()), ProcessType.SERVER.getPidFileName(), READ_PID_FILE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
 +      parsedPid = controller.getProcessId();
 +      
 +      // NOTE in-process request will go infinite loop unless we do the following
 +      if (parsedPid == identifyPid()) {
 +        final ServerLauncher runningLauncher = getInstance();
 +        if (runningLauncher != null) {
 +          return runningLauncher.stopInProcess();
 +        }
 +      }
 +      
 +      controller.stop();
 +      return new ServerState(this, Status.STOPPED);
 +    }
 +    catch (ConnectionFailedException e) {
 +      // failed to attach to server JVM
 +      return createNoResponseState(e, "Failed to connect to server with process id " + parsedPid);
 +    } 
 +    catch (FileNotFoundException e) {
 +      // could not find pid file
 +      return createNoResponseState(e, "Failed to find process file " + ProcessType.SERVER.getPidFileName() + " in " + getWorkingDirectory());
 +    } 
 +    catch (IOException e) {
 +      // failed to open or read file or dir
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + parsedPid);
 +    } 
 +    catch (InterruptedException e) {
 +      Thread.currentThread().interrupt();
 +      return createNoResponseState(e, "Interrupted while trying to communicate with server with process id " + parsedPid);
 +    } 
 +    catch (MBeanInvocationFailedException e) {
 +      // MBean either doesn't exist or method or attribute don't exist
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + parsedPid);
 +    } 
 +    catch (PidUnavailableException e) {
 +      // couldn't determine pid from within server JVM
 +      return createNoResponseState(e, "Failed to find usable process id within file " + ProcessType.SERVER.getPidFileName() + " in " + getWorkingDirectory());
 +    } 
 +    catch (TimeoutException e) {
 +      return createNoResponseState(e, "Timed out trying to find usable process id within file " + ProcessType.SERVER.getPidFileName() + " in " + getWorkingDirectory());
 +    } 
 +    catch (UnableToControlProcessException e) {
 +      return createNoResponseState(e, "Failed to communicate with server with process id " + parsedPid);
 +    }
 +  }
 +
 +  private ServerState createNoResponseState(final Exception cause, final String errorMessage) {
 +    debug(cause);
 +    return new ServerState(this, Status.NOT_RESPONDING, errorMessage);
 +  }
 +
 +  private Properties getOverriddenDefaults() {
 +    final Properties overriddenDefaults = new Properties();
 +    
 +    overriddenDefaults.put(
 +      ProcessLauncherContext.OVERRIDDEN_DEFAULTS_PREFIX.concat(DistributionConfig.LOG_FILE_NAME), 
 +      getLogFileName());
 +
 +    for (String key : System.getProperties().stringPropertyNames()) {
 +      if (key.startsWith(ProcessLauncherContext.OVERRIDDEN_DEFAULTS_PREFIX)) {
 +        overriddenDefaults.put(key, System.getProperty(key));
 +      }
 +    }
 +
 +    return overriddenDefaults;
 +  }
 +
 +  private class ServerControllerParameters implements ProcessControllerParameters {
 +    @Override
 +    public File getPidFile() {
 +      return getServerPidFile();
 +    }
 +  
 +    @Override
 +    public File getWorkingDirectory() {
 +      return new File(ServerLauncher.this.getWorkingDirectory());
 +    }
 +  
 +    @Override
 +    public int getProcessId() {
 +      return getPid();
 +    }
 +  
 +    @Override
 +    public ProcessType getProcessType() {
 +      return ProcessType.SERVER;
 +    }
 +  
 +    @Override
 +    public ObjectName getNamePattern() {
 +      try {
 +        return ObjectName.getInstance("GemFire:type=Member,*");
 +      } catch (MalformedObjectNameException e) {
 +        return null;
 +      } catch (NullPointerException e) {
 +        return null;
 +      }
 +    }
 +  
 +    @Override
 +    public String getPidAttribute() {
 +      return "ProcessId";
 +    }
 +  
 +    @Override
 +    public String getStopMethod() {
 +      return "shutDownMember";
 +    }
 +    
 +    @Override
 +    public String getStatusMethod() {
 +      return "status";
 +    }
 +  
 +    @Override
 +    public String[] getAttributes() {
 +      return new String[] {"Server"};
 +    }
 +  
 +    @Override
 +    public Object[] getValues() {
 +      return new Object[] {Boolean.TRUE};
 +    }
 +  }
 +
 +  /**
 +   * The Builder class, modeled after the Builder creational design pattern, is used to construct a properly configured
 +   * and initialized instance of the ServerLauncher to control and run GemFire servers (in particular, cache servers).
 +   */
 +  public static class Builder {
 +
 +    protected static final Command DEFAULT_COMMAND = Command.UNSPECIFIED;
 +
 +    private boolean serverBindAddressSetByUser;
 +    private boolean serverPortSetByUser;
 +
 +    private Boolean assignBuckets;
 +    private Boolean debug;
 +    private Boolean disableDefaultServer;
 +    private Boolean force;
 +    private Boolean help;
 +    private Boolean rebalance;
 +    private Boolean redirectOutput;
 +
 +    private Cache cache;
 +
 +    private final CacheConfig cacheConfig = new CacheConfig();
 +
 +    private Command command;
 +
 +    private InetAddress serverBindAddress;
 +
 +    private Integer pid;
 +    private Integer serverPort;
 +
 +    private final Properties distributedSystemProperties = new Properties();
 +
 +    private String memberName;
 +    private String springXmlLocation;
 +    private String workingDirectory;
 +    
 +    private Float criticalHeapPercentage;
 +    private Float evictionHeapPercentage;
 +    
 +    private Float criticalOffHeapPercentage;
 +    private Float evictionOffHeapPercentage;
 +    
 +    private String hostNameForClients; 
 +    private Integer loadPollInterval;
 +    private Integer maxConnections;
 +    private Integer maxMessageCount;
 +    private Integer messageTimeToLive;
 +    private Integer socketBufferSize;
 +    private Integer maxThreads;
 +
 +    /**
 +     * Default constructor used to create an instance of the Builder class for programmatical access.
 +     */
 +    public Builder() {
 +    }
 +
 +    /**
 +     * Constructor used to create and configure an instance of the Builder class with the specified arguments, passed in
 +     * from the command-line when launching an instance of this class from the command-line using the Java launcher.
 +     * 
 +     * @param args the array of arguments used to configure the Builder.
 +     * @see #parseArguments(String...)
 +     */
 +    public Builder(final String... args) {
 +      parseArguments(args != null ? args : new String[0]);
 +    }
 +
 +    /**
 +     * Gets an instance of the JOptSimple OptionParser to parse the command-line arguments for Server.
 +     * 
 +     * @return an instance of the JOptSimple OptionParser configured with the command-line options used by the Server.
 +     */
 +    private OptionParser getParser() {
 +      OptionParser parser = new OptionParser(true);
 +
 +      parser.accepts("assign-buckets");
 +      parser.accepts("debug");
 +      parser.accepts("dir").withRequiredArg().ofType(String.class);
 +      parser.accepts("disable-default-server");
 +      parser.accepts("force");
 +      parser.accepts("help");
 +      parser.accepts("member").withRequiredArg().ofType(String.class);
 +      parser.accepts("pid").withRequiredArg().ofType(Integer.class);
 +      parser.accepts("rebalance");
 +      parser.accepts("redirect-output");
 +      parser.accepts("server-bind-address").withRequiredArg().ofType(String.class);
 +      parser.accepts("server-port").withRequiredArg().ofType(Integer.class);
 +      parser.accepts("spring-xml-location").withRequiredArg().ofType(String.class);
 +      parser.accepts("version");
 +      parser.accepts(CliStrings.START_SERVER__CRITICAL__HEAP__PERCENTAGE).withRequiredArg().ofType(Float.class);
 +      parser.accepts(CliStrings.START_SERVER__EVICTION__HEAP__PERCENTAGE).withRequiredArg().ofType(Float.class);
 +      parser.accepts(CliStrings.START_SERVER__CRITICAL_OFF_HEAP_PERCENTAGE).withRequiredArg().ofType(Float.class);
 +      parser.accepts(CliStrings.START_SERVER__EVICTION_OFF_HEAP_PERCENTAGE).withRequiredArg().ofType(Float.class);
 +      parser.accepts(CliStrings.START_SERVER__MAX__CONNECTIONS).withRequiredArg().ofType(Integer.class);
 +      parser.accepts(CliStrings.START_SERVER__MAX__MESSAGE__COUNT).withRequiredArg().ofType(Integer.class);
 +      parser.accepts(CliStrings.START_SERVER__MAX__THREADS).withRequiredArg().ofType(Integer.class);
 +      parser.accepts(CliStrings.START_SERVER__MESSAGE__TIME__TO__LIVE).withRequiredArg().ofType(Integer.class);
 +      parser.accepts(CliStrings.START_SERVER__SOCKET__BUFFER__SIZE).withRequiredArg().ofType(Integer.class);
 +      parser.accepts(CliStrings.START_SERVER__HOSTNAME__FOR__CLIENTS).withRequiredArg().ofType(String.class);
 +
 +      return parser;
 +    }
 +
 +    /**
 +     * Parses the list of arguments to configure this Builder with the intent of constructing a Server launcher to
 +     * invoke a Cache Server.  This method is called to parse the arguments specified by the user on the command-line.
 +     * 
 +     * @param args the array of arguments used to configure this Builder and create an instance of ServerLauncher.
 +     */
 +    protected void parseArguments(final String... args) {
 +      try {
 +        OptionSet options = getParser().parse(args);
 +
 +        parseCommand(args);
 +        parseMemberName(args); // TODO:KIRK: need to get the name to LogService for log file name
 +
 +        setAssignBuckets(options.has("assign-buckets"));
 +        setDebug(options.has("debug"));
 +        setDisableDefaultServer(options.has("disable-default-server"));
 +        setForce(options.has("force"));
 +        setHelp(options.has("help"));
 +        setRebalance(options.has("rebalance"));
 +        setRedirectOutput(options.has("redirect-output"));
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__CRITICAL__HEAP__PERCENTAGE)) {
 +          setCriticalHeapPercentage(Float.parseFloat(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__CRITICAL__HEAP__PERCENTAGE))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__EVICTION__HEAP__PERCENTAGE)) {
 +          setEvictionHeapPercentage(Float.parseFloat(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__EVICTION__HEAP__PERCENTAGE))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__CRITICAL_OFF_HEAP_PERCENTAGE)) {
 +          setCriticalOffHeapPercentage(Float.parseFloat(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__CRITICAL_OFF_HEAP_PERCENTAGE))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__EVICTION_OFF_HEAP_PERCENTAGE)) {
 +          setEvictionOffHeapPercentage(Float.parseFloat(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__EVICTION_OFF_HEAP_PERCENTAGE))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__MAX__CONNECTIONS)) {
 +          setMaxConnections(Integer.parseInt(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__MAX__CONNECTIONS))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__MAX__MESSAGE__COUNT)) {
 +          setMaxConnections(Integer.parseInt(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__MAX__MESSAGE__COUNT))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__MESSAGE__TIME__TO__LIVE)) {
 +          setMaxConnections(Integer.parseInt(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__MESSAGE__TIME__TO__LIVE))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__SOCKET__BUFFER__SIZE)) {
 +          setMaxConnections(Integer.parseInt(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__SOCKET__BUFFER__SIZE))));
 +        } 
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__MAX__THREADS)) {
 +          setMaxThreads(Integer.parseInt(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__MAX__THREADS))));
 +        }
 +        
 +        if (!isHelping()) {
 +          if (options.has("dir")) {
 +            setWorkingDirectory(ObjectUtils.toString(options.valueOf("dir")));
 +          }
 +
 +          if (options.has("pid")) {
 +            setPid((Integer) options.valueOf("pid"));
 +          }
 +
 +          if (options.has("server-bind-address")) {
 +            setServerBindAddress(ObjectUtils.toString(options.valueOf("server-bind-address")));
 +          }
 +
 +          if (options.has("server-port")) {
 +            setServerPort((Integer) options.valueOf("server-port"));
 +          }
 +
 +          if (options.has("spring-xml-location")) {
 +            setSpringXmlLocation(ObjectUtils.toString(options.valueOf("spring-xml-location")));
 +          }
 +
 +          if (options.has("version")) {
 +            setCommand(Command.VERSION);
 +          }
 +        }
 +
 +        // TODO why are these option not inside the 'if (!isHelping())' conditional block!?
 +
 +        if (options.hasArgument(CliStrings.START_SERVER__CRITICAL__HEAP__PERCENTAGE)) {
 +          setCriticalHeapPercentage(Float.parseFloat(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__CRITICAL__HEAP__PERCENTAGE))));
 +        }
 +
 +        if (options.hasArgument(CliStrings.START_SERVER__EVICTION__HEAP__PERCENTAGE)) {
 +          setEvictionHeapPercentage(Float.parseFloat(ObjectUtils.toString(options.valueOf(
 +            CliStrings.START_SERVER__EVICTION__HEAP__PERCENTAGE))));
 +        }
 +
 +        if (options.hasArgument(CliStrings.START_SERVER__MAX__CONNECTIONS)) {
 +          setMaxConnections(Integer.parseInt(ObjectUtils.toString(options.valueOf(
 +            CliStrings.START_SERVER__MAX__CONNECTIONS))));
 +        }
 +
 +        if (options.hasArgument(CliStrings.START_SERVER__MAX__MESSAGE__COUNT)) {
 +          setMaxMessageCount(Integer.parseInt(ObjectUtils.toString(options.valueOf(
 +            CliStrings.START_SERVER__MAX__MESSAGE__COUNT))));
 +        }
 +
 +        if (options.hasArgument(CliStrings.START_SERVER__MAX__THREADS)) {
 +          setMaxThreads(Integer.parseInt(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__MAX__THREADS))));
 +        }
 +
 +        if (options.hasArgument(CliStrings.START_SERVER__MESSAGE__TIME__TO__LIVE)) {
 +          setMessageTimeToLive(Integer.parseInt(ObjectUtils.toString(options.valueOf(
 +            CliStrings.START_SERVER__MESSAGE__TIME__TO__LIVE))));
 +        }
 +        
 +        if (options.hasArgument(CliStrings.START_SERVER__SOCKET__BUFFER__SIZE)) {
 +          setSocketBufferSize(Integer.parseInt(ObjectUtils.toString(options.valueOf(
 +            CliStrings.START_SERVER__SOCKET__BUFFER__SIZE))));
 +        }
 +
 +        if (options.hasArgument(CliStrings.START_SERVER__HOSTNAME__FOR__CLIENTS)) {
 +          setHostNameForClients(ObjectUtils.toString(options.valueOf(CliStrings.START_SERVER__HOSTNAME__FOR__CLIENTS)));
 +        }
 +
 +      }
 +      catch (OptionException e) {
 +        throw new IllegalArgumentException(LocalizedStrings.Launcher_Builder_PARSE_COMMAND_LINE_ARGUMENT_ERROR_MESSAGE
 +          .toLocalizedString("Server", e.getMessage()), e);
 +      }
 +      catch (Exception e) {
 +        throw new RuntimeException(e.getMessage(), e);
 +      }
 +    }
 +
 +    /**
 +     * Iterates the list of arguments in search of the target Server launcher command.
 +     * 
 +     * @param args an array of arguments from which to search for the Server launcher command.
 +     * @see com.gemstone.gemfire.distributed.ServerLauncher.Command#valueOfName(String)
 +     * @see #parseArguments(String...)
 +     */
 +    protected void parseCommand(final String... args) {
 +      if (args != null) {
 +        for (String arg : args) {
 +          Command command = Command.valueOfName(arg);
 +          if (command != null) {
 +            setCommand(command);
 +            break;
 +          }
 +        }
 +      }
 +    }
 +
 +    /**
 +     * Iterates the list of arguments in search of the Server's GemFire member name.  If the argument does not
 +     * start with '-' or is not the name of a Server launcher command, then the value is presumed to be the member name
 +     * for the Server in GemFire.
 +     * 
 +     * @param args the array of arguments from which to search for the Server's member name in GemFire.
 +     * @see com.gemstone.gemfire.distributed.ServerLauncher.Command#isCommand(String)
 +     * @see #parseArguments(String...)
 +     */
 +    protected void parseMemberName(final String... args) {
 +      if (args != null) {
 +        for (String arg : args) {
 +          if (!(arg.startsWith(OPTION_PREFIX) || Command.isCommand(arg))) {
 +            setMemberName(arg);
 +            break;
 +          }
 +        }
 +      }
 +    }
 +
 +    /**
 +     * Gets the CacheConfig object used to configure PDX on the GemFire Cache by the Builder.
 +     *
 +     * @return the CacheConfig object used to configure PDX on the GemFire Cache by the Builder.
 +     */
 +    CacheConfig getCacheConfig() {
 +      return this.cacheConfig;
 +    }
 +
 +    /**
 +     * Gets the Server launcher command used during the invocation of the ServerLauncher.
 +     * 
 +     * @return the Server launcher command used to invoke (run) the ServerLauncher class.
 +     * @see #setCommand(com.gemstone.gemfire.distributed.ServerLauncher.Command)
 +     * @see com.gemstone.gemfire.distributed.ServerLauncher.Command
 +     */
 +    public Command getCommand() {
 +      return ObjectUtils.defaultIfNull(this.command, DEFAULT_COMMAND);
 +    }
 +
 +    /**
 +     * Sets the Sever launcher command used during the invocation of the ServerLauncher
 +     * 
 +     * @param command the targeted Server launcher command used during the invocation (run) of ServerLauncher.
 +     * @return this Builder instance.
 +     * @see #getCommand()
 +     * @see com.gemstone.gemfire.distributed.ServerLauncher.Command
 +     */
 +    public Builder setCommand(final Command command) {
 +      this.command = command;
 +      return this;
 +    }
 +
 +    /**
 +     * Determines whether buckets should be assigned to partitioned regions in the cache upon Server start.
 +     * 
 +     * @return a boolean indicating if buckets should be assigned upon Server start.
 +     * @see #setAssignBuckets(Boolean)
 +     */
 +    public Boolean getAssignBuckets() {
 +      return this.assignBuckets;
 +    }
 +
 +    /**
 +     * Sets whether buckets should be assigned to partitioned regions in the cache upon Server start.
 +     * 
 +     * @param assignBuckets a boolean indicating if buckets should be assigned upon Server start.
 +     * @return this Builder instance.
 +     * @see #getAssignBuckets()
 +     */
 +    public Builder setAssignBuckets(final Boolean assignBuckets) {
 +      this.assignBuckets = assignBuckets;
 +      return this;
 +    }
 +
 +    // For testing purposes only!
 +    Cache getCache() {
 +      return this.cache;
 +    }
 +
 +    // For testing purposes only!
 +    Builder setCache(final Cache cache) {
 +      this.cache = cache;
 +      return this;
 +    }
 +
 +    /**
 +     * Determines whether the new instance of the ServerLauncher will be set to debug mode.
 +     * 
 +     * @return a boolean value indicating whether debug mode is enabled or disabled.
 +     * @see #setDebug(Boolean)
 +     */
 +    public Boolean getDebug() {
 +      return this.debug;
 +    }
 +
 +    /**
 +     * Sets whether the new instance of the ServerLauncher will be set to debug mode.
 +     * 
 +     * @param debug a boolean value indicating whether debug mode is to be enabled or disabled.
 +     * @return this Builder instance.
 +     * @see #getDebug()
 +     */
 +    public Builder setDebug(final Boolean debug) {
 +      this.debug = debug;
 +      return this;
 +    }
 +
 +    /**
 +     * Determines whether a default cache server will be added when the GemFire Server comes online.
 +     * 
 +     * @return a boolean value indicating whether to add a default cache server.
 +     * @see #setDisableDefaultServer(Boolean)
 +     */
 +    public Boolean getDisableDefaultServer() {
 +      return this.disableDefaultServer;
 +    }
 +
 +    /**
 +     * Sets a boolean value indicating whether to add a default cache when the GemFire Server comes online.
 +     * 
 +     * @param disableDefaultServer a boolean value indicating whether to add a default cache server.
 +     * @return this Builder instance.
 +     * @see #getDisableDefaultServer()
 +     */
 +    public Builder setDisableDefaultServer(final Boolean disableDefaultServer) {
 +      this.disableDefaultServer = disableDefaultServer;
 +      return this;
 +    }
 +
 +    /**
 +     * Gets the GemFire Distributed System (cluster) Properties configuration.
 +     *
 +     * @return a Properties object containing configuration settings for the GemFire Distributed System (cluster).
 +     * @see java.util.Properties
 +     */
 +    public Properties getDistributedSystemProperties() {
 +      return this.distributedSystemProperties;
 +    }
 +
 +    /**
 +     * Gets the boolean value used by the Server to determine if it should overwrite the PID file if it already exists.
 +     * 
 +     * @return the boolean value specifying whether or not to overwrite the PID file if it already exists.
 +     * @see com.gemstone.gemfire.internal.process.LocalProcessLauncher
 +     * @see #setForce(Boolean)
 +     */
 +    public Boolean getForce() {
 +      return ObjectUtils.defaultIfNull(this.force, DEFAULT_FORCE);
 +    }
 +
 +    /**
 +     * Sets the boolean value used by the Server to determine if it should overwrite the PID file if it already exists.
 +     * 
 +     * @param force a boolean value indicating whether to overwrite the PID file when it already exists.
 +     * @return this Builder instance.
 +     * @see com.gemstone.gemfire.internal.process.LocalProcessLauncher
 +     * @see #getForce()
 +     */
 +    public Builder setForce(final Boolean force) {
 +      this.force = force;
 +      return this;
 +    }
 +
 +    /**
 +     * Determines whether the new instance of the ServerLauncher will be used to output help information for either
 +     * a specific command, or for using ServerLauncher in general.
 +     * 
 +     * @return a boolean value indicating whether help will be output during the invocation of the ServerLauncher.
 +     * @see #setHelp(Boolean)
 +     */
 +    public Boolean getHelp() {
 +      return this.help;
 +    }
 +
 +    /**
 +     * Determines whether help has been enabled.
 +     * 
 +     * @return a boolean indicating if help was enabled.
 +     */
 +    protected final boolean isHelping() {
 +      return Boolean.TRUE.equals(getHelp());
 +    }
 +
 +    /**
 +     * Sets whether the new instance of ServerLauncher will be used to output help information for either a specific
 +     * command, or for using ServerLauncher in general.
 +     * 
 +     * @param help a boolean indicating whether help information is to be displayed during invocation of ServerLauncher.
 +     * @return this Builder instance.
 +     * @see #getHelp()
 +     */
 +    public Builder setHelp(final Boolean help) {
 +      this.help = help;
 +      return this;
 +    }
 +
 +    /**
 +     * Determines whether a rebalance operation on the cache will occur upon starting the GemFire server.
 +     * 
 +     * @return a boolean indicating if the cache will be rebalance when the GemFire server starts.
 +     * @see #setRebalance(Boolean)
 +     */
 +    public Boolean getRebalance() {
 +      return this.rebalance;
 +    }
 +
 +    /**
 +     * Set a boolean value indicating whether a rebalance operation on the cache should occur upon starting
 +     * the GemFire server.
 +     * 
 +     * @param rebalance a boolean indicating if the cache will be rebalanced when the GemFire server starts.
 +     * @return this Builder instance.
 +     * @see #getRebalance()
 +     */
 +    public Builder setRebalance(final Boolean rebalance) {
 +      this.rebalance = rebalance;
 +      return this;
 +    }
 +
 +    /**
 +     * Gets the member name of this Server in GemFire.
 +     * 
 +     * @return a String indicating the member name of this Server in GemFire.
 +     * @see #setMemberName(String)
 +     */
 +    public String getMemberName() {
 +      return this.memberName;
 +    }
 +
 +    /**
 +     * Sets the member name of the Server in GemFire.
 +     * 
 +     * @param memberName a String indicating the member name of this Server in GemFire.
 +     * @return this Builder instance.
 +     * @throws IllegalArgumentException if the member name is invalid.
 +     * @see #getMemberName()
 +     */
 +    public Builder setMemberName(final String memberName) {
 +      if (StringUtils.isEmpty(StringUtils.trim(memberName))) {
 +        throw new IllegalArgumentException(LocalizedStrings.Launcher_Builder_MEMBER_NAME_ERROR_MESSAGE
 +          .toLocalizedString("Server"));
 +      }
 +      this.memberName = memberName;
 +      return this;
 +    }
 +
 +    /**
 +     * Gets the process ID (PID) of the running Server indicated by the user as an argument to the ServerLauncher.
 +     * This PID is used by the Server launcher to determine the Server's status, or invoke shutdown on the Server.
 +     * 
 +     * @return a user specified Integer value indicating the process ID of the running Server.
 +     * @see #setPid(Integer)
 +     */
 +    public Integer getPid() {
 +      return this.pid;
 +    }
 +
 +    /**
 +     * Sets the process ID (PID) of the running Server indicated by the user as an argument to the ServerLauncher.
 +     * This PID will be used by the Server launcher to determine the Server's status, or invoke shutdown on the Server.
 +     * 
 +     * @param pid a user specified Integer value indicating the process ID of the running Server.
 +     * @return this Builder instance.
 +     * @throws IllegalArgumentException if the process ID (PID) is not valid (greater than zero if not null).
 +     * @see #getPid()
 +     */
 +    public Builder setPid(final Integer pid) {
 +      if (pid != null && pid < 0) {
 +        throw new IllegalArgumentException(LocalizedStrings.Launcher_Builder_PID_ERROR_MESSAGE.toLocalizedString());
 +      }
 +      this.pid = pid;
 +      return this;
 +    }
 +
 +    /**
 +     * Determines whether the new instance of LocatorLauncher will redirect
 +     * output to system logs when starting a Locator.
 +     * 
 +     * @return a boolean value indicating if output will be redirected to system 
 +     * logs when starting a Locator
 +     * 
 +     * @see #setRedirectOutput(Boolean)
 +     */
 +    public Boolean getRedirectOutput() {
 +      return this.redirectOutput;
 +    }
 +
 +    /**
 +     * Determines whether redirecting of output has been enabled.
 +     * 
 +     * @return a boolean indicating if redirecting of output was enabled.
 +     */
 +    private boolean isRedirectingOutput() {
 +      return Boolean.TRUE.equals(getRedirectOutput());
 +    }
 +
 +    /**
 +     * Sets whether the new instance of LocatorLauncher will redirect output to system logs when starting a Locator.
 +     * 
 +     * @param redirectOutput a boolean value indicating if output will be redirected to system logs when starting
 +     * a Locator.
 +     * @return this Builder instance.
 +     * @see #getRedirectOutput()
 +     */
 +    public Builder setRedirectOutput(final Boolean redirectOutput) {
 +      this.redirectOutput = redirectOutput;
 +      return this;
 +    }
 +
 +    /**
 +     * Gets the IP address to which the Server will be bound listening for and accepting cache client connections in
 +     * a client/server topology.
 +     * 
 +     * @return an InetAddress indicating the IP address that the Server is bound to listening for and accepting cache
 +     * client connections in a client/server topology.
 +     * @see #setServerBindAddress(String)
 +     */
 +    public InetAddress getServerBindAddress() {
 +      return this.serverBindAddress;
 +    }
 +    
 +    boolean isServerBindAddressSetByUser() {
 +      return this.serverBindAddressSetByUser;
 +    }
 +
 +    /**
 +     * Sets the IP address to which the Server will be bound listening for and accepting cache client connections in
 +     * a client/server topology.
 +     * 
 +     * @param serverBindAddress a String specifying the IP address or hostname that the Server will be bound to listen
 +     * for and accept cache client connections in a client/server topology.
 +     * @return this Builder instance.
 +     * @throws IllegalArgumentException wrapping the UnknownHostException if the IP address or hostname for the
 +     * server bind address is unknown.
 +     * @see #getServerBindAddress()
 +     */
 +    public Builder setServerBindAddress(final String serverBindAddress) {
 +      if (StringUtils.isBlank(serverBindAddress)) {
 +        this.serverBindAddress = null;
 +        return this;
 +      }
 +      // NOTE only set the 'bind address' if the user specified a value
 +      else {
 +        try {
 +          this.serverBindAddress = InetAddress.getByName(serverBindAddress);
-           this.serverBindAddressSetByUser = true;
-           return this;
++          if (SocketCreator.isLocalHost(this.serverBindAddress)) {
++            this.serverBindAddressSetByUser = true;
++            return this;
++          }
++          else {
++            throw new IllegalArgumentException(serverBindAddress + " is not an address for this machine.");
++          }
 +        }
 +        catch (UnknownHostException e) {
 +          throw new IllegalArgumentException(LocalizedStrings.Launcher_Builder_UNKNOWN_HOST_ERROR_MESSAGE
 +            .toLocalizedString("Server"), e);
 +        }
 +      }
 +    }
 +
 +    /**
 +     * Gets the port on which the Server will listen for and accept cache client connections in a client/server topology.
 +     * 
 +     * @return an Integer value specifying the port the Server will listen on and accept cache client connections in
 +     * a client/server topology.
 +     * @see #setServerPort(Integer)
 +     */
 +    public Integer getServerPort() {
 +      return ObjectUtils.defaultIfNull(this.serverPort, getDefaultServerPort());
 +    }
 +    
 +    boolean isServerPortSetByUser() {
 +      return this.serverPortSetByUser;
 +    }
 +
 +    /**
 +     * Sets the port on which the Server will listen for and accept cache client connections in a client/server topology.
 +     * 
 +     * @param serverPort an Integer value specifying the port the Server will listen on and accept cache client
 +     * connections in a client/server topology.
 +     * @return this Builder instance.
 +     * @throws IllegalArgumentException if the port number is not valid.
 +     * @see #getServerPort()
 +     */
 +    public Builder setServerPort(final Integer serverPort) {
 +      if (serverPort != null && (serverPort < 0 || serverPort > 65535)) {
 +        throw new IllegalArgumentException(LocalizedStrings.Launcher_Builder_INVALID_PORT_ERROR_MESSAGE
 +          .toLocalizedString("Server"));
 +      }
 +      this.serverPort = serverPort;
 +      this.serverPortSetByUser = true;
 +      return this;
 +    }
 +
 +    /**
 +     * Gets the location of the Spring XML configuration meta-data file used to bootstrap, configure and initialize
 +     * the GemFire Server on start.
 +     * <p>
 +     * @return a String indicating the location of the Spring XML configuration file.
 +     * @see #setSpringXmlLocation(String)
 +     */
 +    public String getSpringXmlLocation() {
 +      return this.springXmlLocation;
 +    }
 +
 +    /**
 +     * Sets the location of the Spring XML configuration meta-data file used to bootstrap, configure and initialize
 +     * the GemFire Server on start.
 +     * <p>
 +     * @param springXmlLocation a String indicating the location of the Spring XML configuration file.
 +     * @return this Builder instance.
 +     * @see #getSpringXmlLocation()
 +     */
 +    public Builder setSpringXmlLocation(final String springXmlLocation) {
 +      this.springXmlLocation = springXmlLocation;
 +      return this;
 +    }
 +
 +    /**
 +     * Gets the working directory pathname in which the Server will be ran.  If the directory is unspecified,
 +     * then working directory defaults to the current directory.
 +     * 
 +     * @return a String indicating the working directory pathname.
 +     * @see #setWorkingDirectory(String)
 +     */
 +    public String getWorkingDirectory() {
 +      return IOUtils.tryGetCanonicalPathElseGetAbsolutePath(
 +        new File(StringUtils.defaultIfBlank(this.workingDirectory, DEFAULT_WORKING_DIRECTORY)));
 +    }
 +
 +    /**
 +     * Sets the working directory in which the Server will be ran.  This also the directory in which all Server files
 +     * (such as log and license files) will be written.  If the directory is unspecified, then the working directory
 +     * defaults to the current directory.
 +     * 
 +     * @param workingDirectory a String indicating the pathname of the directory in which the Server will be ran.
 +     * @return this Builder instance.
 +     * @throws IllegalArgumentException wrapping a FileNotFoundException if the working directory pathname cannot be
 +     * found.
 +     * @see #getWorkingDirectory()
 +     * @see java.io.FileNotFoundException
 +     */
 +    public Builder setWorkingDirectory(final String workingDirectory) {
 +      if (!(new File(StringUtils.defaultIfBlank(workingDirectory, DEFAULT_WORKING_DIRECTORY)).isDirectory())) {
 +        throw new IllegalArgumentException(
 +          LocalizedStrings.Launcher_Builder_WORKING_DIRECTORY_NOT_FOUND_ERROR_MESSAGE.toLocalizedString("Server"),
 +            new FileNotFoundException(workingDirectory));
 +      }
 +      this.workingDirectory = workingDirectory;
 +      return this;
 +    }
 +    
 +    public Float getCriticalHeapPercentage() {
 +      return this.criticalHeapPercentage;
 +    }
 +
 +    public Builder setCriticalHeapPercentage(final Float criticalHeapPercentage) {
 +      if (criticalHeapPercentage != null) {
 +        if (criticalHeapPercentage < 0 || criticalHeapPercentage > 100.0f) {
 +          throw new IllegalArgumentException(String.format("Critical heap percentage (%1$s) must be between 0 and 100!",
 +            criticalHeapPercentage));
 +        }
 +      }
 +      this.criticalHeapPercentage = criticalHeapPercentage;
 +      return this;
 +    }
 +
 +    public Float getCriticalOffHeapPercentage() {
 +      return this.criticalOffHeapPercentage;
 +    }
 +    
 +    public Builder setCriticalOffHeapPercentage(final Float criticalOffHeapPercentage) {
 +      if (criticalOffHeapPercentage != null) {
 +        if (criticalOffHeapPercentage < 0 || criticalOffHeapPercentage > 100.0f) {
 +          throw new IllegalArgumentException(String.format("Critical off-heap percentage (%1$s) must be between 0 and 100!", criticalOffHeapPercentage));
 +        }
 +      }
 +     this.criticalOffHeapPercentage = criticalOffHeapPercentage;
 +     return this;
 +    }
 +    
 +    public Float getEvictionHeapPercentage() {
 +      return this.evictionHeapPercentage;
 +    }
 +
 +    public Builder setEvictionHeapPercentage(final Float evictionHeapPercentage) {
 +      if (evictionHeapPercentage != null) {
 +        if (evictionHeapPercentage < 0 || evictionHeapPercentage > 100.0f) {
 +          throw new IllegalArgumentException(String.format("Eviction heap percentage (%1$s) must be between 0 and 100!",
 +            evictionHeapPercentage));
 +        }
 +      }
 +      this.evictionHeapPercentage = evictionHeapPercentage;
 +      return this;
 +    }
 +    
 +    public Float getEvictionOffHeapPercentage() {
 +      return this.evictionOffHeapPercentage;
 +    }
 +    
 +    public Builder setEvictionOffHeapPercentage(final Float evictionOffHeapPercentage) {
 +      if (evictionOffHeapPercentage != null) {
 +        if (evictionOffHeapPercentage < 0 || evictionOffHeapPercentage > 100.0f) {
 +          throw new IllegalArgumentException(String.format("Eviction off-heap percentage (%1$s) must be between 0 and 100", evictionOffHeapPercentage));
 +        }
 +      }
 +      this.evictionOffHeapPercentage = evictionOffHeapPercentage;
 +      return this;
 +    }
 +    
 +    public String getHostNameForClients() {
 +      return this.hostNameForClients;
 +    }
 +
 +    public Builder setHostNameForClients(String hostNameForClients) {
 +      this.hostNameForClients = hostNameForClients;
 +      return this;
 +    }
 +
 +    public Integer getMaxConnections() {
 +      return this.maxConnections;
 +    }
 +
 +    public Builder setMaxConnections(Integer maxConnections) {
 +      if (maxConnections != null && maxConnections < 1) {
 +        throw new IllegalArgumentException(String.format("Max Connections (%1$s) must be greater than 0!",
 +          maxConnections));
 +      }
 +      this.maxConnections = maxConnections;
 +      return this;
 +    }
 +
 +    public Integer getMaxMessageCount() {
 +      return this.maxMessageCount;
 +    }
 +
 +    public Builder setMaxMessageCount(Integer maxMessageCount) {
 +      if (maxMessageCount != null && maxMessageCount < 1) {
 +        throw new IllegalArgumentException(String.format("Max Message Count (%1$s) must be greater than 0!",
 +          maxMessageCount));
 +      }
 +      this.maxMessageCount = maxMessageCount;
 +      return this;
 +    }
 +


<TRUNCATED>