All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.cache.CacheServerLauncher Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed 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. See accompanying
 * LICENSE file.
 */

package com.gemstone.gemfire.internal.cache;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.net.URL;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

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.DistributedSystem;
import com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.distributed.internal.DistributionConfigImpl;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.OSProcess;
import com.gemstone.gemfire.internal.SocketCreator;
import com.gemstone.gemfire.internal.cache.persistence.PersistentMemberID;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.process.FileAlreadyExistsException;
import com.gemstone.gemfire.internal.process.StartupStatus;
import com.gemstone.gemfire.internal.process.StartupStatusListener;
import com.gemstone.gemfire.internal.shared.ClientSharedUtils;
import com.gemstone.gemfire.internal.shared.NativeCalls;
import com.gemstone.gemfire.internal.shared.SystemProperties;
import com.gemstone.gemfire.internal.util.IOUtils;
import com.gemstone.gemfire.internal.util.JavaCommandBuilder;


/**
 * Launcher program to start a cache server.
 *
 * @author Sudhir Menon
 * @author Barry Oglesby
 * @author David Whitlock
 * @author John Blum
 *
 * @since 2.0.2
 */
public class CacheServerLauncher  {

  /** Is this VM a dedicated Cache Server?  This value is used mainly by the admin API. */
  public static boolean isDedicatedCacheServer = Boolean.getBoolean("gemfire.isDedicatedServer");

  public static boolean ASSIGN_BUCKETS = Boolean.getBoolean("gemfire.CacheServerLauncher.assignBucketsToPartitions");

  //default is to exit if property not defined
  public static boolean DONT_EXIT_AFTER_LAUNCH = Boolean.getBoolean("gemfire.CacheServerLauncher.dontExitAfterLaunch");

  /** Should the launch command be printed? */
  public static final boolean PRINT_LAUNCH_COMMAND = Boolean.getBoolean(
    CacheServerLauncher.class.getSimpleName() + ".PRINT_LAUNCH_COMMAND");

  protected static long STATUS_WAIT_TIME 
    = Long.getLong("gemfire.CacheServerLauncher.STATUS_WAIT_TIME_MS", 15000);
  
  /** How long to wait for a cache server to stop */
  protected static long SHUTDOWN_WAIT_TIME 
    = Long.getLong("gemfire.CacheServerLauncher.SHUTDOWN_WAIT_TIME_MS", 20000);

  protected final String baseName;
  protected final String defaultLogFileName;
  protected final String startLogFileName;
  protected final String pidFileName;
  protected final String statusName;
  protected Status status = null;
  protected File workingDir = null;
  protected PrintStream oldOut = System.out;
  protected PrintStream oldErr = System.err;
  protected LogWriterI18n logger = null;
  protected String maxHeapSize;
  protected String initialHeapSize;
  protected String offHeapSize;
  protected String maxPermGenSize;
  protected boolean useThriftServerDefault;

  protected static CacheServerLauncher instance;

  public static final int SHUTDOWN = 0;
  public static final int STARTING = 1;
  public static final int RUNNING = 2;
  public static final int SHUTDOWN_PENDING = 3;
  public static final int WAITING = 4;
  public static final int STANDBY = 5;

  private static final int FORCE_STATUS_FILE_READ_ITERATION_COUNT = 10;

  public CacheServerLauncher(final String baseName) {
    assert baseName != null : "The base name used for the cache server launcher files cannot be null!";
    this.baseName = baseName;
    final String baseNameLowerCase = getBaseName(baseName);
    this.startLogFileName = "start_" + baseNameLowerCase + ".log";
    this.defaultLogFileName = baseNameLowerCase + ".log";
    this.pidFileName = baseNameLowerCase + ".pid";
    this.statusName = "." + baseNameLowerCase + ".ser";
  }

  protected String getBaseName(final String name) {
    return name.toLowerCase().replace(" ", "");
  }

  protected static Status createStatus(final String baseName, final int state,
      final int pid) {
    return createStatus(baseName, state, pid, null, null);
  }

  protected static Status createStatus(final String baseName, final int state,
      final int pid, final String msg, final Throwable t) {
    final Status status = new Status(baseName);
    status.state = state;
    status.pid = pid;
    status.msg = msg;
    status.exception = t;
    return status;
  }

  /**
   * Prints usage information about this program.
   */
  protected void usage() throws IOException {
    PrintStream out = System.out;
    out.println("cacheserver start [-J]* [=]* [-dir=] [-classpath=] [-disable-default-server] [-rebalance] [-lock-memory] [-server-port=] [-server-bind-address=] [-critical-heap-percentage=] [-eviction-heap-percentage=] [-critical-off-heap-percentage=] [-eviction-off-heap-percentage=]\n" );
    out.println("\t" + LocalizedStrings.CacheServerLauncher_STARTS_A_GEMFIRE_CACHESERVER_VM.toLocalizedString() );
    out.println("\t" + LocalizedStrings.CacheServerLauncher_VMARG.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_DIR.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_CLASSPATH.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_ATTNAME.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_REBALANCE.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_DISABLE_DEFAULT_SERVER.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_SERVER_PORT.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_SERVER_BIND_ADDRESS.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_CRITICAL_HEAP_PERCENTAGE.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_EVICTION_HEAP_PERCENTAGE.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_CRITICAL_OFF_HEAP_PERCENTAGE.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_EVICTION_OFF_HEAP_PERCENTAGE.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_LOCK_MEMORY.toLocalizedString());

    out.println();
    out.println( "cacheserver stop [-dir=]" );
    out.println("\t" + LocalizedStrings.CacheServerLauncher_STOPS_A_GEMFIRE_CACHESERVER_VM.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_DIR.toLocalizedString());
    out.println();
    out.println( "cacheserver status [-dir=]" );
    out.println( "\t" + LocalizedStrings.CacheServerLauncher_STATUS.toLocalizedString());
    out.println("\t" + LocalizedStrings.CacheServerLauncher_DIR.toLocalizedString());
  }

  /**
   * Prints the status of the cache server running the configured
   * working directory.
   */
  protected void status(final String[] args) throws Exception {
    workingDir = (File) getStopOptions(args).get(DIR);
    System.out.println(getStatus());
    if (DONT_EXIT_AFTER_LAUNCH) {
      return;
    }
    System.exit(0);
  }

