http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/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 b87792d..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AgentLauncher.java +++ /dev/null @@ -1,888 +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/c0221bed/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 e08f3a5..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/CacheServerJmxImpl.java +++ /dev/null @@ -1,641 +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/c0221bed/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 9f7f940..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigAttributeInfo.java +++ /dev/null @@ -1,76 +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/c0221bed/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 a318eb5..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/ConfigurationParameterJmxImpl.java +++ /dev/null @@ -1,169 +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/c0221bed/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 18c3fd5..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributedSystemHealthConfigJmxImpl.java +++ /dev/null @@ -1,107 +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/c0221bed/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 ad60682..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DistributionLocatorJmxImpl.java +++ /dev/null @@ -1,185 +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/c0221bed/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 1b62fb0..0000000 --- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/DynamicManagedBean.java +++ /dev/null @@ -1,141 +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()); - } -} -