http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/6bb27bf1/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AgentLauncher.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AgentLauncher.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AgentLauncher.java deleted file mode 100644 index 6a1bd63..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AgentLauncher.java +++ /dev/null @@ -1,918 +0,0 @@ -/* - * 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.admin.jmx.internal; - -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.admin.AdminException; -import org.apache.geode.admin.jmx.Agent; -import org.apache.geode.admin.jmx.AgentConfig; -import org.apache.geode.admin.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/6bb27bf1/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/CacheServerJmxImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/CacheServerJmxImpl.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/CacheServerJmxImpl.java deleted file mode 100644 index 5e6202d..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/CacheServerJmxImpl.java +++ /dev/null @@ -1,590 +0,0 @@ -/* - * 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.admin.jmx.internal; - -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.admin.AdminException; -import org.apache.geode.admin.CacheServerConfig; -import org.apache.geode.admin.CacheVmConfig; -import org.apache.geode.admin.ConfigurationParameter; -import org.apache.geode.admin.StatisticResource; -import org.apache.geode.admin.SystemMemberCache; -import org.apache.geode.admin.SystemMemberCacheEvent; -import org.apache.geode.admin.SystemMemberRegionEvent; -import org.apache.geode.admin.internal.CacheServerImpl; -import org.apache.geode.admin.internal.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 org.apache.geode.admin.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 org.apache.geode.admin.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 org.apache.geode.admin.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 org.apache.geode.admin.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 org.apache.geode.admin.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/6bb27bf1/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigAttributeInfo.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigAttributeInfo.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigAttributeInfo.java deleted file mode 100755 index 3273ccc..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigAttributeInfo.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.admin.jmx.internal; - -// import org.apache.geode.admin.ConfigurationParameter; -import org.apache.geode.internal.Assert; - -import javax.management.Descriptor; -import javax.management.modelmbean.DescriptorSupport; -import javax.management.modelmbean.ModelMBeanAttributeInfo; - -/** - * Subclass of AttributeInfo with {@link org.apache.geode.admin.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/6bb27bf1/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigurationParameterJmxImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigurationParameterJmxImpl.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigurationParameterJmxImpl.java deleted file mode 100755 index f9add13..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigurationParameterJmxImpl.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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.admin.jmx.internal; - -import java.io.IOException; -import java.io.Serializable; - -import org.apache.logging.log4j.Level; - -import org.apache.geode.SystemFailure; -import org.apache.geode.admin.UnmodifiableConfigurationException; -import org.apache.geode.internal.Assert; -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 org.apache.geode.admin.internal.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/6bb27bf1/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributedSystemHealthConfigJmxImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributedSystemHealthConfigJmxImpl.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributedSystemHealthConfigJmxImpl.java deleted file mode 100644 index 54d3072..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributedSystemHealthConfigJmxImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.admin.jmx.internal; - -import org.apache.geode.admin.*; -import org.apache.geode.admin.internal.*; -// import org.apache.geode.internal.admin.*; -import javax.management.*; -import javax.management.modelmbean.*; -// import org.apache.commons.modeler.ManagedBean; - -/** - * 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/6bb27bf1/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributionLocatorJmxImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributionLocatorJmxImpl.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributionLocatorJmxImpl.java deleted file mode 100755 index 65c2477..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributionLocatorJmxImpl.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.admin.jmx.internal; - -// import org.apache.geode.admin.AdminException; -// import org.apache.geode.admin.DistributionLocator; -import org.apache.geode.admin.DistributionLocatorConfig; -import org.apache.geode.admin.internal.AdminDistributedSystemImpl; -import org.apache.geode.internal.i18n.LocalizedStrings; -// import org.apache.geode.internal.Assert; - -// import org.apache.commons.modeler.ManagedBean; -// import org.apache.commons.modeler.AttributeInfo; - -// import java.util.Date; -// import java.util.Set; - -// import javax.management.Attribute; -// import javax.management.AttributeList; -// import javax.management.Descriptor; -// import javax.management.JMException; -// import javax.management.MBeanServer; -// import javax.management.MalformedObjectNameException; -// import javax.management.Notification; -// import javax.management.NotificationListener; -import javax.management.ObjectName; -// import javax.management.modelmbean.DescriptorSupport; -import javax.management.modelmbean.ModelMBean; -// import javax.management.modelmbean.ModelMBeanAttributeInfo; - -/** - * Provides MBean support for managing a distribution locator. - * - */ -public class DistributionLocatorJmxImpl - extends org.apache.geode.admin.internal.DistributionLocatorImpl - implements org.apache.geode.admin.jmx.internal.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/6bb27bf1/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DynamicManagedBean.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DynamicManagedBean.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DynamicManagedBean.java deleted file mode 100755 index 32ee5b4..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DynamicManagedBean.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.admin.jmx.internal; - - -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()); - } -} -