  /**
   * Wait for the member's status to become "running".
   */
  protected void waitForRunning(final String[] args) throws Exception {
    workingDir = (File)getStopOptions(args).get(DIR);

    // go for wait only if the status is not already SHUTDOWN
    final Status status = getStatus();
    if (status == null) {
      throw new Exception(
          LocalizedStrings.CacheServerLauncher_NO_AVAILABLE_STATUS
              .toLocalizedString(this.statusName));
    }
    if (status.state != SHUTDOWN) {
      if (status.state == SHUTDOWN_PENDING) {
        pollCacheServerForShutdown();
      }
      else {
        waitForRunning((Properties)null);
      }
    }
    else {
      System.out.println(LocalizedStrings.CacheServerLauncher_0_STOPPED
          .toLocalizedString(this.baseName));
    }
    System.exit(0);
  }

  /**
   * Returns the Status of the cache server in the
   * workingDir.
   */
  protected Status getStatus() throws Exception {
    Status status;

    if (new File(workingDir, statusName).exists()) {
      status = spinReadStatus(false); // See bug 32456
    }
    else {
      // no pid since the cache server is not running
      status = createStatus(this.baseName, SHUTDOWN, 0);
    }

    return status;
  }

  public static CacheServerLauncher getCurrentInstance() {
    return instance;
  }

  /**
   * Main method that parses the command line and performs an
   * will start, stop, or get the status of a cache server.  This main
   * method is also the main method of the launched cacher server VM
   * ("server" mode).
   */
  public static void main(final String[] args) {
    final CacheServerLauncher launcher = new CacheServerLauncher("CacheServer");
    instance = launcher;
    boolean inServer = false;

    try {
      if (args.length > 0) {
        if (args[0].equalsIgnoreCase("start")) {
          launcher.start(args);
        }
        else if (args[0].equalsIgnoreCase("server")) {
          inServer = true;
          launcher.server(args);
        }
        else if (args[0].equalsIgnoreCase("stop")) {
          launcher.stop(args);
        }
        else if (args[0].equalsIgnoreCase("status")) {
          launcher.status(args);
        }
        else {
          launcher.usage();
          System.exit(1);
        }
      }
      else {
        launcher.usage();
        System.exit(1);
      }

      throw new Exception(LocalizedStrings.CacheServerLauncher_INTERNAL_ERROR_SHOULDNT_REACH_HERE.toLocalizedString());
    }
    catch (Throwable t) {
      Error err;
      if (t instanceof Error && SystemFailure.isJVMFailureError(
          err = (Error)t)) {
        SystemFailure.initiateFailure(err);
        // If this ever returns, rethrow the error. We're poisoned
        // now, so don't let this thread continue.
        throw err;
      }
      // Whenever you catch Error or Throwable, you must also
      // check for fatal JVM error (see above).  However, there is
      // _still_ a possibility that you are dealing with a cascading
      // error condition, so you also need to check to see if the JVM
      // is still usable:
      SystemFailure.checkFailure();
      t.printStackTrace();
      if (inServer) {
        launcher.setServerError(LocalizedStrings.CacheServerLauncher_ERROR_STARTING_SERVER_PROCESS
          .toLocalizedString(), t);
      }
      launcher.restoreStdOut();
      if (launcher.logger != null) {
        launcher.logger.severe(LocalizedStrings.CacheServerLauncher_CACHE_SERVER_ERROR, t);

      }
      else {
        System.out.println(LocalizedStrings.CacheServerLauncher_ERROR_0.toLocalizedString(t.getMessage()));
      }
      System.exit(1);
    } finally {
      instance = null;
    }
  }

  protected void restoreStdOut( ) {
    System.setErr( oldErr );
    System.setOut( oldOut );
  }

  protected static final String DIR     = "dir";
  protected static final String VMARGS  = "vmargs";
  protected static final String ENVARGS = "envargs";
  protected static final String PROPERTIES = "properties";
  protected static final String CLASSPATH = "classpath";
  public static final String REBALANCE = "rebalance";
  public static final String SERVER_PORT = "server-port";
  public static final String SERVER_BIND_ADDRESS = "server-bind-address";
  public static final String DISABLE_DEFAULT_SERVER = "disable-default-server";
  public static final String CRITICAL_HEAP_PERCENTAGE =
    "critical-heap-percentage";
  public static final String EVICTION_HEAP_PERCENTAGE =
      "eviction-heap-percentage";
  public static final String CRITICAL_OFF_HEAP_PERCENTAGE =
      "critical-off-heap-percentage";
  public static final String EVICTION_OFF_HEAP_PERCENTAGE =
      "eviction-off-heap-percentage";
  protected static final String LOCK_MEMORY = "lock-memory";

  protected static final String MAX_PERM_SIZE = "-XX:MaxPermSize";
  protected static final String MAX_PERM_DEFAULT = "=128m";
  
  protected final File processDirOption(final Map options, final String dirValue) throws FileNotFoundException {
    final File inputWorkingDirectory = new File(dirValue);

    if (!inputWorkingDirectory.exists()) {
      throw new FileNotFoundException(LocalizedStrings.CacheServerLauncher_THE_INPUT_WORKING_DIRECTORY_DOES_NOT_EXIST_0
        .toLocalizedString(dirValue));
    }

    options.put(DIR, inputWorkingDirectory);

    return inputWorkingDirectory;
  }

