You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/10/27 17:19:12 UTC

[20/50] [abbrv] incubator-geode git commit: GEODE-288: move admin packages to internal

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentLauncher.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentLauncher.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentLauncher.java
new file mode 100644
index 0000000..932fe21
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentLauncher.java
@@ -0,0 +1,918 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.GemFireException;
+import org.apache.geode.SystemFailure;
+import org.apache.geode.internal.admin.api.AdminException;
+import org.apache.geode.internal.admin.api.jmx.Agent;
+import org.apache.geode.internal.admin.api.jmx.AgentConfig;
+import org.apache.geode.internal.admin.api.jmx.AgentFactory;
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.internal.OSProcess;
+import org.apache.geode.internal.PureJavaMode;
+import org.apache.geode.internal.net.SocketCreator;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.util.IOUtils;
+import org.apache.geode.internal.util.JavaCommandBuilder;
+
+/**
+ * A command line utility inspired by the <code>CacheServerLauncher</code> that is responsible for
+ * administering a stand-along GemFire JMX {@link Agent}.
+ * <p/>
+ * 
+ * @since GemFire 3.5
+ */
+public class AgentLauncher {
+
+  private static final Logger logger = LogService.getLogger();
+
+  /** Should the launch command be printed? */
+  public static final boolean PRINT_LAUNCH_COMMAND =
+      Boolean.getBoolean(AgentLauncher.class.getSimpleName() + ".PRINT_LAUNCH_COMMAND");
+
+  /* constants used to define state */
+  static final int SHUTDOWN = 0;
+  static final int STARTING = 1;
+  static final int RUNNING = 2;
+  static final int SHUTDOWN_PENDING = 3;
+  static final int SHUTDOWN_PENDING_AFTER_FAILED_STARTUP = 4;
+  static final int UNKNOWN = 6;
+
+  /** Agent configuration options */
+  static final String AGENT_PROPS = "agent-props";
+
+  /**
+   * A flag to indicate if the current log file should be kept. Used only when 'start' is used to
+   * fork off the 'server'
+   */
+  static final String APPENDTO_LOG_FILE = "appendto-log-file";
+
+  /** optional and additional classpath entries */
+  static final String CLASSPATH = "classpath";
+
+  /** The directory argument */
+  static final String DIR = "dir";
+
+  /** Extra VM arguments */
+  static final String VMARGS = "vmargs";
+
+  /** The directory in which the agent's output resides */
+  private File workingDirectory = null;
+
+  /** The Status object for the agent */
+  private Status status = null;
+
+  /** base name for the agent to be launched */
+  private final String basename;
+
+  /** The name for the start up log file */
+  private final String startLogFileName;
+
+  /** The name of the status file */
+  private final String statusFileName;
+
+  /**
+   * Instantiates an AgentLauncher for execution and control of the GemFire JMX Agent process. This
+   * constructor is package private to prevent direct instantiation or subclassing by classes
+   * outside this package, but does allow the class to be tested as needed.
+   * <p/>
+   * 
+   * @param basename base name for the application to be launched
+   */
+  AgentLauncher(final String basename) {
+    assert basename != null : "The base name used by the AgentLauncher to create files cannot be null!";
+    this.basename = basename;
+    final String formattedBasename = this.basename.toLowerCase().replace(" ", "");
+    this.startLogFileName = "start_" + formattedBasename + ".log";
+    this.statusFileName = "." + formattedBasename + ".ser";
+  }
+
+  /**
+   * Prints information about the agent configuration options
+   */
+  public void configHelp() {
+    PrintStream out = System.out;
+
+    Properties props = AgentConfigImpl.getDefaultValuesForAllProperties();
+
+    out.println("\n");
+    out.println(LocalizedStrings.AgentLauncher_AGENT_CONFIGURATION_PROPERTIES.toString());
+
+    SortedMap<String, String> map = new TreeMap<String, String>();
+
+    int maxLength = 0;
+    for (Iterator<Object> iter = props.keySet().iterator(); iter.hasNext();) {
+      String prop = (String) iter.next();
+      int length = prop.length();
+      if (length > maxLength) {
+        maxLength = length;
+      }
+
+      map.put(prop,
+          AgentConfigImpl.getPropertyDescription(prop) + " ("
+              + LocalizedStrings.AgentLauncher_DEFAULT.toLocalizedString() + "  \""
+              + props.getProperty(prop) + "\")");
+    }
+
+    Iterator<Entry<String, String>> entries = map.entrySet().iterator();
+    while (entries.hasNext()) {
+      Entry<String, String> entry = entries.next();
+      String prop = entry.getKey();
+      out.print("  ");
+      out.println(prop);
+
+      String description = entry.getValue();
+      StringTokenizer st = new StringTokenizer(description, " ");
+      out.print("    ");
+      int printed = 6;
+      while (st.hasMoreTokens()) {
+        String word = st.nextToken();
+        if (printed + word.length() > 72) {
+          out.print("\n    ");
+          printed = 6;
+        }
+        out.print(word);
+        out.print(" ");
+        printed += word.length() + 1;
+      }
+      out.println("");
+    }
+    out.println("");
+
+    System.exit(1);
+  }
+
+  /**
+   * Returns a map that maps the name of the start options to its value on the command line. If no
+   * value is specified on the command line, a default one is provided.
+   */
+  protected Map<String, Object> getStartOptions(final String[] args) throws Exception {
+    final Map<String, Object> options = new HashMap<String, Object>();
+
+    options.put(APPENDTO_LOG_FILE, "false");
+    options.put(DIR, IOUtils.tryGetCanonicalFileElseGetAbsoluteFile(new File(".")));
+
+    final List<String> vmArgs = new ArrayList<String>();
+    options.put(VMARGS, vmArgs);
+
+    final Properties agentProps = new Properties();
+    options.put(AGENT_PROPS, agentProps);
+
+    for (final String arg : args) {
+      if (arg.startsWith("-classpath=")) {
+        options.put(CLASSPATH, arg.substring("-classpath=".length()));
+      } else if (arg.startsWith("-dir=")) {
+        final File workingDirectory = processDirOption(options, arg.substring("-dir=".length()));
+        System.setProperty(AgentConfigImpl.AGENT_PROPSFILE_PROPERTY_NAME,
+            new File(workingDirectory, AgentConfig.DEFAULT_PROPERTY_FILE).getPath());
+      } else if (arg.startsWith("-J")) {
+        vmArgs.add(arg.substring(2));
+      } else if (arg.contains("=")) {
+        final int index = arg.indexOf("=");
+        final String prop = arg.substring(0, index);
+        final String value = arg.substring(index + 1);
+
+        // if appendto-log-file is set, put it in options; it is not set as an agent prop
+        if (prop.equals(APPENDTO_LOG_FILE)) {
+          options.put(APPENDTO_LOG_FILE, value);
+          continue;
+        }
+
+        // verify the property is valid
+        AgentConfigImpl.getPropertyDescription(prop);
+
+        // Note, the gfAgentPropertyFile System property is ultimately read in the constructor of
+        // the AgentImpl class
+        // in order to make any properties defined in this file not only accessible to the
+        // DistributedSystem but to
+        // the GemFire Agent as well.
+        if (AgentConfigImpl.PROPERTY_FILE_NAME.equals(prop)) {
+          System.setProperty(AgentConfigImpl.AGENT_PROPSFILE_PROPERTY_NAME, value);
+        }
+
+        // The Agent properties file (specified with the command-line key=value) is used to pass
+        // configuration settings
+        // to the GemFire DistributedSystem. A property file can be passed using the property-file
+        // command-line switch
+        // is a large number of properties are specified, or the properties maybe individually
+        // specified on the
+        // command-line as property=value arguments.
+        agentProps.setProperty(prop, value);
+      }
+    }
+
+    return options;
+  }
+
+  /**
+   * After parsing the command line arguments, spawn the Java VM that will host the GemFire JMX
+   * Agent.
+   */
+  public void start(final String[] args) throws Exception {
+    final Map<String, Object> options = getStartOptions(args);
+
+    workingDirectory = IOUtils.tryGetCanonicalFileElseGetAbsoluteFile((File) options.get(DIR));
+
+    // verify that any GemFire JMX Agent process has been properly shutdown and delete any remaining
+    // status files...
+    verifyAndClearStatus();
+
+    // start the GemFire JMX Agent process...
+    runCommandLine(options, buildCommandLine(options));
+
+    // wait for the GemFire JMX Agent process to complete startup and begin running...
+    // it is also possible the Agent process may fail to start, so this should not wait indefinitely
+    // unless
+    // the status file was not successfully written to
+    pollAgentUntilRunning();
+
+    System.exit(0);
+  }
+
+  private void verifyAndClearStatus() throws Exception {
+    final Status status = getStatus();
+
+    if (status != null && status.state != SHUTDOWN) {
+      throw new IllegalStateException(
+          LocalizedStrings.AgentLauncher_JMX_AGENT_EXISTS_BUT_WAS_NOT_SHUTDOWN.toLocalizedString());
+    }
+
+    deleteStatus();
+  }
+
+  private String[] buildCommandLine(final Map<String, Object> options) {
+    final List<String> commands = JavaCommandBuilder.buildCommand(AgentLauncher.class.getName(),
+        (String) options.get(CLASSPATH), null, (List<String>) options.get(VMARGS));
+
+    commands.add("server");
+    commands.add("-dir=" + workingDirectory);
+
+    final Properties agentProps = (Properties) options.get(AGENT_PROPS);
+
+    for (final Object key : agentProps.keySet()) {
+      commands.add(key + "=" + agentProps.get(key.toString()));
+    }
+
+    return commands.toArray(new String[commands.size()]);
+  }
+
+  private void printCommandLine(final String[] commandLine) {
+    if (PRINT_LAUNCH_COMMAND) {
+      System.out.print("Starting " + this.basename + " with command:\n");
+      for (final String command : commandLine) {
+        System.out.print(command);
+        System.out.print(' ');
+      }
+      System.out.println();
+    }
+  }
+
+  private int runCommandLine(final Map<String, Object> options, final String[] commandLine)
+      throws IOException {
+    // initialize the startup log starting with a fresh log file (where all startup messages are
+    // printed)
+    final File startLogFile = IOUtils
+        .tryGetCanonicalFileElseGetAbsoluteFile(new File(workingDirectory, startLogFileName));
+
+    if (startLogFile.exists() && !startLogFile.delete()) {
+      throw new IOException(LocalizedStrings.AgentLauncher_UNABLE_TO_DELETE_FILE_0
+          .toLocalizedString(startLogFile.getAbsolutePath()));
+    }
+
+    Map<String, String> env = new HashMap<String, String>();
+    // read the passwords from command line
+    SocketCreator.readSSLProperties(env, true);
+
+    printCommandLine(commandLine);
+
+    final int pid = OSProcess.bgexec(commandLine, workingDirectory, startLogFile, false, env);
+
+    System.out.println(
+        LocalizedStrings.AgentLauncher_STARTING_JMX_AGENT_WITH_PID_0.toLocalizedString(pid));
+
+    return pid;
+  }
+
+  private void pollAgentUntilRunning() throws Exception {
+    Status status = spinReadStatus();
+
+    // TODO this loop could recurse indefinitely if the GemFire JMX Agent's state never changes from
+    // STARTING
+    // to something else (like RUNNING), which could happen if server process fails to startup
+    // correctly
+    // and did not or could not write to the status file!
+    // TODO should we really allow the InterruptedException from the Thread.sleep call to break this
+    // loop (yeah, I
+    // think so given the fact this could loop indefinitely)?
+    while (status != null && status.state == STARTING) {
+      Thread.sleep(500);
+      status = spinReadStatus();
+    }
+
+    if (status == null) {
+      // TODO throw a more appropriate Exception here!
+      throw new Exception(LocalizedStrings.AgentLauncher_NO_AVAILABLE_STATUS.toLocalizedString());
+    } else {
+      System.out.println(status);
+    }
+  }
+
+  /**
+   * Starts the GemFire JMX Agent "server" process with the given command line arguments.
+   */
+  public void server(final String[] args) throws Exception {
+    final Map<String, Object> options = getStartOptions(args);
+
+    workingDirectory = IOUtils.tryGetCanonicalFileElseGetAbsoluteFile((File) options.get(DIR));
+
+    writeStatus(createStatus(this.basename, STARTING, OSProcess.getId()));
+
+    final Agent agent = createAgent((Properties) options.get(AGENT_PROPS));
+
+    final Thread thread = createAgentProcessThread(createAgentProcessThreadGroup(), agent);
+    thread.setDaemon(true);
+    thread.start();
+
+    // periodically check and see if the JMX Agent has been told to stop
+    pollAgentForPendingShutdown(agent);
+  }
+
+  private Agent createAgent(final Properties props) throws IOException, AdminException {
+    DistributionManager.isDedicatedAdminVM = true;
+    SystemFailure.setExitOK(true);
+
+    final AgentConfigImpl config = new AgentConfigImpl(props);
+
+    // see bug 43760
+    if (config.getLogFile() == null || "".equals(config.getLogFile().trim())) {
+      config.setLogFile(AgentConfigImpl.DEFAULT_LOG_FILE);
+    }
+
+    // LOG:TODO: redirectOutput called here
+    OSProcess.redirectOutput(new File(config.getLogFile())); // redirect output to the configured
+                                                             // log file
+
+    return AgentFactory.getAgent(config);
+  }
+
+  private ThreadGroup createAgentProcessThreadGroup() {
+    return new ThreadGroup(LocalizedStrings.AgentLauncher_STARTING_AGENT.toLocalizedString()) {
+      @Override
+      public void uncaughtException(final Thread t, final Throwable e) {
+        if (e instanceof VirtualMachineError) {
+          SystemFailure.setFailure((VirtualMachineError) e);
+        }
+        setServerError(LocalizedStrings.AgentLauncher_UNCAUGHT_EXCEPTION_IN_THREAD_0
+            .toLocalizedString(t.getName()), e);
+      }
+    };
+  }
+
+  private Thread createAgentProcessThread(final ThreadGroup group, final Agent agent) {
+    return new Thread(group, createAgentProcessRunnable(agent), "Start agent");
+  }
+
+  private Runnable createAgentProcessRunnable(final Agent agent) {
+    return new Runnable() {
+      public void run() {
+        try {
+          agent.start();
+          writeStatus(createStatus(AgentLauncher.this.basename, RUNNING, OSProcess.getId()));
+        } catch (IOException e) {
+          e.printStackTrace();
+        } catch (GemFireException e) {
+          e.printStackTrace();
+          handleGemFireException(e);
+        }
+      }
+
+      private void handleGemFireException(final GemFireException e) {
+        String message = LocalizedStrings.AgentLauncher_SERVER_FAILED_TO_START_0
+            .toLocalizedString(e.getMessage());
+
+        if (e.getCause() != null) {
+          if (e.getCause().getCause() != null) {
+            message += ", " + e.getCause().getCause().getMessage();
+          }
+        }
+
+        setServerError(null, new Exception(message));
+      }
+    };
+  }
+
+
+  /**
+   * Notes that an error has occurred in the agent and that it has shut down because of it.
+   */
+  private void setServerError(final String message, final Throwable cause) {
+    try {
+      writeStatus(createStatus(this.basename, SHUTDOWN_PENDING_AFTER_FAILED_STARTUP,
+          OSProcess.getId(), message, cause));
+    } catch (Exception e) {
+      logger.fatal(e.getMessage(), e);
+      System.exit(1);
+    }
+  }
+
+  private void pollAgentForPendingShutdown(final Agent agent) throws Exception {
+    while (true) {
+      pause(500);
+      spinReadStatus();
+
+      if (isStatus(SHUTDOWN_PENDING, SHUTDOWN_PENDING_AFTER_FAILED_STARTUP)) {
+        agent.stop();
+        final int exitCode = (isStatus(SHUTDOWN_PENDING_AFTER_FAILED_STARTUP) ? 1 : 0);
+        writeStatus(createStatus(this.status, SHUTDOWN));
+        System.exit(exitCode);
+      }
+    }
+  }
+
+  /**
+   * Extracts configuration information for stopping a agent based on the contents of the command
+   * line. This method can also be used with getting the status of a agent.
+   */
+  protected Map<String, Object> getStopOptions(final String[] args) throws Exception {
+    final Map<String, Object> options = new HashMap<String, Object>();
+
+    options.put(DIR, IOUtils.tryGetCanonicalFileElseGetAbsoluteFile(new File(".")));
+
+    for (final String arg : args) {
+      if (arg.equals("stop") || arg.equals("status")) {
+        // expected
+      } else if (arg.startsWith("-dir=")) {
+        processDirOption(options, arg.substring("-dir=".length()));
+      } else {
+        throw new Exception(
+            LocalizedStrings.AgentLauncher_UNKNOWN_ARGUMENT_0.toLocalizedString(arg));
+      }
+    }
+
+    return options;
+  }
+
+  /**
+   * Stops a running JMX Agent by setting the status to "shutdown pending".
+   */
+  public void stop(final String[] args) throws Exception {
+    final Map<String, Object> options = getStopOptions(args);
+
+    workingDirectory = IOUtils.tryGetCanonicalFileElseGetAbsoluteFile((File) options.get(DIR));
+
+    int exitStatus = 1;
+
+    if (new File(workingDirectory, statusFileName).exists()) {
+      spinReadStatus();
+
+      if (!isStatus(SHUTDOWN)) {
+        writeStatus(createStatus(this.basename, SHUTDOWN_PENDING, status.pid));
+      }
+
+      pollAgentForShutdown();
+
+      if (isStatus(SHUTDOWN)) {
+        System.out
+            .println(LocalizedStrings.AgentLauncher_0_HAS_STOPPED.toLocalizedString(this.basename));
+        deleteStatus();
+        exitStatus = 0;
+      } else {
+        System.out
+            .println(LocalizedStrings.AgentLauncher_TIMEOUT_WAITING_FOR_0_TO_SHUTDOWN_STATUS_IS_1
+                .toLocalizedString(this.basename, status));
+      }
+    } else {
+      System.out.println(
+          LocalizedStrings.AgentLauncher_THE_SPECIFIED_WORKING_DIRECTORY_0_CONTAINS_NO_STATUS_FILE
+              .toLocalizedString(workingDirectory));
+    }
+
+    System.exit(exitStatus);
+  }
+
+  private void pollAgentForShutdown() throws InterruptedException {
+    final long endTime = (System.currentTimeMillis() + 20000);
+    long clock = 0;
+
+    while (clock < endTime && !isStatus(SHUTDOWN)) {
+      pause(500);
+      spinReadStatus();
+      clock = System.currentTimeMillis();
+    }
+  }
+
+  /**
+   * Prints the status of the GemFire JMX Agent running in the configured working directory.
+   */
+  public void status(final String[] args) throws Exception {
+    this.workingDirectory =
+        IOUtils.tryGetCanonicalFileElseGetAbsoluteFile((File) getStopOptions(args).get(DIR));
+    System.out.println(getStatus());
+    System.exit(0);
+  }
+
+  /**
+   * Returns the <code>Status</code> of the GemFire JMX Agent in the <code>workingDirectory</code>.
+   */
+  protected Status getStatus() throws Exception {
+    Status status;
+
+    if (new File(workingDirectory, statusFileName).exists()) {
+      status = spinReadStatus();
+    } else {
+      status = createStatus(this.basename, SHUTDOWN, 0,
+          LocalizedStrings.AgentLauncher_0_IS_NOT_RUNNING_IN_SPECIFIED_WORKING_DIRECTORY_1
+              .toLocalizedString(this.basename, this.workingDirectory),
+          null);
+    }
+
+    return status;
+  }
+
+  /**
+   * Determines if the Status.state is one of the specified states in the given array of states.
+   * Note, the status of the Agent, as indicated in the .agent.ser status file, should never have a
+   * written value of UNKNOWN.
+   * <p/>
+   * 
+   * @param states an array of possible acceptable states satisfying the condition of the Agent's
+   *        status.
+   * @return a boolean value indicating whether the Agent's status satisfies one of the specified
+   *         states.
+   */
+  private boolean isStatus(final Integer... states) {
+    return (this.status != null
+        && Arrays.asList(defaultToUnknownStateIfNull(states)).contains(this.status.state));
+  }
+
+  /**
+   * Removes an agent's status file
+   */
+  protected void deleteStatus() throws IOException {
+    final File statusFile = new File(workingDirectory, statusFileName);
+
+    if (statusFile.exists() && !statusFile.delete()) {
+      throw new IOException("Could not delete status file (" + statusFile.getAbsolutePath() + ")");
+    }
+  }
+
+  /**
+   * Reads the GemFire JMX Agent's status from the status file (.agent.ser) in it's working
+   * directory.
+   * <p/>
+   * 
+   * @return a Status object containing the state persisted to the .agent.ser file in the working
+   *         directory and representing the status of the Agent
+   * @throws IOException if the status file was unable to be read.
+   * @throws RuntimeException if the class of the object written to the .agent.ser file is not of
+   *         type Status.
+   */
+  protected Status readStatus() throws IOException {
+    FileInputStream fileIn = null;
+    ObjectInputStream objectIn = null;
+
+    try {
+      fileIn = new FileInputStream(new File(workingDirectory, statusFileName));
+      objectIn = new ObjectInputStream(fileIn);
+      this.status = (Status) objectIn.readObject();
+      return this.status;
+    } catch (ClassNotFoundException e) {
+      throw new RuntimeException(e);
+    } finally {
+      IOUtils.close(objectIn);
+      IOUtils.close(fileIn);
+    }
+  }
+
+  /**
+   * A wrapper method for the readStatus method to make one last check for the GemFire JMX Agent
+   * process if running with the native libraries.
+   * 
+   * @return the Status object as returned from readStatus unless running in native mode and a
+   *         determination is made such that the Agent process is not running.
+   * @throws IOException if the state of the Agent process could not be read from the .agent.ser
+   *         status file.
+   * @see #readStatus()
+   */
+  protected Status nativeReadStatus() throws IOException {
+    Status status = readStatus();
+
+    // @see Bug #32760 - the bug is still possible in pure Java mode
+    if (status != null && !PureJavaMode.isPure() && !OSProcess.exists(status.pid)) {
+      status = createStatus(status, SHUTDOWN);
+    }
+
+    return status;
+  }
+
+  /**
+   * Reads the JMX Agent's status from the .agent.ser status file. If the status file cannot be read
+   * due to I/O problems, the method will keep attempting to read the file for up to 20 seconds.
+   * <p/>
+   * 
+   * @return the Status of the GemFire JMX Agent as determined by the .agent.ser status file, or
+   *         natively based on the presence/absence of the Agent process.
+   */
+  protected Status spinReadStatus() {
+    Status status = null;
+
+    final long endTime = (System.currentTimeMillis() + 20000);
+    long clock = 0;
+
+    while (status == null && clock < endTime) {
+      try {
+        status = nativeReadStatus();
+      } catch (Exception ignore) {
+        // see bug 31575
+        // see bug 36998
+        // try again after a short delay... the status file might have been read prematurely before
+        // it existed
+        // or while the server was trying to write to it resulting in a possible EOFException, or
+        // other IOException.
+        pause(500);
+      } finally {
+        clock = System.currentTimeMillis();
+      }
+    }
+
+    return status;
+  }
+
+  /**
+   * Sets the status of the GemFire JMX Agent by serializing a <code>Status</code> object to a
+   * status file in the Agent's working directory.
+   * <p/>
+   * 
+   * @param status the Status object representing the state of the Agent process to persist to disk.
+   * @return the written Status object.
+   * @throws IOException if the Status could not be successfully persisted to disk.
+   */
+  public Status writeStatus(final Status status) throws IOException {
+    FileOutputStream fileOut = null;
+    ObjectOutputStream objectOut = null;
+
+    try {
+      fileOut = new FileOutputStream(new File(workingDirectory, statusFileName));
+      objectOut = new ObjectOutputStream(fileOut);
+      objectOut.writeObject(status);
+      objectOut.flush();
+      this.status = status;
+      return this.status;
+    } finally {
+      IOUtils.close(objectOut);
+      IOUtils.close(fileOut);
+    }
+  }
+
+  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;
+  }
+
+  protected static Status createStatus(final Status status, final int state) {
+    assert status != null : "The status to clone cannot be null!";
+    return createStatus(status.baseName, state, status.pid, status.msg, status.exception);
+  }
+
+  protected static Integer[] defaultToUnknownStateIfNull(final Integer... states) {
+    return (states != null ? states : new Integer[] {UNKNOWN});
+  }
+
+  protected static boolean pause(final int milliseconds) {
+    try {
+      Thread.sleep(milliseconds);
+      return true;
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+      return false;
+    }
+  }
+
+  protected static File processDirOption(final Map<String, Object> options, final String dirValue)
+      throws FileNotFoundException {
+    final File workingDirectory = new File(dirValue);
+
+    if (!workingDirectory.exists()) {
+      throw new FileNotFoundException(
+          LocalizedStrings.AgentLauncher_THE_INPUT_WORKING_DIRECTORY_DOES_NOT_EXIST_0
+              .toLocalizedString(dirValue));
+    }
+
+    options.put(DIR, workingDirectory);
+
+    return workingDirectory;
+  }
+
+  /**
+   * Prints usage information for the AgentLauncher to the command line.
+   * <p/>
+   * 
+   * @param message a String to output to the command line indicating the user error.
+   */
+  private static void usage(final String message) {
+    final PrintStream out = System.out;
+
+    out.println("\n** " + message + "\n");
+
+    out.println("agent start [-J<vmarg>]* [-dir=<dir>] [prop=value]*");
+    out.println(LocalizedStrings.AgentLauncher_STARTS_THE_GEMFIRE_JMX_AGENT.toLocalizedString());
+    out.println("\t" + LocalizedStrings.AgentLauncher_VMARG.toLocalizedString());
+    out.println("\t" + LocalizedStrings.AgentLauncher_DIR.toLocalizedString());
+    out.println("\t" + LocalizedStrings.AgentLauncher_PROP.toLocalizedString());
+    out.println("\t" + LocalizedStrings.AgentLauncher_SEE_HELP_CONFIG.toLocalizedString());
+    out.println();
+
+    out.println("agent stop [-dir=<dir>]");
+    out.println(LocalizedStrings.AgentLauncher_STOPS_A_GEMFIRE_JMX_AGENT.toLocalizedString());
+    out.println("\t" + LocalizedStrings.AgentLauncher_DIR.toLocalizedString());
+    out.println("");
+    out.println("agent status [-dir=<dir>]");
+    out.println(
+        LocalizedStrings.AgentLauncher_REPORTS_THE_STATUS_AND_THE_PROCESS_ID_OF_A_GEMFIRE_JMX_AGENT
+            .toLocalizedString());
+    out.println("\t" + LocalizedStrings.AgentLauncher_DIR.toLocalizedString());
+    out.println();
+
+    System.exit(1);
+  }
+
+  /**
+   * Bootstrap method to launch the GemFire JMX Agent process to monitor and manage a GemFire
+   * Distributed System/Cache. Main will read the arguments passed on the command line and dispatch
+   * the command to the appropriate handler.
+   */
+  public static void main(final String[] args) {
+    if (args.length < 1) {
+      usage(LocalizedStrings.AgentLauncher_MISSING_COMMAND.toLocalizedString());
+    }
+
+    // TODO is this only needed on 'agent server'? 'agent {start|stop|status}' technically do no run
+    // any GemFire Cache
+    // or DS code inside the current process.
+    SystemFailure.loadEmergencyClasses();
+
+    final AgentLauncher launcher = new AgentLauncher("Agent");
+
+    try {
+      final String command = args[0];
+
+      if (command.equalsIgnoreCase("start")) {
+        launcher.start(args);
+      } else if (command.equalsIgnoreCase("server")) {
+        launcher.server(args);
+      } else if (command.equalsIgnoreCase("stop")) {
+        launcher.stop(args);
+      } else if (command.equalsIgnoreCase("status")) {
+        launcher.status(args);
+      } else if (command.toLowerCase().matches("-{0,2}help")) {
+        if (args.length > 1) {
+          final String topic = args[1];
+
+          if (topic.equals("config")) {
+            launcher.configHelp();
+          } else {
+            usage(LocalizedStrings.AgentLauncher_NO_HELP_AVAILABLE_FOR_0.toLocalizedString(topic));
+          }
+        }
+
+        usage(LocalizedStrings.AgentLauncher_AGENT_HELP.toLocalizedString());
+      } else {
+        usage(LocalizedStrings.AgentLauncher_UNKNOWN_COMMAND_0.toLocalizedString(command));
+      }
+    } catch (VirtualMachineError e) {
+      SystemFailure.initiateFailure(e);
+      throw e;
+    } catch (Throwable t) {
+      SystemFailure.checkFailure();
+      t.printStackTrace();
+      System.err.println(
+          LocalizedStrings.AgentLauncher_ERROR_0.toLocalizedString(t.getLocalizedMessage()));
+      System.exit(1);
+    }
+  }
+
+  /**
+   * A class representing the current state of the GemFire JMX Agent process. Instances of this
+   * class are serialized to a {@linkplain #statusFileName file} on disk in the specified working
+   * directory {@linkplain #workingDirectory}.
+   * <p/>
+   * 
+   * @see #SHUTDOWN
+   * @see #STARTING
+   * @see #RUNNING
+   * @see #SHUTDOWN_PENDING
+   * @see #SHUTDOWN_PENDING_AFTER_FAILED_STARTUP
+   */
+  // TODO refactor this class and internalize the state
+  // TODO refactor the class and make immutable
+  static class Status implements Serializable {
+
+    private static final long serialVersionUID = -7758402454664266174L;
+
+    int pid = 0;
+    int state = 0;
+
+    final String baseName;
+    String msg;
+
+    Throwable exception;
+
+    public Status(final String baseName) {
+      this.baseName = baseName;
+    }
+
+    @Override
+    public String toString() {
+      final StringBuilder buffer = new StringBuilder();
+
+      if (pid == Integer.MIN_VALUE && state == SHUTDOWN && msg != null) {
+        buffer.append(msg);
+      } else {
+        buffer.append(
+            LocalizedStrings.AgentLauncher_0_PID_1_STATUS.toLocalizedString(this.baseName, pid));
+
+        switch (state) {
+          case SHUTDOWN:
+            buffer.append(LocalizedStrings.AgentLauncher_SHUTDOWN.toLocalizedString());
+            break;
+          case STARTING:
+            buffer.append(LocalizedStrings.AgentLauncher_STARTING.toLocalizedString());
+            break;
+          case RUNNING:
+            buffer.append(LocalizedStrings.AgentLauncher_RUNNING.toLocalizedString());
+            break;
+          case SHUTDOWN_PENDING:
+            buffer.append(LocalizedStrings.AgentLauncher_SHUTDOWN_PENDING.toLocalizedString());
+            break;
+          case SHUTDOWN_PENDING_AFTER_FAILED_STARTUP:
+            buffer.append(LocalizedStrings.AgentLauncher_SHUTDOWN_PENDING_AFTER_FAILED_STARTUP
+                .toLocalizedString());
+            break;
+          default:
+            buffer.append(LocalizedStrings.AgentLauncher_UNKNOWN.toLocalizedString());
+            break;
+        }
+
+        if (exception != null) {
+          if (msg != null) {
+            buffer.append("\n").append(msg).append(" - ");
+          } else {
+            buffer.append("\n " + LocalizedStrings.AgentLauncher_EXCEPTION_IN_0_1
+                .toLocalizedString(this.baseName, exception.getMessage()) + " - ");
+          }
+          buffer
+              .append(LocalizedStrings.AgentLauncher_SEE_LOG_FILE_FOR_DETAILS.toLocalizedString());
+        }
+      }
+
+      return buffer.toString();
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/CacheServerJmxImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/CacheServerJmxImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/CacheServerJmxImpl.java
new file mode 100644
index 0000000..ef6c7c4
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/CacheServerJmxImpl.java
@@ -0,0 +1,588 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.modelmbean.ModelMBean;
+import javax.naming.OperationNotSupportedException;
+
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.internal.admin.api.AdminException;
+import org.apache.geode.internal.admin.api.CacheServerConfig;
+import org.apache.geode.internal.admin.api.CacheVmConfig;
+import org.apache.geode.internal.admin.api.ConfigurationParameter;
+import org.apache.geode.internal.admin.api.StatisticResource;
+import org.apache.geode.internal.admin.api.SystemMemberCache;
+import org.apache.geode.internal.admin.api.SystemMemberCacheEvent;
+import org.apache.geode.internal.admin.api.SystemMemberRegionEvent;
+import org.apache.geode.internal.admin.api.CacheVm;
+import org.apache.geode.internal.admin.api.impl.CacheServerImpl;
+import org.apache.geode.internal.admin.api.impl.ConfigurationParameterImpl;
+import org.apache.geode.internal.admin.ClientMembershipMessage;
+import org.apache.geode.internal.admin.GemFireVM;
+import org.apache.geode.internal.admin.StatResource;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.LogService;
+
+/**
+ * MBean representation of a {@link CacheVm}.
+ *
+ * @since GemFire 4.0
+ */
+public class CacheServerJmxImpl extends CacheServerImpl
+    implements ManagedResource, CacheVmConfig, CacheServerConfig, SystemMemberJmx {
+
+  private static final Logger logger = LogService.getLogger();
+
+  /**
+   * Interval in seconds between refreshes. Value less than one results in no refreshing
+   */
+  private int refreshInterval = 0;
+
+  /** The object name of this managed resource */
+  private ObjectName objectName;
+
+  /** The name of the MBean that will manage this resource */
+  private String mbeanName;
+
+  /** The ModelMBean that is configured to manage this resource */
+  private ModelMBean modelMBean;
+
+  /** Reference to the cache MBean representing a Cache in the Cache VM Member */
+  private SystemMemberCacheJmxImpl managedSystemMemberCache;
+
+  /** collection to collect all the resources created for this member */
+  private Map<StatResource, StatisticResourceJmxImpl> managedStatisticsResourcesMap =
+      new HashMap<StatResource, StatisticResourceJmxImpl>();
+
+  ////////////////////// Constructors //////////////////////
+
+  /**
+   * Creates a new <code>CacheServerJmxImpl</code> for an existing cache server.
+   */
+  CacheServerJmxImpl(AdminDistributedSystemJmxImpl system, GemFireVM vm) throws AdminException {
+
+    super(system, vm);
+    initializeMBean();
+  }
+
+  /**
+   * Creates a new <code>CacheServerJmxImpl</code> for an newly-created cache server.
+   */
+  CacheServerJmxImpl(AdminDistributedSystemJmxImpl system, CacheVmConfig config)
+      throws AdminException {
+
+    super(system, config);
+    initializeMBean();
+  }
+
+  ////////////////////// Instance Methods //////////////////////
+
+  /**
+   * Creates and registers the MBean to manage this resource
+   */
+  private void initializeMBean() throws AdminException {
+    // initialize Managed Resources for stats & cache first.
+    // initializeManagedResources();
+
+    this.mbeanName = new StringBuffer("GemFire.CacheVm:").append("id=")
+        .append(MBeanUtil.makeCompliantMBeanNameProperty(getId())).append(",type=")
+        .append(MBeanUtil.makeCompliantMBeanNameProperty(getType().getName())).toString();
+
+    this.objectName =
+        MBeanUtil.createMBean(this, addDynamicAttributes(MBeanUtil.lookupManagedBean(this)));
+
+    // Refresh Interval
+    AdminDistributedSystemJmxImpl sysJmx = (AdminDistributedSystemJmxImpl) system;
+    if (sysJmx.getRefreshInterval() > 0)
+      this.refreshInterval = sysJmx.getRefreshInterval();
+  }
+
+  public String getMBeanName() {
+    return this.mbeanName;
+  }
+
+  public ModelMBean getModelMBean() {
+    return this.modelMBean;
+  }
+
+  public void setModelMBean(ModelMBean modelMBean) {
+    this.modelMBean = modelMBean;
+  }
+
+  public ObjectName getObjectName() {
+    return this.objectName;
+  }
+
+  public ManagedResourceType getManagedResourceType() {
+    return ManagedResourceType.CACHE_VM;
+  }
+
+  /**
+   * Un-registers all the statistics & cache managed resource created for this member. After
+   * un-registering the resource MBean instances, clears managedStatisticsResourcesMap collection &
+   * sets managedSystemMemberCache to null.
+   * 
+   * Creates ConfigurationParameterJmxImpl, StatisticResourceJmxImpl and SystemMemberCacheJmxImpl.
+   * But cleans up only StatisticResourceJmxImpl and SystemMemberCacheJmxImpl which are of type
+   * ManagedResource.
+   */
+  public void cleanupResource() {
+    synchronized (this.managedStatisticsResourcesMap) {
+      ConfigurationParameter[] names = getConfiguration();
+      if (names != null) {
+        for (int i = 0; i < names.length; i++) {
+          ConfigurationParameter parm = names[i];
+          ((ConfigurationParameterImpl) parm).removeConfigurationParameterListener(this);
+        }
+      }
+      this.parms.clear();
+
+      Collection<StatisticResourceJmxImpl> statisticResources =
+          managedStatisticsResourcesMap.values();
+
+      for (StatisticResourceJmxImpl statisticResource : statisticResources) {
+        MBeanUtil.unregisterMBean(statisticResource);
+      }
+
+      this.managedStatisticsResourcesMap.clear();
+    }
+
+    MBeanUtil.unregisterMBean(this.managedSystemMemberCache);
+    this.managedSystemMemberCache = null;
+  }
+
+  /////////////////////// Configuration ///////////////////////
+
+  @Override
+  public String getHost() {
+    return this.getConfig().getHost();
+  }
+
+  public void setHost(String host) {
+    this.getConfig().setHost(host);
+  }
+
+  @Override
+  public String getWorkingDirectory() {
+    return this.getConfig().getWorkingDirectory();
+  }
+
+  @Override
+  public void setWorkingDirectory(String dir) {
+    this.getConfig().setWorkingDirectory(dir);
+  }
+
+  @Override
+  public String getProductDirectory() {
+    return this.getConfig().getProductDirectory();
+  }
+
+  @Override
+  public void setProductDirectory(String dir) {
+    this.getConfig().setProductDirectory(dir);
+  }
+
+  public String getRemoteCommand() {
+    return this.getConfig().getRemoteCommand();
+  }
+
+  public void setRemoteCommand(String remoteCommand) {
+    this.getConfig().setRemoteCommand(remoteCommand);
+  }
+
+  public void validate() {
+    throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString());
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString());
+  }
+
+  public String getCacheXMLFile() {
+    return this.getConfig().getCacheXMLFile();
+  }
+
+  public void setCacheXMLFile(String cacheXMLFile) {
+    this.getConfig().setCacheXMLFile(cacheXMLFile);
+  }
+
+  public String getClassPath() {
+    return this.getConfig().getClassPath();
+  }
+
+  public void setClassPath(String classpath) {
+    this.getConfig().setClassPath(classpath);
+  }
+
+  // -------------------------------------------------------------------------
+  // MBean attribute accessors/mutators
+  // -------------------------------------------------------------------------
+
+  /**
+   * Gets the interval in seconds between config refreshes
+   *
+   * @return the current refresh interval in seconds
+   */
+  public int getRefreshInterval() {
+    return this.refreshInterval;
+  }
+
+  /**
+   * Sets interval in seconds between cache config refreshes; zero or less turns off auto
+   * refreshing. Manual refreshing has no effect on when the next scheduled refresh will occur.
+   *
+   * @param refreshInterval the new refresh interval in seconds
+   */
+  public void _setRefreshInterval(int refreshInterval) {
+    boolean isRegistered = MBeanUtil.isRefreshNotificationRegistered(this,
+        RefreshNotificationType.SYSTEM_MEMBER_CONFIG);
+
+    if (isRegistered && (getRefreshInterval() == refreshInterval))
+      return;
+
+    this.refreshInterval = Helper.setAndReturnRefreshInterval(this, refreshInterval);
+  }
+
+  /**
+   * RefreshInterval is now set only through the AdminDistributedSystem property refreshInterval.
+   * Attempt to set refreshInterval on CacheServerJmx MBean would result in an
+   * OperationNotSupportedException Auto-refresh is enabled on demand when a call to refreshConfig
+   * is made
+   * 
+   * @param refreshInterval the new refresh interval in seconds
+   * @deprecated since 6.0 use DistributedSystemConfig.refreshInterval instead
+   */
+  @Deprecated
+  public void setRefreshInterval(int refreshInterval) throws OperationNotSupportedException {
+    throw new OperationNotSupportedException(
+        LocalizedStrings.MANAGED_RESOURCE_REFRESH_INTERVAL_CANT_BE_SET_DIRECTLY
+            .toLocalizedString());
+  }
+
+  // -------------------------------------------------------------------------
+  // MBean Operations
+  // -------------------------------------------------------------------------
+
+  public void refreshConfig() throws AdminException {
+    // 1st call to refreshConfig would trigger
+    // the auto-refresh if an interval is set
+    if (this.refreshInterval > 0) {
+      this._setRefreshInterval(this.refreshInterval);
+    }
+
+    super.refreshConfig();
+  }
+
+  /**
+   * Initializes Cache & Statistics managed resources.
+   * 
+   * @throws AdminException if initialization of managed resources fails
+   */
+  // private void initializeManagedResources() throws AdminException {
+  // try {
+  // manageCache();
+  // } catch (MalformedObjectNameException e) {
+  // throw new
+  // AdminException(LocalizedStrings.SystemMemberJmxImpl_EXCEPTION_OCCURRED_WHILE_INITIALIZING_0_MBEANS_FOR_1.toLocalizedString(
+  // new Object[] {"Cache", getId()}),
+  // e);
+  // } catch (AdminException ae) {
+  // if
+  // (LocalizedStrings.SystemMemberJmx_THIS_SYSTEM_MEMBER_DOES_NOT_HAVE_A_CACHE.toLocalizedString().equals(ae.getMessage()))
+  // {
+  // //ignore this exception for a cache-less peer
+  // } else {
+  // throw ae;
+  // }
+  // }
+  // try {
+  // manageStats();
+  // } catch (MalformedObjectNameException e) {
+  // throw new
+  // AdminException(LocalizedStrings.SystemMemberJmxImpl_EXCEPTION_OCCURRED_WHILE_INITIALIZING_0_MBEANS_FOR_1.toLocalizedString(
+  // new Object[] {"Statistics", getId()}),
+  // e);
+  // }
+  // }
+
+  /**
+   * Gets this member's cache.
+   *
+   * @return array of ObjectName for this member's cache
+   */
+  public ObjectName manageCache() throws AdminException, MalformedObjectNameException {
+    return Helper.manageCache(this);
+  }
+
+  /**
+   * Gets all active StatisticResources for this manager.
+   *
+   * @return array of ObjectName instances
+   */
+  public ObjectName[] manageStats() throws AdminException, MalformedObjectNameException {
+    return Helper.manageStats(this);
+  }
+
+  /**
+   * Gets the active StatisticResources for this manager, based on the typeName as the key
+   *
+   * @return ObjectName of StatisticResourceJMX instance
+   */
+  public ObjectName[] manageStat(String statisticsTypeName)
+      throws AdminException, MalformedObjectNameException {
+
+    return Helper.manageStat(this, statisticsTypeName);
+  }
+
+  // -------------------------------------------------------------------------
+  // JMX Notification listener
+  // -------------------------------------------------------------------------
+
+  /**
+   * Handles notification to refresh. Reacts by refreshing the values of this GemFireManager's
+   * ConfigurationParamaters. Any other notification is ignored. Given notification is handled only
+   * if there is any JMX client connected to the system.
+   * 
+   * @param notification the JMX notification being received
+   * @param hb handback object is unused
+   */
+  public void handleNotification(Notification notification, Object hb) {
+    AdminDistributedSystemJmxImpl systemJmx = (AdminDistributedSystemJmxImpl) this.system;
+
+    if (!systemJmx.isRmiClientCountZero()) {
+      Helper.handleNotification(this, notification, hb);
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // Template methods overriden from superclass...
+  // -------------------------------------------------------------------------
+
+  /**
+   * Template method for creating instance of ConfigurationParameter. Overridden to return
+   * ConfigurationParameterJmxImpl.
+   */
+  @Override
+  protected ConfigurationParameter createConfigurationParameter(String name, String description,
+      Object value, Class type, boolean userModifiable) {
+    return new ConfigurationParameterJmxImpl(name, description, value, type, userModifiable);
+  }
+
+
+
+  /**
+   * Override createStatisticResource by instantiating StatisticResourceJmxImpl if it was not
+   * created earlier otherwise returns the same instance.
+   * 
+   * @param stat StatResource reference for which this JMX resource is to be created
+   * @return StatisticResourceJmxImpl - JMX Implementation of StatisticResource
+   * @throws AdminException if constructing StatisticResourceJmxImpl instance fails
+   */
+  @Override
+  protected StatisticResource createStatisticResource(StatResource stat) throws AdminException {
+    StatisticResourceJmxImpl managedStatisticResource = null;
+
+    synchronized (this.managedStatisticsResourcesMap) {
+      /*
+       * Ensuring that a single instance of Statistic Resource is created per StatResource.
+       */
+      StatisticResourceJmxImpl statisticResourceJmxImpl = managedStatisticsResourcesMap.get(stat);
+      if (statisticResourceJmxImpl != null) {
+        managedStatisticResource = statisticResourceJmxImpl;
+      } else {
+        managedStatisticResource = new StatisticResourceJmxImpl(stat, this);
+        managedStatisticResource.getStatistics();// inits timer
+        managedStatisticsResourcesMap.put(stat, managedStatisticResource);
+      }
+    }
+    return managedStatisticResource;
+  }
+
+  /**
+   * Override createSystemMemberCache by instantiating SystemMemberCacheJmxImpl if it was not
+   * created earlier.
+   * 
+   * @param vm GemFireVM reference for which this JMX resource is to be created
+   * @return SystemMemberCacheJmxImpl - JMX Implementation of SystemMemberCache
+   * @throws AdminException if constructing SystemMemberCacheJmxImpl instance fails
+   */
+  @Override
+  protected SystemMemberCache createSystemMemberCache(GemFireVM vm) throws AdminException {
+    if (managedSystemMemberCache == null) {
+      managedSystemMemberCache = new SystemMemberCacheJmxImpl(vm);
+    }
+    return managedSystemMemberCache;
+  }
+
+  // -------------------------------------------------------------------------
+  // Create MBean attributes for each ConfigurationParameter
+  // -------------------------------------------------------------------------
+
+  /**
+   * Add MBean attribute definitions for each ConfigurationParameter.
+   *
+   * @param managed the mbean definition to add attributes to
+   * @return a new instance of ManagedBean copied from <code>managed</code> but with the new
+   *         attributes added
+   */
+  public ManagedBean addDynamicAttributes(ManagedBean managed) throws AdminException {
+    return Helper.addDynamicAttributes(this, managed);
+  }
+
+  /**
+   * Cleans up Managed Resources created for the client that was connected to the server represented
+   * by this class.
+   * 
+   * @param clientId id of the client to be removed
+   * @return List of ManagedResources associated with the client of given client id
+   */
+  /*
+   * This clean up is for the clients. The clients are started with a loner DM. Hence the clientId
+   * is not supposed to contain '/' as per InternalDistributedMember.toString().
+   */
+  public List<ManagedResource> cleanupBridgeClientResources(String clientId) {
+    List<ManagedResource> returnedResources = new ArrayList<ManagedResource>();
+
+    String compatibleId = "id_" + MBeanUtil.makeCompliantMBeanNameProperty(clientId);
+    synchronized (this.managedStatisticsResourcesMap) {
+      Set<Entry<StatResource, StatisticResourceJmxImpl>> entrySet =
+          this.managedStatisticsResourcesMap.entrySet();
+
+      for (Iterator<Entry<StatResource, StatisticResourceJmxImpl>> it = entrySet.iterator(); it
+          .hasNext();) {
+        Entry<StatResource, StatisticResourceJmxImpl> entry = it.next();
+        StatisticResourceJmxImpl resource = entry.getValue();
+        if (resource.getMBeanName().contains(compatibleId)) {
+          it.remove(); // remove matching entry
+          returnedResources.add(resource);
+        }
+      }
+    }
+    return returnedResources;
+  }
+
+  /**
+   * Implementation handles client membership changes.
+   * 
+   * @param clientId id of the client for whom membership change happened
+   * @param eventType membership change type; one of {@link ClientMembershipMessage#JOINED},
+   *        {@link ClientMembershipMessage#LEFT}, {@link ClientMembershipMessage#CRASHED}
+   */
+  public void handleClientMembership(String clientId, int eventType) {
+    String notifType = null;
+    List<ManagedResource> cleanedUp = null;
+
+    if (eventType == ClientMembershipMessage.LEFT) {
+      notifType = NOTIF_CLIENT_LEFT;
+      cleanedUp = cleanupBridgeClientResources(clientId);
+    } else if (eventType == ClientMembershipMessage.CRASHED) {
+      notifType = NOTIF_CLIENT_CRASHED;
+      cleanedUp = cleanupBridgeClientResources(clientId);
+    } else if (eventType == ClientMembershipMessage.JOINED) {
+      notifType = NOTIF_CLIENT_JOINED;
+    }
+
+    if (cleanedUp != null) {
+      for (ManagedResource resource : cleanedUp) {
+        MBeanUtil.unregisterMBean(resource);
+      }
+    }
+
+    Helper.sendNotification(this, new Notification(notifType, this.modelMBean,
+        Helper.getNextNotificationSequenceNumber(), clientId));
+  }
+
+  /**
+   * Implementation handles creation of cache by extracting the details from the given event object
+   * and sending the {@link SystemMemberJmx#NOTIF_CACHE_CREATED} notification to the connected JMX
+   * Clients.
+   * 
+   * @param event event object corresponding to the creation of the cache
+   */
+  public void handleCacheCreate(SystemMemberCacheEvent event) {
+    Helper.sendNotification(this, new Notification(NOTIF_CACHE_CREATED, this.modelMBean,
+        Helper.getNextNotificationSequenceNumber(), Helper.getCacheEventDetails(event)));
+  }
+
+  /**
+   * Implementation handles closure of cache by extracting the details from the given event object
+   * and sending the {@link SystemMemberJmx#NOTIF_CACHE_CLOSED} notification to the connected JMX
+   * Clients.
+   * 
+   * @param event event object corresponding to the closure of the cache
+   */
+  public void handleCacheClose(SystemMemberCacheEvent event) {
+    Helper.sendNotification(this, new Notification(NOTIF_CACHE_CLOSED, this.modelMBean,
+        Helper.getNextNotificationSequenceNumber(), Helper.getCacheEventDetails(event)));
+  }
+
+  /**
+   * Implementation handles creation of region by extracting the details from the given event object
+   * and sending the {@link SystemMemberJmx#NOTIF_REGION_CREATED} notification to the connected JMX
+   * Clients. Region Path is set as User Data in Notification.
+   * 
+   * @param event event object corresponding to the creation of a region
+   */
+  public void handleRegionCreate(SystemMemberRegionEvent event) {
+    Notification notification = new Notification(NOTIF_REGION_CREATED, this.modelMBean,
+        Helper.getNextNotificationSequenceNumber(), Helper.getRegionEventDetails(event));
+
+    notification.setUserData(event.getRegionPath());
+
+    Helper.sendNotification(this, notification);
+  }
+
+  /**
+   * Implementation should handle loss of region by extracting the details from the given event
+   * object and sending the {@link SystemMemberJmx#NOTIF_REGION_LOST} notification to the connected
+   * JMX Clients. Region Path is set as User Data in Notification. Additionally, it also clears the
+   * ManagedResources created for the region that is lost.
+   * 
+   * @param event event object corresponding to the loss of a region
+   */
+  public void handleRegionLoss(SystemMemberRegionEvent event) {
+    SystemMemberCacheJmxImpl cacheResource = this.managedSystemMemberCache;
+
+    if (cacheResource != null) {
+      ManagedResource cleanedUp = cacheResource.cleanupRegionResources(event.getRegionPath());
+
+      if (cleanedUp != null) {
+        MBeanUtil.unregisterMBean(cleanedUp);
+      }
+    }
+
+    Notification notification = new Notification(NOTIF_REGION_LOST, this.modelMBean,
+        Helper.getNextNotificationSequenceNumber(), Helper.getRegionEventDetails(event));
+
+    notification.setUserData(event.getRegionPath());
+
+    Helper.sendNotification(this, notification);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigAttributeInfo.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigAttributeInfo.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigAttributeInfo.java
new file mode 100755
index 0000000..e94f521
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigAttributeInfo.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import org.apache.geode.internal.Assert;
+import org.apache.geode.internal.admin.api.ConfigurationParameter;
+
+import javax.management.Descriptor;
+import javax.management.modelmbean.DescriptorSupport;
+import javax.management.modelmbean.ModelMBeanAttributeInfo;
+
+/**
+ * Subclass of AttributeInfo with {@link ConfigurationParameter} added for use as the
+ * {@link javax.management.modelmbean.ModelMBeanAttributeInfo} descriptor's <i>targetObject</i>
+ * value.
+ *
+ * @since GemFire 3.5
+ *
+ */
+class ConfigAttributeInfo extends org.apache.commons.modeler.AttributeInfo {
+  private static final long serialVersionUID = -1918437700841687078L;
+
+  private final ConfigurationParameterJmxImpl config;
+
+  public ConfigAttributeInfo(ConfigurationParameterJmxImpl config) {
+    super();
+    this.config = config;
+  }
+
+  public ConfigurationParameterJmxImpl getConfig() {
+    return this.config;
+  }
+
+  @Override
+  public ModelMBeanAttributeInfo createAttributeInfo() {
+    Descriptor desc = new DescriptorSupport(new String[] {"name=" + this.displayName,
+        "descriptorType=attribute", "currencyTimeLimit=-1", // always stale
+        "displayName=" + this.displayName, "getMethod=getJmxValue", "setMethod=setJmxValue"});
+
+    Assert.assertTrue(this.config != null, "Config target object is null!");
+    desc.setField("targetObject", this.config);
+
+    ModelMBeanAttributeInfo info = new ModelMBeanAttributeInfo(this.displayName, // name
+        this.type, // type
+        this.description, // description
+        this.readable, // isReadable
+        this.writeable, // isWritable
+        this.is, // isIs
+        desc);
+
+    return info;
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigurationParameterJmxImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigurationParameterJmxImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigurationParameterJmxImpl.java
new file mode 100755
index 0000000..0476fdb
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/ConfigurationParameterJmxImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import org.apache.logging.log4j.Level;
+
+import org.apache.geode.SystemFailure;
+import org.apache.geode.internal.admin.api.UnmodifiableConfigurationException;
+import org.apache.geode.internal.Assert;
+import org.apache.geode.internal.admin.api.impl.ConfigurationParameterImpl;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+
+/**
+ * Provides MBean support for managing accessing a ConfigurationParameter.
+ * <p>
+ * Implements java.io.Serializable because several MBeans have attributes of type
+ * ConfigurationParameter. This means that calls to getMBeanInfo which may be serialized for remote
+ * clients will be broken unless those attributes support serialization.
+ * <p>
+ * TODO: refactor to implement ConfigurationParameter and delegate to ConfigurationParameterImpl.
+ * Wrap all delegate calls w/ e.printStackTrace() since the HttpAdaptor devours them
+ *
+ * @since GemFire 3.5
+ *
+ */
+public class ConfigurationParameterJmxImpl extends ConfigurationParameterImpl
+    implements Serializable {
+
+  private static final long serialVersionUID = -7822171853906772375L;
+  private boolean deserialized = false;
+
+  // -------------------------------------------------------------------------
+  // Constructor(s)
+  // -------------------------------------------------------------------------
+
+  protected ConfigurationParameterJmxImpl(String name, String description, Object value, Class type,
+      boolean userModifiable) {
+    super(name, description, value, type, userModifiable);
+  }
+
+  protected ConfigurationParameterJmxImpl(String name, Object value) {
+    super(name, value);
+  }
+
+  /** Constructor to allow serialization */
+  protected ConfigurationParameterJmxImpl() {
+    super();
+  }
+
+  @Override
+  public void setValue(Object value) throws UnmodifiableConfigurationException {
+    if (deserialized) {
+      throw new UnsupportedOperationException(
+          LocalizedStrings.ConfigurationParameterJmxImpl_REMOTE_MUTATION_OF_CONFIGURATIONPARAMETER_IS_CURRENTLY_UNSUPPORTED
+              .toLocalizedString());
+    }
+    try {
+      super.setValue(value);
+    } catch (UnmodifiableConfigurationException e) {
+      MBeanUtil.logStackTrace(Level.WARN, e);
+      throw e;
+    } catch (java.lang.RuntimeException e) {
+      MBeanUtil.logStackTrace(Level.WARN, e);
+      throw e;
+    } catch (VirtualMachineError err) {
+      SystemFailure.initiateFailure(err);
+      // If this ever returns, rethrow the error. We're poisoned
+      // now, so don't let this thread continue.
+      throw err;
+    } catch (java.lang.Error e) {
+      // Whenever you catch Error or Throwable, you must also
+      // catch VirtualMachineError (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();
+      MBeanUtil.logStackTrace(Level.ERROR, e);
+      throw e;
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // HACK
+  // -------------------------------------------------------------------------
+  public void setJmxValue(Integer value) throws UnmodifiableConfigurationException {
+    setValue(value);
+  }
+
+  public void setJmxValue(String value) throws UnmodifiableConfigurationException {
+    setValue(value);
+  }
+
+  public void setJmxValue(java.io.File value) throws UnmodifiableConfigurationException {
+    setValue(value);
+  }
+
+  public void setJmxValue(Boolean value) throws UnmodifiableConfigurationException {
+    setValue(value);
+  }
+
+  public Class getJmxValueType() {
+    if (isInetAddress() || isFile() || isOctal()) {
+      return java.lang.String.class;
+    }
+    return getValueType();
+  }
+
+  public Object getJmxValue() {
+    if (isInetAddress() || isFile() || isOctal()) {
+      return getValueAsString();
+    }
+    return getValue();
+  }
+
+  /**
+   * Override writeObject which is used in serialization. This class is serialized when JMX client
+   * acquires MBeanInfo for ConfigurationParameter MBean. Super class is not serializable.
+   */
+  private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+    out.writeObject(this.name);
+    out.writeObject(this.description);
+    out.writeObject(this.value);
+    out.writeObject(this.type);
+    out.writeBoolean(this.userModifiable);
+  }
+
+  /**
+   * Override readObject which is used in serialization. Customize serialization of this exception
+   * to avoid escape of InternalRole which is not Serializable.
+   */
+  private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+    String inName = (String) in.readObject();
+    String inDescription = (String) in.readObject();
+    Object inValue = in.readObject();
+    Class inClass = (Class) in.readObject();
+    boolean inUserModifiable = in.readBoolean();
+
+    Assert.assertTrue(inName != null);
+    Assert.assertTrue(inDescription != null);
+    Assert.assertTrue(inValue != null);
+    Assert.assertTrue(inClass != null);
+
+    this.deserialized = true;
+    this.name = inName;
+    setInternalState(inDescription, inValue, inClass, inUserModifiable);
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributedSystemHealthConfigJmxImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributedSystemHealthConfigJmxImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributedSystemHealthConfigJmxImpl.java
new file mode 100644
index 0000000..af13bbb
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributedSystemHealthConfigJmxImpl.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import org.apache.geode.internal.admin.api.AdminException;
+import org.apache.geode.internal.admin.api.GemFireHealth;
+import org.apache.geode.internal.admin.api.impl.DistributedSystemHealthConfigImpl;
+import javax.management.*;
+import javax.management.modelmbean.*;
+
+/**
+ * The JMX "managed resource" that represents the configuration for the health of a distributed
+ * system. Basically, it provides the behavior of <code>DistributedSystemHealthConfigImpl</code>,
+ * but does some JMX stuff like registering beans with the agent.
+ *
+ * @see GemFireHealthJmxImpl#createDistributedSystemHealthConfig
+ *
+ *
+ * @since GemFire 3.5
+ */
+public class DistributedSystemHealthConfigJmxImpl extends DistributedSystemHealthConfigImpl
+    implements ManagedResource {
+
+  /** The <code>GemFireHealth</code> that we help configure */
+  private GemFireHealth health;
+
+  /** The name of the MBean that will manage this resource */
+  private String mbeanName;
+
+  /** The ModelMBean that is configured to manage this resource */
+  private ModelMBean modelMBean;
+
+  /** The JMX object name of the MBean for this managed resource */
+  private final ObjectName objectName;
+
+  /////////////////////// Constructors ///////////////////////
+
+  /**
+   * Creates a new <code>DistributedSystemHealthCOnfigJmxImpl</code> that configures the health of
+   * the distributed system monitored by <code>health</code>.
+   */
+  DistributedSystemHealthConfigJmxImpl(GemFireHealthJmxImpl health) throws AdminException {
+
+    super();
+    this.health = health;
+    this.mbeanName =
+        new StringBuffer().append(MBEAN_NAME_PREFIX).append("DistributedSystemHealthConfig,id=")
+            .append(MBeanUtil.makeCompliantMBeanNameProperty(health.getDistributedSystem().getId()))
+            .toString();
+    this.objectName = MBeanUtil.createMBean(this);
+  }
+
+  ////////////////////// Instance Methods //////////////////////
+
+  /**
+   * Applies the changes made to this config back to the health monitor.
+   *
+   * @see GemFireHealth#setDistributedSystemHealthConfig
+   */
+  public void applyChanges() {
+    this.health.setDistributedSystemHealthConfig(this);
+  }
+
+  public String getMBeanName() {
+    return this.mbeanName;
+  }
+
+  public ModelMBean getModelMBean() {
+    return this.modelMBean;
+  }
+
+  public void setModelMBean(ModelMBean modelMBean) {
+    this.modelMBean = modelMBean;
+  }
+
+  public ManagedResourceType getManagedResourceType() {
+    return ManagedResourceType.DISTRIBUTED_SYSTEM_HEALTH_CONFIG;
+  }
+
+  public ObjectName getObjectName() {
+    return this.objectName;
+  }
+
+  public void cleanupResource() {}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributionLocatorJmxImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributionLocatorJmxImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributionLocatorJmxImpl.java
new file mode 100755
index 0000000..8da7482
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DistributionLocatorJmxImpl.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import org.apache.geode.internal.admin.api.DistributionLocatorConfig;
+import org.apache.geode.internal.admin.api.impl.AdminDistributedSystemImpl;
+import org.apache.geode.internal.admin.api.impl.DistributionLocatorImpl;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import javax.management.ObjectName;
+import javax.management.modelmbean.ModelMBean;
+
+/**
+ * Provides MBean support for managing a distribution locator.
+ *
+ */
+public class DistributionLocatorJmxImpl extends DistributionLocatorImpl
+    implements ManagedResource, DistributionLocatorConfig {
+
+  /** The JMX object name of this managed resource */
+  private ObjectName objectName;
+
+  // -------------------------------------------------------------------------
+  // Constructor(s)
+  // -------------------------------------------------------------------------
+
+  /**
+   * Constructs new instance of DistributionLocatorJmxImpl for managing a distribution locator
+   * service via JMX.
+   */
+  public DistributionLocatorJmxImpl(DistributionLocatorConfig config,
+      AdminDistributedSystemImpl system) {
+    super(config, system);
+    initializeMBean();
+  }
+
+  /** Create and register the MBean to manage this resource */
+  private void initializeMBean() {
+    this.mbeanName =
+        "GemFire:type=DistributionLocator,id=" + MBeanUtil.makeCompliantMBeanNameProperty(getId());
+    this.objectName = MBeanUtil.createMBean(this, MBeanUtil.lookupManagedBean(this));
+  }
+
+  //////////////////////// Configuration ////////////////////////
+
+  public String getHost() {
+    return this.getConfig().getHost();
+  }
+
+  public void setHost(String host) {
+    this.getConfig().setHost(host);
+  }
+
+  public String getWorkingDirectory() {
+    return this.getConfig().getWorkingDirectory();
+  }
+
+  public void setWorkingDirectory(String dir) {
+    this.getConfig().setWorkingDirectory(dir);
+  }
+
+  public String getProductDirectory() {
+    return this.getConfig().getProductDirectory();
+  }
+
+  public void setProductDirectory(String dir) {
+    this.getConfig().setProductDirectory(dir);
+  }
+
+  public String getRemoteCommand() {
+    return this.getConfig().getRemoteCommand();
+  }
+
+  public void setRemoteCommand(String remoteCommand) {
+    this.getConfig().setRemoteCommand(remoteCommand);
+  }
+
+  public java.util.Properties getDistributedSystemProperties() {
+    return this.getConfig().getDistributedSystemProperties();
+  }
+
+  public void setDistributedSystemProperties(java.util.Properties props) {
+    this.getConfig().setDistributedSystemProperties(props);
+  }
+
+  public void validate() {
+    throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString());
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString());
+  }
+
+  public int getPort() {
+    return this.getConfig().getPort();
+  }
+
+  public void setPort(int port) {
+    this.getConfig().setPort(port);
+  }
+
+  public String getBindAddress() {
+    return this.getConfig().getBindAddress();
+  }
+
+  public void setBindAddress(String bindAddress) {
+    this.getConfig().setBindAddress(bindAddress);
+  }
+
+  // -------------------------------------------------------------------------
+  // MBean attributes - accessors/mutators
+  // -------------------------------------------------------------------------
+
+  // -------------------------------------------------------------------------
+  // JMX Notification listener
+  // -------------------------------------------------------------------------
+
+  // -------------------------------------------------------------------------
+  // ManagedResource implementation
+  // -------------------------------------------------------------------------
+
+  /** The name of the MBean that will manage this resource */
+  private String mbeanName;
+
+  /** The ModelMBean that is configured to manage this resource */
+  private ModelMBean modelMBean;
+
+  public String getMBeanName() {
+    return this.mbeanName;
+  }
+
+  public ModelMBean getModelMBean() {
+    return this.modelMBean;
+  }
+
+  public void setModelMBean(ModelMBean modelMBean) {
+    this.modelMBean = modelMBean;
+  }
+
+  public ObjectName getObjectName() {
+    return this.objectName;
+  }
+
+  public ManagedResourceType getManagedResourceType() {
+    return ManagedResourceType.DISTRIBUTION_LOCATOR;
+  }
+
+  public void cleanupResource() {}
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/20a32286/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DynamicManagedBean.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DynamicManagedBean.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DynamicManagedBean.java
new file mode 100755
index 0000000..5693a5a
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/DynamicManagedBean.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.modeler.AttributeInfo;
+import org.apache.commons.modeler.OperationInfo;
+import org.apache.commons.modeler.ManagedBean;
+
+/**
+ * Extends ManagedBean to allow for dynamically creating new instances of ManagedBean based on an
+ * existing instance of ManagedBean.
+ * 
+ * @since GemFire 5.0.1
+ */
+public class DynamicManagedBean extends org.apache.commons.modeler.ManagedBean {
+  private static final long serialVersionUID = 4051924500150228160L;
+
+  public DynamicManagedBean(ManagedBean managed) {
+    super();
+
+    this.attributes = managed.getAttributes();
+    this.className = managed.getClassName();
+    this.constructors = managed.getConstructors();
+    this.description = managed.getDescription();
+    this.domain = managed.getDomain();
+    this.group = managed.getGroup();
+    this.name = managed.getName();
+    this.fields = managed.getFields();
+    this.notifications = managed.getNotifications();
+    this.operations = managed.getOperations();
+    this.type = managed.getType();
+
+    /*
+     * we don't use modelerType and it's nice to remove it to keep the list of attributes cleaned
+     * up...
+     */
+    removeAttribute("modelerType");
+  }
+
+  /**
+   * Removes an attribute from this ManagedBean's attribute descriptor list.
+   *
+   * @param name the attribute to be removed
+   */
+  public void removeAttribute(String name) {
+    if (name == null || name.length() < 1) {
+      return;
+    }
+    synchronized (this.attributes) {
+      List attributesList = new ArrayList(this.attributes.length);
+      for (int i = 0; i < this.attributes.length; i++) {
+        if (!name.equals(this.attributes[i].getName())) {
+          attributesList.add(this.attributes[i]);
+        }
+      }
+      this.attributes =
+          (AttributeInfo[]) attributesList.toArray(new AttributeInfo[attributesList.size()]);
+
+      /*
+       * super.info should be nulled out anytime the structure is changed, such as altering the
+       * attributes, operations, or notifications
+       *
+       * however super.info is private, so we need the following hack to cause the super class to
+       * null it out for us...
+       */
+      setType(this.type); // causes this in super: "this.info = null;"
+    }
+  }
+
+  /**
+   * Removes the operation with the given name from thie <code>ManageBean</code>'s operation
+   * descriptor list.
+   *
+   * @since GemFire 4.0
+   */
+  public void removeOperation(String name) {
+    if (name == null || name.length() < 1) {
+      return;
+    }
+
+    synchronized (operations) {
+      List operationsList = new ArrayList(this.operations.length);
+      for (int i = 0; i < this.operations.length; i++) {
+        if (!name.equals(this.operations[i].getName())) {
+          operationsList.add(this.operations[i]);
+        }
+      }
+      this.operations =
+          (OperationInfo[]) operationsList.toArray(new OperationInfo[operationsList.size()]);
+
+      /*
+       * super.info should be nulled out anytime the structure is changed, such as altering the
+       * operations, operations, or notifications
+       *
+       * however super.info is private, so we need the following hack to cause the super class to
+       * null it out for us...
+       */
+      setType(this.type); // causes this in super: "this.info = null;"
+    }
+  }
+
+  /**
+   * Return a string representation of this managed bean.
+   */
+  @Override
+  public String toString() {
+    StringBuffer sb = new StringBuffer("DynamicManagedBean[");
+    sb.append("name=");
+    sb.append(name);
+    sb.append(", className=");
+    sb.append(className);
+    sb.append(", description=");
+    sb.append(description);
+    if (group != null) {
+      sb.append(", group=");
+      sb.append(group);
+    }
+    sb.append(", type=");
+    sb.append(type);
+    sb.append(", attributes=");
+    sb.append(Arrays.asList(attributes));
+    sb.append("]");
+    return (sb.toString());
+  }
+}
+