  /**
   * Populates a map that maps the name of the start options such as {@link #DIR} to its value on the command line.
   * If no value is specified on the command line, a default one is provided.
   */
  public Map getStartOptions(String[] args) throws Exception {
    final Map options = new HashMap();
    options.put(DIR, new File(System.getProperty("user.dir")));

    List vmArgs = new ArrayList();
    
    final Map envArgs = new HashMap();
    options.put(ENVARGS, envArgs);

    final Properties props = new Properties();
    options.put(PROPERTIES, props);

    for (final String arg : args) {
      if(arg == null) {
        continue;
      }
      if (arg.equals("start")) {
        // expected
      }
      else if (arg.startsWith("-classpath=")) {
        options.put(CLASSPATH, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-dir=")) {
        processDirOption(options, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-disable-default-server")) {
        options.put(DISABLE_DEFAULT_SERVER, arg);
      }
      else if (arg.startsWith("-lock-memory")) {
        if (System.getProperty("os.name").indexOf("Windows") >= 0) {
          throw new IllegalArgumentException("Unable to lock memory on this operating system");
        }
        props.put(DistributionConfig.LOCK_MEMORY_NAME, "true");
      }
      else if (arg.startsWith("-rebalance")) {
        options.put(REBALANCE, Boolean.TRUE);
      }
      else if (arg.startsWith("-server-port")) {
        options.put(SERVER_PORT, arg);
      }
      else if (arg.startsWith("-" + CRITICAL_HEAP_PERCENTAGE) ) {
        options.put(CRITICAL_HEAP_PERCENTAGE, arg);
      }
      else if (arg.startsWith("-" + EVICTION_HEAP_PERCENTAGE) ) {
        options.put(EVICTION_HEAP_PERCENTAGE, arg);
      }
      else if (arg.startsWith("-" + CRITICAL_OFF_HEAP_PERCENTAGE) ) {
        options.put(CRITICAL_OFF_HEAP_PERCENTAGE, arg);
      }
      else if (arg.startsWith("-" + EVICTION_OFF_HEAP_PERCENTAGE) ) {
        options.put(EVICTION_OFF_HEAP_PERCENTAGE, arg);
      }
      else if (arg.startsWith("-server-bind-address")) {
        options.put(SERVER_BIND_ADDRESS, arg);
      }
      else if (arg.startsWith("-J")) {
        String vmArg = arg.substring(2);
        String thriftArg;
        if (vmArg.startsWith("-Xmx")) {
          this.maxHeapSize = vmArg.substring(4);
        } else if (vmArg.startsWith("-Xms")) {
          this.initialHeapSize = vmArg.substring(4);
        } else if (vmArg.startsWith(MAX_PERM_SIZE)) {
          this.maxPermGenSize = vmArg;
        } else if (vmArg.startsWith(thriftArg = ("-D"
            + SystemProperties.getServerInstance().getSystemPropertyNamePrefix()
            + ClientSharedUtils.USE_THRIFT_AS_DEFAULT_PROP))) {
          int len = thriftArg.length();
          if (vmArg.length() > (len + 1) && vmArg.charAt(len) == '=') {
            this.useThriftServerDefault = Boolean.parseBoolean(vmArg.substring(
                len + 1).trim());
          }
        }
        vmArgs.add(vmArg);
      }
      else if (arg.length() > 0) {
        // moved this default block down so that "-J" like options can have '=' in them.
        // an 'indexOf' the assignment operator with greater than 0 ensures a non-empty String key value
        if (arg.indexOf("=") > 0) {
          final int assignmentIndex = arg.indexOf("=");
          final String key = arg.substring(0, assignmentIndex);
          final String value = arg.substring(assignmentIndex + 1);
          if (key.startsWith("-")) {
            if (key.substring(1).startsWith("-")) {
              throw new IllegalArgumentException(
                  LocalizedStrings.CacheServerLauncher_UNKNOWN_ARGUMENT_0
                      .toLocalizedString(arg));
            }   
            processStartOption(key.substring(1), value, options, vmArgs, envArgs, props);
          }
          else {
            processStartArg(key, value, options, vmArgs, props);
          }
        }
        else if (arg.equalsIgnoreCase("-password")) {
          processStartOption(arg.substring(1), null, options, vmArgs, envArgs, props);
        }
        else {
          throw new IllegalArgumentException(
              LocalizedStrings.CacheServerLauncher_UNKNOWN_ARGUMENT_0
                  .toLocalizedString(arg));
        }
      }
      else {
        throw new IllegalArgumentException(LocalizedStrings.CacheServerLauncher_UNKNOWN_ARGUMENT_0
          .toLocalizedString(arg));
      }
    }

    // -J-Djava.awt.headless=true has been added for Mac platform where it
    // causes an icon to appear for gfxd launched procs
    // TODO: check which library/GemFire code causes awt to be touched
    vmArgs.add("-Djava.awt.headless=true");

    // configure commons-logging to use Log4J logging
    vmArgs.add("-Dorg.apache.commons.logging.Log=" +
        "org.apache.commons.logging.impl.Log4JLogger");

    vmArgs = postProcessOptions(vmArgs, options);
    options.put(VMARGS, vmArgs);
    return options;
  }

  /**
   * Process a command-line options of the form "key=value".
   */
  protected void processStartArg(final String key,
                                 final String value,
                                 final Map options,
                                 final List vmArgs,
                                 final Properties props)
    throws Exception
  {
    props.setProperty(key, value);
  }

  /**
   * Process a command-line option of the form "-key=value".
   * @param envArgs environment variables in the form of 'name=value'. 
   */
  protected void processStartOption(final String key,
                                    final String value,
                                    final Map options,
                                    final List vmArgs,
                                    final Map envArgs,
                                    final Properties props)
    throws Exception
  {
    processUnknownStartOption(key, value, options, vmArgs, props);
  }

  /**
   * Process a command-line option of the form "-key=value" unknown to the base class.
   */
  protected void processUnknownStartOption(final String key,
                                           final String value,
                                           final Map options,
                                           final List vmArgs,
                                           final Properties props) {
    throw new IllegalArgumentException(LocalizedStrings.CacheServerLauncher_UNKNOWN_ARGUMENT_0.toLocalizedString(key));
  }

  /**
   * Extracts configuration information used when launching the cache server VM.
   */
  protected Map getServerOptions(final String[] args) throws Exception {
    final Map options = new HashMap();
    options.put(DIR, new File("."));
    workingDir = (File) options.get(DIR);

    final Properties props = new Properties();
    options.put(PROPERTIES, props);

    for (final String arg : args) {
      if (arg.equals("server")) {
        // expected
      }
      else if (arg.startsWith("-dir=")) {
        this.workingDir = processDirOption(options, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-rebalance")) {
        options.put(REBALANCE, Boolean.TRUE);
      }
      else if (arg.startsWith("-disable-default-server")) {
        options.put(DISABLE_DEFAULT_SERVER, Boolean.TRUE);
      }
      else if (arg.startsWith("-lock-memory")) {
        props.put(DistributionConfig.LOCK_MEMORY_NAME, "true");
      }
      else if (arg.startsWith("-server-port")) {
        options.put(SERVER_PORT, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-server-bind-address")) {
        options.put(SERVER_BIND_ADDRESS, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-" + CRITICAL_HEAP_PERCENTAGE)) {
        options.put(CRITICAL_HEAP_PERCENTAGE, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-" + EVICTION_HEAP_PERCENTAGE)) {
        options.put(EVICTION_HEAP_PERCENTAGE, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-" + CRITICAL_OFF_HEAP_PERCENTAGE)) {
        options.put(CRITICAL_OFF_HEAP_PERCENTAGE, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.startsWith("-" + EVICTION_OFF_HEAP_PERCENTAGE)) {
        options.put(EVICTION_OFF_HEAP_PERCENTAGE, arg.substring(arg.indexOf("=") + 1));
      }
      else if (arg.indexOf("=") > 1) {
        final int assignmentIndex = arg.indexOf("=");
        final String key = arg.substring(0, assignmentIndex);
        final String value = arg.substring(assignmentIndex + 1);

        if (key.startsWith("-")) {
          options.put(key.substring(1), value);
        }
        else {
          props.setProperty(key, value);
        }
      }
      else {
        throw new IllegalArgumentException(LocalizedStrings.CacheServerLauncher_UNKNOWN_ARGUMENT_0.toLocalizedString(arg));
      }
    }
    // any last minute processing of environment variables or otherwise
    processServerEnv(props);

    return options;
  }

  protected void processServerEnv(Properties props) throws Exception {
  }

  /**
   * Extracts configuration information for stopping a cache server based on the contents of the command-line.
   * This method can also be used with getting the status of a cache server.
   */
  protected Map getStopOptions(final String[] args) throws Exception {
    final Map options = new HashMap();
    options.put(DIR, new File("."));

    for (final String arg : args) {
      if (arg.equals("stop") || arg.equals("status") || arg.equals("wait")) {
        // expected
      }
      else if (arg.startsWith("-dir=")) {
        processDirOption(options, arg.substring(arg.indexOf("=") + 1));
      }
      else {
        throw new IllegalArgumentException(LocalizedStrings.CacheServerLauncher_UNKNOWN_ARGUMENT_0
          .toLocalizedString(arg));
      }
    }

    return options;
  }

  protected List postProcessOptions(List vmArgs,
      final Map map) {
    return vmArgs;
  }

  /**
   * Configures and spawns a VM that hosts a cache server.  Any output
   * generated while starting the VM will be placed in a special
   * {@linkplain #startLogFileName log file}.
   *
   * See #getStartOptions
   * @see OSProcess#bgexec(String[], File, File, boolean, Map)
   */
  public void start(final String[] args) throws Exception {
    final Map options = getStartOptions(args);

    workingDir = (File) options.get(DIR);

    // Complain if a cache server is already running in the specified working directory.
    // See bug 32574.
    verifyAndClearStatus();

    final Properties props = (Properties)options.get(PROPERTIES);
    // start the GemFire Cache Server proces...
    runCommandLine(options, props, buildCommandLine(options));

    // wait for status.state == RUNNING
    waitForRunning(props);

    if (DONT_EXIT_AFTER_LAUNCH) {
      return;
    }

    System.exit(0);
  }

  private void verifyAndClearStatus() throws Exception {
    final Status status = getStatus();

    if (status != null && status.state != SHUTDOWN) {
      throw new IllegalStateException(LocalizedStrings.CacheServerLauncher_A_0_IS_ALREADY_RUNNING_IN_DIRECTORY_1_2
        .toLocalizedString(this.baseName, workingDir, status));
    }

    deleteStatus();
  }

  private String[] buildCommandLine(final Map options) {
    final List commandLine = JavaCommandBuilder.buildCommand(this.getClass().getName(),
      (String) options.get(CLASSPATH), null, (List) options.get(VMARGS));

    commandLine.add("server");
    addToServerCommand(commandLine, options);

    return commandLine.toArray(new String[commandLine.size()]);
  }

  protected void printCommandLine(final String[] commandLine,
      final Map env) throws Exception {
    if (printLaunchCommand()) {
      System.out.println("Starting " + this.baseName + " with command:");
      for (final String command : commandLine) {
        System.out.print(command);
        System.out.print(" ");
      }
      System.out.println();
    }
  }

  public void printCommandLine(LogWriterI18n logger) {
    // nothing in GemFire
  }

  private int runCommandLine(final Map options,
      Properties props, final String[] commandLine) throws Exception {
    final File startLogFile = new File(workingDir, startLogFileName).getAbsoluteFile(); // see bug 32548

    if (startLogFile.exists() && !startLogFile.delete()) {
      throw new IOException("Unable to delete start log file (" + startLogFile.getAbsolutePath() + ")!");
    }
    final File pidFile = new File(workingDir, pidFileName).getAbsoluteFile(); // see bug 32548

    if (pidFile.exists() && !pidFile.delete()) {
      throw new IOException("Unable to delete pid file (" + pidFile.getAbsolutePath() + ")!");
    }
    Map env = (Map)options.get(ENVARGS);
    if (env == null) {
      env = new HashMap();
    }
    // read the passwords from command line
    SocketCreator.readSSLProperties(env);

    printCommandLine(commandLine, env);

    final int pid = OSProcess.bgexec(commandLine, workingDir, startLogFile,
        false, env);
    printStartMessage(options, props, pid);

    return pid;
  }

  protected void printStartMessage(final Map options,
      final Properties props, final int pid) throws Exception {
    System.out
        .println(LocalizedStrings.CacheServerLauncher_STARTING_0_WITH_PID_1
            .toLocalizedString(this.baseName, pid));
  }

  /**
   * Sets the status of the cache server to be {@link #RUNNING}.
   */
  public void running(final InternalDistributedSystem system) {
    Status stat = this.status;
    if (stat == null) {
      stat = this.status = createStatus(this.baseName, RUNNING, getProcessId());
    }
    else {
      if (stat.state == WAITING) {
        stat.dsMsg = null;
      } else {
        stat.state = RUNNING;
      }
    }
    setRunningStatus(stat, system);
  }

  protected void setRunningStatus(final Status stat,
      final InternalDistributedSystem system) {
    try {
      writeStatus(stat);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void setWaitingStatus(String regionPath,
      Set membersToWaitFor, Set missingBuckets,
      PersistentMemberID myId, String message) {
    try {
      Status stat = this.status;
      if (stat == null) {
        stat = this.status = createStatus(this.baseName, WAITING,
            getProcessId(), null, null);
      }
      else {
        stat.state = WAITING;
      }
      stat.dsMsg = message;
      writeStatus(stat);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static ThreadLocal serverPort = new ThreadLocal();

  public static ThreadLocal serverBindAddress = new ThreadLocal();

  public static Integer getServerPort() {
    return serverPort.get();
  }

  public static String getServerBindAddress() {
    return serverBindAddress.get();
  }

  public static ThreadLocal disableDefaultServer = new ThreadLocal();

  public static Boolean getDisableDefaultServer() {
    return disableDefaultServer.get();
  }

  /**
   * The method that does the work of being a cache server.  It is
   * invoked in the VM spawned by the {@link #start} method.
   * Basically, it creates a GemFire {@link Cache} based on
   * configuration passed in from the command line.  (It will also
   * take gemfire.properties, etc. into account, just
   * like an application cache.)
   *
   * 

* * After creating the cache and setting the server's status to {@link * #RUNNING}, it periodically monitors the status, waiting for it to * change to {@link #SHUTDOWN_PENDING} (see {@link #stop}). When * the status does change, it closes the Cache and sets * the status to be {@link #SHUTDOWN}. * * @param args Configuration options passed in from the command line */ @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD") public void server(final String[] args) throws Exception { isDedicatedCacheServer = true; // make the process a UNIX daemon if possible String errMsg = makeDaemon(); SystemFailure.setExitOK(true); final Map options = getServerOptions(args); final String serverPortString = (String) options.get(SERVER_PORT); if (serverPortString != null) { serverPort.set(Integer.parseInt(serverPortString)); } serverBindAddress.set((String) options.get(SERVER_BIND_ADDRESS)); disableDefaultServer.set((Boolean) options.get(DISABLE_DEFAULT_SERVER)); workingDir = new File(System.getProperty("user.dir")); // Say that we're starting... Status originalStatus = createStatus(this.baseName, STARTING, getProcessId()); status = originalStatus; status.msg = errMsg; writeStatus(status); // Connect to the distributed system. The properties will // properly configure logging, the declarative caching file, etc. final Properties props = (Properties) options.get(PROPERTIES); if (props.getProperty(DistributionConfig.LOG_FILE_NAME) == null && CacheServerLauncher.isLoggingToStdOut()) { // Check First if the gemfire.properties set the log-file. If they do, we shouldn't override that default final Properties gemfireProperties = new Properties(); DistributionConfigImpl.loadGemFireProperties(gemfireProperties); if (gemfireProperties.get(DistributionConfig.LOG_FILE_NAME) == null) { // Do not allow the cache server to log to stdout, override the logger with #defaultLogFileName props.setProperty(DistributionConfig.LOG_FILE_NAME, defaultLogFileName); } } installLogListener(); final AsyncServerLauncher launcher = this.new AsyncServerLauncher(this, props, options); final Thread serverConnector = new Thread( launcher, "serverConnector"); serverConnector.start(); while(true) { serverConnector.join(500); if (serverConnector.isAlive()) { Status s = readStatus(true); if (s.state == SHUTDOWN_PENDING || s.state == SHUTDOWN) { InternalDistributedSystem ids = InternalDistributedSystem.getConnectedInstance(); if (ids != null) { ids.disconnect(); } serverConnector.interrupt(); s.state = SHUTDOWN; writeStatus(s); System.exit(0); } else { Thread.sleep( 250 ); } } else { break; } } InternalDistributedSystem system = launcher.getSystem(); logger = system.getLogWriter().convertToLogWriterI18n(); // redirect output to the log file redirectOutputToLogFile(system); Cache cache = this.createCache(system, options); cache.setIsServer(true); startAdditionalServices(cache, options, props); this.running(system); clearLogListener(); if (ASSIGN_BUCKETS) { for (PartitionedRegion region : ((GemFireCacheImpl) cache).getPartitionedRegions()) { PartitionRegionHelper.assignBucketsToPartitions(region); } } // start rebalancing if specified startRebalanceFactory(cache, options); File statusFile = new File( workingDir, statusName ); long lastModified=0, oldModified = statusFile.lastModified(); // Every FORCE_STATUS_FILE_READ_ITERATION_COUNT iterations, read the status file despite the modification time // to catch situations where the file is modified quicker than the file timestamp's resolution. short count = 0; boolean externalShutDown = true; boolean loggedWarning = false; while(true) { lastModified = statusFile.lastModified(); if (lastModified > oldModified || count++ == FORCE_STATUS_FILE_READ_ITERATION_COUNT) { count = 0; Thread.sleep( 500 ); // allow for it to be finished writing. //Sometimes the status file is partially written causing readObject to //fail, sleep and retry. try { status = readStatus(false); } catch(IOException ioeSecondChance) { Thread.sleep(1000); try { status = readStatus(false); } catch(IOException ioeThirdChance) { Thread.sleep(5000); try { status = readStatus(false); } catch (FileNotFoundException fnfe) { // See bug 44627. // The cache server used to just shutdown at this point. Instead, // recreate the status file if possible and continue. status = createStatus(this.baseName, RUNNING, originalStatus.pid); try { writeStatus(status); } catch (FileNotFoundException e) { if (!loggedWarning) { logger.warning(LocalizedStrings.CacheServerLauncher_CREATE_STATUS_EXCEPTION_0, e.toString()); loggedWarning = true; } } } } } oldModified = lastModified; if (status.state == SHUTDOWN_PENDING) { stopAdditionalServices(); this.disconnect(cache); status.state = SHUTDOWN; writeStatus(status); externalShutDown = false; } else { Thread.sleep( 250 ); } } else { Thread.sleep(1000); } if (!system.isConnected()) { // System.out.println("System is disconnected. isReconnecting = " + system.isReconnecting()); boolean reconnected = false; if (system.isReconnecting()) { reconnected = system.waitUntilReconnected(-1, TimeUnit.SECONDS); if (reconnected) { system = (InternalDistributedSystem)system.getReconnectedSystem(); cache = GemFireCacheImpl.getInstance(); } } if (!reconnected) { // shutdown-all disconnected the DS if( externalShutDown) { //delete the status file deleteStatus(); } System.exit(0); } } } } class AsyncServerLauncher implements Runnable { private CacheServerLauncher _this; private Properties props; private Map options; private InternalDistributedSystem system; private Throwable exception; AsyncServerLauncher(CacheServerLauncher cs, Properties props, Map options) { _this = cs; this.props = props; this.options = options; } @Override public void run() { try { system = _this.connect(props, options); } catch(Throwable t) { exception = t; } } public InternalDistributedSystem getSystem() { if (exception != null) { throw new RuntimeException(exception); } return system; } } protected void redirectOutputToLogFile(InternalDistributedSystem system) throws Exception { DistributionConfig config = system.getConfig(); OSProcess.redirectOutput(config.getLogFile()); } protected String makeDaemon() { try { NativeCalls.getInstance().daemonize(null); } catch (UnsupportedOperationException e) { return "WARNING: " + e.getMessage(); } catch (IllegalStateException ignored) { } return null; } private void installLogListener() { MainLogReporter reporter = new MainLogReporter(this.status); StartupStatus.setListener(reporter); reporter.setDaemon(true); reporter.start(); } private void clearLogListener() { MainLogReporter mainLogListener = (MainLogReporter)StartupStatus .getStartupListener(); if(mainLogListener != null) { mainLogListener.shutdown(); StartupStatus.clearListener(); } } protected InternalDistributedSystem connect(Properties props, Map options) { return (InternalDistributedSystem)DistributedSystem.connect(props); } protected static float getCriticalHeapPercent(Map options) { if (options != null) { String criticalHeapThreshold = (String)options .get(CRITICAL_HEAP_PERCENTAGE); if (criticalHeapThreshold != null) { return Float.parseFloat(criticalHeapThreshold .substring(criticalHeapThreshold.indexOf("=") + 1)); } } return -1.0f; } protected static float getEvictionHeapPercent(Map options) { if (options != null) { String evictionHeapThreshold = (String)options .get(EVICTION_HEAP_PERCENTAGE); if (evictionHeapThreshold != null) { return Float.parseFloat(evictionHeapThreshold .substring(evictionHeapThreshold.indexOf("=") + 1)); } } return -1.0f; } protected static float getCriticalOffHeapPercent(Map options) { if (options != null) { String criticalOffHeapThreshold = (String)options .get(CRITICAL_OFF_HEAP_PERCENTAGE); if (criticalOffHeapThreshold != null) { return Float.parseFloat(criticalOffHeapThreshold .substring(criticalOffHeapThreshold.indexOf("=") + 1)); } } return -1.0f; } protected static float getEvictionOffHeapPercent(Map options) { if (options != null) { String evictionOffHeapThreshold = (String)options .get(EVICTION_OFF_HEAP_PERCENTAGE); if (evictionOffHeapThreshold != null) { return Float.parseFloat(evictionOffHeapThreshold .substring(evictionOffHeapThreshold.indexOf("=") + 1)); } } return -1.0f; } protected Cache createCache(InternalDistributedSystem system, Map options) throws IOException { Cache cache = CacheFactory.create(system); float threshold = getCriticalHeapPercent(options); if (threshold > 0.0f) { cache.getResourceManager().setCriticalHeapPercentage(threshold); } threshold = getEvictionHeapPercent(options); if (threshold > 0.0f) { cache.getResourceManager().setEvictionHeapPercentage(threshold); } threshold = getCriticalOffHeapPercent(options); getCriticalOffHeapPercent(options); if (threshold > 0.0f) { cache.getResourceManager().setCriticalOffHeapPercentage(threshold); } threshold = getEvictionOffHeapPercent(options); if (threshold > 0.0f) { cache.getResourceManager().setEvictionOffHeapPercentage(threshold); } // Create and start a default cache server // If (disableDefaultServer is not set or it is set but false) AND (the number of cacheservers is 0) Boolean disable = disableDefaultServer.get(); if ((disable == null || !disable) && cache.getCacheServers().size() == 0) { // Create and add a cache server CacheServer server = cache.addCacheServer(); // Set its port if necessary Integer serverPort = CacheServerLauncher.getServerPort(); if (serverPort != null) { server.setPort(serverPort); } // Set its bind address if necessary String serverBindAddress = getServerBindAddress(); if (serverBindAddress != null) { server.setBindAddress(serverBindAddress.trim()); } // Start it server.start(); } return cache; } protected void disconnect(Cache cache) { DistributedSystem dsys = cache.getDistributedSystem(); cache.close(); dsys.disconnect(); } protected void startRebalanceFactory(final Cache cache, final Map options) { Boolean rebalanceOnStartup = (Boolean)options.get(REBALANCE); if (rebalanceOnStartup != null && rebalanceOnStartup.booleanValue()) { cache.getResourceManager().createRebalanceFactory().start(); } } /** * Stops a cache server (which is running in a different VM) by setting its status to {@link #SHUTDOWN_PENDING}. * Waits for the cache server to actually shut down. */ public void stop(final String[] args) throws Exception { this.workingDir = (File) getStopOptions(args).get(DIR); // determine the current state of the Cache Server process... final File statusFile = new File(this.workingDir, this.statusName); int exitStatus = 1; if (statusFile.exists()) { this.status = spinReadStatus(false); if (status == null) { throw new Exception( LocalizedStrings.CacheServerLauncher_NO_AVAILABLE_STATUS .toLocalizedString(this.statusName)); } // upon reading the status file, request the Cache Server to shutdown if it has not already... if (this.status.state != SHUTDOWN) { // copy server PID and not use own PID; see bug #39707 this.status = createStatus(this.baseName, SHUTDOWN_PENDING, this.status.pid); writeStatus(this.status); } // poll the Cache Server for a response to our shutdown request (passes through if the Cache Server // has already shutdown)... pollCacheServerForShutdown(); // after polling, determine the status of the Cache Server one last time and determine how to exit... if (this.status.state == SHUTDOWN) { System.out.println(LocalizedStrings.CacheServerLauncher_0_STOPPED.toLocalizedString(this.baseName)); deleteStatus(); exitStatus = 0; } else { System.out.println(LocalizedStrings.CacheServerLauncher_TIMEOUT_WAITING_FOR_0_TO_SHUTDOWN_STATUS_IS_1 .toLocalizedString(this.baseName, this.status)); } } else { System.out.println(LocalizedStrings.CacheServerLauncher_THE_SPECIFIED_WORKING_DIRECTORY_0_CONTAINS_NO_STATUS_FILE .toLocalizedString(this.workingDir)); } if (DONT_EXIT_AFTER_LAUNCH) { return; } System.exit(exitStatus); } protected void pollCacheServerForShutdown() throws InterruptedException { final int increment = 250; // unit is in milliseconds int clock = 0; // wait for a default total of 20000 milliseconds (or 20 seconds) while (clock < SHUTDOWN_WAIT_TIME && status.state != SHUTDOWN) { try { status = readStatus(false); } catch (IOException ignore) { } try { Thread.sleep(increment); } catch (InterruptedException ie) { break; } clock += increment; } } /** * A class that represents the status of a cache server. Instances * of this class are serialized to a {@linkplain #statusName file} * on disk. * * @see #SHUTDOWN * @see #STARTING * @see #RUNNING * @see #SHUTDOWN_PENDING * @see #WAITING */ public static class Status implements Serializable { private static final long serialVersionUID = 190943081363646485L; public int state = 0; public int pid = 0; private final String baseName; public Throwable exception; public String msg; public String dsMsg; public Status(String baseName) { this.baseName = baseName; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append(this.baseName).append(" pid: ").append(pid).append(" status: "); switch (state) { case SHUTDOWN: buffer.append("stopped"); break; case STARTING: buffer.append("starting"); break; case RUNNING: buffer.append("running"); break; case SHUTDOWN_PENDING: buffer.append("stopping"); break; case WAITING: buffer.append("waiting"); break; case STANDBY: buffer.append("standby"); break; default: buffer.append("unknown"); break; } if (exception != null || msg != null) { if (msg != null) { buffer.append("\n").append(msg); } else { buffer.append("\nException in ").append(this.baseName); } if ((state != WAITING && state != RUNNING) || msg == null) { buffer.append(" - ").append(LocalizedStrings .CacheServerLauncher_SEE_LOG_FILE_FOR_DETAILS.toLocalizedString()); } } if (this.dsMsg != null) { buffer.append('\n').append(this.dsMsg); } return buffer.toString(); } } /** * Notes that an error has occurred in the cache server and that it * has shut down because of it. */ protected void setServerError(final String msg, final Throwable t) { try { writeStatus(createStatus(this.baseName, SHUTDOWN, getProcessId(), msg, t)); } catch (Exception e) { if (logger != null) { logger.severe(e); } else { e.printStackTrace(); } System.exit(1); } } protected int getProcessId() { return NativeCalls.getInstance().getProcessId(); } /** * Sets the status of a cache server by serializing a Status * instance to a file in the server's working directory. */ public void writeStatus(final Status s) throws IOException { final File statusFile = new File(workingDir, statusName); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = null; RandomAccessFile raFile = null; try { if (!statusFile.exists()) { NativeCalls.getInstance().preBlow(statusFile.getAbsolutePath(), 2048, true); } raFile = new RandomAccessFile(statusFile.getAbsolutePath(), "rw"); out = new ObjectOutputStream(bos); out.writeObject(s); raFile.write(bos.toByteArray()); } finally { if (out != null) { try { out.close(); } catch (IOException ignore) { } } bos.close(); IOUtils.close(raFile); } } /** * Reads a cache server's status. If the status file cannot be read because of I/O problems, it will try again. */ protected Status spinReadStatus(boolean starting) { final long timeout = (System.currentTimeMillis() + 60000); Status status = null; while (status == null && System.currentTimeMillis() < timeout) { try { status = readStatus(starting); } catch (Exception e) { // try again - the status might have been read in the middle of it being written by the server resulting in // an EOFException here try { Thread.sleep(500); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); status = null; break; } } } return status; } /** * Reads a cache server's status from a file in its working directory. */ protected Status readStatus(boolean starting) throws InterruptedException, FileNotFoundException, IOException { final File statusFile = new File(workingDir, statusName); FileInputStream fileInput = null; ObjectInputStream objectInput = null; int maxTries = 1000; while (true) { try { fileInput = new FileInputStream(statusFile); objectInput = new ObjectInputStream(fileInput); Status status = (Status) objectInput.readObject(); // See bug 32760 // Note, only execute the conditional createStatus statement if we are in // native mode; if we are in pure Java mode // the the process ID identified in the Status object is assumed to exist! if (status.state != SHUTDOWN && status.pid > 0 && !isExistingProcess(status.pid)) { status = createStatus(this.baseName, SHUTDOWN, status.pid); } return status; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (FileNotFoundException e) { Thread.sleep(500); if (statusFile.exists()) { if (maxTries-- > 0) { continue; } else { throw e; } } else { throw e; } } finally { IOUtils.close(objectInput); IOUtils.close(fileInput); } } } /** * Removes a cache server's status file */ private void deleteStatus() throws IOException { final File statusFile = new File(workingDir, statusName); if (statusFile.exists() && !statusFile.delete()) { throw new IOException("Could not delete status file (" + statusFile.getAbsolutePath() + ")!"); } } public static final boolean isExistingProcess(final int pid) { try { return NativeCalls.getInstance().isProcessActive(pid); } catch (UnsupportedOperationException uoe) { // no native API to determine if process exists, so assume it to be true return true; } } protected void waitForRunning(final Properties props) throws Exception { Status status = spinReadStatus(true); String lastReadMessage = null; String lastReportedMessage = null; long lastReadTime = System.nanoTime(); if ( status == null ) { throw new Exception( LocalizedStrings.CacheServerLauncher_NO_AVAILABLE_STATUS .toLocalizedString(this.statusName)); } else { // Properties as null means no log-file message needs to be displayed if (props != null) { String logFilePath = getLogFilePath(props); // if log does not exist then point System.out.println(LocalizedStrings.CacheServerLauncher_LOGS_GENERATED_IN .toLocalizedString(logFilePath)); } if (checkStatusForWait(status)) { // re-read status for a while... while (checkStatusForWait(status)) { Thread.sleep( 500 ); // fix for bug 36998 status = spinReadStatus(true); if (status == null) { throw new Exception( LocalizedStrings.CacheServerLauncher_NO_AVAILABLE_STATUS .toLocalizedString(this.statusName)); } //check to see if the status message has changed if(status.dsMsg != null && !status.dsMsg.equals(lastReadMessage)) { lastReadMessage = status.dsMsg; lastReadTime = System.nanoTime(); } //if the status message has not changed for 15 seconds, print //out the message. long elapsed = System.nanoTime() - lastReadTime; if(TimeUnit.NANOSECONDS.toMillis(elapsed) > STATUS_WAIT_TIME && lastReadMessage != null && !lastReadMessage.equals(lastReportedMessage)) { long elapsedSec = TimeUnit.NANOSECONDS.toSeconds(elapsed); System.out.println(LocalizedStrings.CacheServerLauncher_LAUNCH_IN_PROGRESS_0 .toLocalizedString(elapsedSec, status.dsMsg)); lastReportedMessage = lastReadMessage; } } if (status.state == SHUTDOWN) { System.out.println(status); System.exit(1); } } writePidToFile(status); System.out.println( status ); } } /** * Creates a new pid file and writes this process's pid into it. */ private void writePidToFile(Status status) throws FileAlreadyExistsException, IOException { final File pidFile = new File(workingDir, pidFileName).getAbsoluteFile(); // see bug 32548 final FileWriter writer = new FileWriter(pidFile); try { writer.write(String.valueOf(status.pid)); writer.flush(); } finally { writer.close(); } } protected String getLogFilePath(Properties props) { String logFilePath = props.getProperty(DistributionConfig.LOG_FILE_NAME); if (logFilePath == null || logFilePath.length() == 0) { final File logFile = new File(this.workingDir, this.defaultLogFileName); logFilePath = logFile.getAbsolutePath(); } // check for absolute file path else if (logFilePath.charAt(0) != '/' && logFilePath.charAt(1) != ':') { final File logFile = new File(this.workingDir, logFilePath); logFilePath = logFile.getAbsolutePath(); } return logFilePath; } protected boolean checkStatusForWait(Status status) { return (status.state == STARTING || status.state == WAITING); } /** * Reads {@link DistributedSystem#PROPERTY_FILE} and determines if the * {@link DistributionConfig#LOG_FILE_NAME} property is set to stdout * @return true if the logging would go to stdout */ protected static boolean isLoggingToStdOut() { Properties gfprops = new Properties(); URL url = DistributedSystem.getPropertyFileURL(); if (url != null) { try { gfprops.load(url.openStream()); } catch (IOException io) { //throw new GemFireIOException("Failed reading " + url, io); System.out.println("Failed reading " + url); System.exit( 1 ); } final String logFile = gfprops.getProperty(DistributionConfig.LOG_FILE_NAME); if ( logFile == null || logFile.length() == 0 ) { return true; } } else { //Didnt find a property file, assuming the default is to log to stdout return true; } return false; } /** * Process information contained in the options map and add to the command * line of the subprocess as needed. */ protected void addToServerCommand(final List commandLine, final Map options) { final ListWrapper commandLineWrapper = new ListWrapper(commandLine); if (Boolean.TRUE.equals(options.get(REBALANCE))) { commandLineWrapper.add("-rebalance"); } String criticalHeapThreshold = (String)options.get(CRITICAL_HEAP_PERCENTAGE); if (criticalHeapThreshold != null) { commandLineWrapper.add(criticalHeapThreshold); } String evictionHeapThreshold = (String)options .get(EVICTION_HEAP_PERCENTAGE); if (evictionHeapThreshold != null) { commandLineWrapper.add(evictionHeapThreshold); } String criticalOffHeapThreshold = (String)options.get(CRITICAL_OFF_HEAP_PERCENTAGE); if (criticalOffHeapThreshold != null) { commandLineWrapper.add(criticalOffHeapThreshold); } String evictionOffHeapThreshold = (String)options .get(EVICTION_OFF_HEAP_PERCENTAGE); if (evictionOffHeapThreshold != null) { commandLineWrapper.add(evictionOffHeapThreshold); } commandLineWrapper.add((String) options.get(DISABLE_DEFAULT_SERVER)); commandLineWrapper.add((String) options.get(SERVER_PORT)); commandLineWrapper.add((String) options.get(SERVER_BIND_ADDRESS)); final Properties props = (Properties) options.get(PROPERTIES); for (final Object key : props.keySet()) { commandLineWrapper.add(key + "=" + props.getProperty(key.toString())); } if (props.getProperty(DistributionConfig.LOG_FILE_NAME) == null && CacheServerLauncher.isLoggingToStdOut()) { // Do not allow the cache server to log to stdout; override the logger with #defaultLogFileName commandLineWrapper.add(DistributionConfig.LOG_FILE_NAME + "=" + defaultLogFileName); } } /** * This method is called immediately following cache creation in the spawned * process, but prior to setting the RUNNING flag in the status file. So the * spawning process will block until this method completes. */ protected void startAdditionalServices(Cache cache, Map options, Properties props) throws Exception { } /** * This method is called prior to DistributedSytstem.disconnect(). Care should * be taken not to take too long in this method or else * #CacheServerLauncher.stop may timeout. */ protected void stopAdditionalServices() throws Exception { } protected boolean printLaunchCommand() { return PRINT_LAUNCH_COMMAND; } /** * A List implementation that disallows null values. * @param the Class type for the List elements. */ protected static class ListWrapper extends AbstractList { private static final ThreadLocal addResult = new ThreadLocal(); private final List list; public ListWrapper(final List list) { assert list != null : "The List cannot be null!"; this.list = list; } @Override public boolean add(final E e) { final boolean localAddResult = super.add(e); return (localAddResult && addResult.get()); } @Override public void add(final int index, final E element) { if (element != null) { list.add(index, element); } addResult.set(element != null); } @Override public E get(final int index) { return this.list.get(index); } @Override public E remove(final int index) { return list.remove(index); } @Override public E set(final int index, final E element) { return (element != null ? list.set(index, element) : list.get(index)); } @Override public int size() { return list.size(); } } private class MainLogReporter extends Thread implements StartupStatusListener { private volatile String lastLogMessage; private final Status status; boolean running = true; public MainLogReporter(Status status) { this.status = status; } public synchronized void shutdown() { this.running = false; this.status.dsMsg = null; this.notifyAll(); } @Override public void setStatus(String status) { lastLogMessage = status; } @Override public synchronized void run() { while(running) { try { wait(1000); } catch (InterruptedException e) { //this should not happen. break; } if(running && lastLogMessage != status.dsMsg // don't overwrite if someone else has already written a message // for WAITING status && !(status.state == WAITING && status.dsMsg != null)) { status.dsMsg = lastLogMessage; try { writeStatus(status); } catch (IOException e) { //this could happen if there was a concurrent write to the file //eg a stop. continue; } } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy