org.apache.geode.admin.jmx.internal.AgentImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.geode.admin.jmx.internal.AgentImpl.java

Source

/*
 * 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.IOException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.ReflectionException;
import javax.management.modelmbean.ModelMBean;
import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.rmi.ssl.SslRMIClientSocketFactory;

import mx4j.tools.adaptor.http.HttpAdaptor;

import org.apache.logging.log4j.Logger;

import org.apache.geode.GemFireException;
import org.apache.geode.GemFireIOException;
import org.apache.geode.LogWriter;
import org.apache.geode.SystemFailure;
import org.apache.geode.admin.AdminDistributedSystem;
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.i18n.StringId;
import org.apache.geode.internal.Banner;
import org.apache.geode.internal.GemFireVersion;
import org.apache.geode.internal.admin.remote.TailLogResponse;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.InternalLogWriter;
import org.apache.geode.internal.logging.LogConfig;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.LogWriterFactory;
import org.apache.geode.internal.logging.LoggingThreadGroup;
import org.apache.geode.internal.logging.log4j.AlertAppender;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.internal.logging.log4j.LogWriterAppender;
import org.apache.geode.internal.logging.log4j.LogWriterAppenders;

/**
 * The GemFire JMX Agent provides the ability to administrate one GemFire distributed system via
 * JMX.
 *
 * @since GemFire 3.5
 */
public class AgentImpl
        implements org.apache.geode.admin.jmx.Agent, org.apache.geode.admin.jmx.internal.ManagedResource {

    private static final Logger logger = LogService.getLogger();

    /**
     * MX4J HttpAdaptor only supports "basic" as an authentication method. Enabling HttpAdaptor
     * authentication ({@link AgentConfig#HTTP_AUTHENTICATION_ENABLED_NAME}) causes the browser to
     * require a login with username ({@link AgentConfig#HTTP_AUTHENTICATION_USER_NAME}) and password
     * ({@link AgentConfig#HTTP_AUTHENTICATION_PASSWORD_NAME}).
     */
    private static final String MX4J_HTTPADAPTOR_BASIC_AUTHENTICATION = "basic";

    /** JMX Service URL template for JMX/RMI Connector Server */
    private static final String JMX_SERVICE_URL = "service:jmx:rmi://{0}:{1}/jndi/rmi://{2}:{3}{4}";

    /**
     * Set third-party logging configration: MX4J, Jakarta Commons-Logging.
     */
    static {
        checkDebug();
        String commonsLog = System.getProperty("org.apache.commons.logging.log");
        if (commonsLog == null || commonsLog.length() == 0) {
            System.setProperty("org.apache.commons.logging.log", "org.apache.commons.logging.impl.SimpleLog");
        }
    }

    /** Enables mx4j tracing if Agent debugging is enabled. */
    private static void checkDebug() {
        try {
            if (Boolean.getBoolean("gfAgentDebug")) {
                mx4j.log.Log.setDefaultPriority(mx4j.log.Logger.TRACE); // .DEBUG
            }
        } 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 (Throwable t) {
            // 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();
            /* ignore */
        }
    }

    // -------------------------------------------------------------------------
    // Member variables
    // -------------------------------------------------------------------------

    /** This Agent's log writer */
    private LogWriterAppender logWriterAppender;
    private InternalLogWriter logWriter;

    /** This Agent's JMX http adaptor from MX4J */
    private HttpAdaptor httpAdaptor;

    /** This Agent's RMI Connector Server from MX4J */
    private JMXConnectorServer rmiConnector;

    /** The name of the MBean manages this resource */
    private final String mbeanName;

    /** The ObjectName of the MBean that manages this resource */
    private final ObjectName objectName;

    /** The actual ModelMBean that manages this resource */
    private ModelMBean modelMBean;

    /** The configuration for this Agent */
    private final AgentConfigImpl agentConfig;

    /**
     * The <code>AdminDistributedSystem</code> this Agent is currently connected to or
     * <code>null</code>
     */
    private AdminDistributedSystem system;

    /** The agent's configuration file */
    private String propertyFile;

    /**
     * A lock object to guard the Connect and Disconnect calls being made on the agent for connections
     * to the DS
     **/
    private Object CONN_SYNC = new Object();

    protected MemberInfoWithStatsMBean memberInfoWithStatsMBean;

    private MBeanServer mBeanServer;

    // -------------------------------------------------------------------------
    // Constructor(s)
    // -------------------------------------------------------------------------

    /**
     * Constructs a new Agent using the specified configuration.
     *
     * @param agentConfig instance of configuration for Agent
     * @throws org.apache.geode.admin.AdminException TODO-javadocs
     * @throws IllegalArgumentException if agentConfig is null
     */
    public AgentImpl(AgentConfigImpl agentConfig) throws AdminException, IllegalArgumentException {
        addShutdownHook();
        if (agentConfig == null) {
            throw new IllegalArgumentException(
                    LocalizedStrings.AgentImpl_AGENTCONFIG_MUST_NOT_BE_NULL.toLocalizedString());
        }
        this.agentConfig = (AgentConfigImpl) agentConfig;
        this.mbeanName = MBEAN_NAME_PREFIX + MBeanUtil.makeCompliantMBeanNameProperty("Agent");

        try {
            this.objectName = new ObjectName(this.mbeanName);
        } catch (MalformedObjectNameException ex) {
            String s = LocalizedStrings.AgentImpl_WHILE_CREATING_OBJECTNAME_0
                    .toLocalizedString(new Object[] { this.mbeanName });
            throw new AdminException(s, ex);
        }

        this.propertyFile = this.agentConfig.getPropertyFile().getAbsolutePath();

        // bind address only affects how the Agent VM connects to the system...
        // It should be set only once in the agent lifecycle
        this.agentConfig.setBindAddress(getBindAddress());

        // LOG: create LogWriterAppender and LogWriterLogger
        initLogWriter();

        mBeanServer = MBeanUtil.start();

        MBeanUtil.createMBean(this);

        initializeHelperMbean();
    }

    private void initializeHelperMbean() {
        try {
            memberInfoWithStatsMBean = new MemberInfoWithStatsMBean(this);

            MBeanServer mbs = getMBeanServer();
            mbs.registerMBean(memberInfoWithStatsMBean, memberInfoWithStatsMBean.getObjectName());
            /*
             * We are not re-throwing these exceptions as failure create/register the GemFireTypesWrapper
             * will not stop the Agent from working. But we are logging it as it could be an indication of
             * some problem. Also not creating Localized String for the exception.
             */
        } catch (OperationsException e) {
            logger.info(LocalizedMessage
                    .create(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN), e);
        } catch (MBeanRegistrationException e) {
            logger.info(LocalizedMessage
                    .create(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN), e);
        } catch (AdminException e) {
            logger.info(LocalizedMessage
                    .create(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN), e);
        }
    }

    // -------------------------------------------------------------------------
    // Public operations
    // -------------------------------------------------------------------------

    public AgentConfig getConfig() {
        return this.agentConfig;
    }

    public AdminDistributedSystem getDistributedSystem() {
        return this.system;
    }

    /**
     * Persists the current Agent configuration to its property file.
     *
     * @throws GemFireIOException if unable to persist the configuration to props
     * @see #getPropertyFile
     */
    public void saveProperties() {
        throw new GemFireIOException("saveProperties is no longer supported for security reasons");
    }

    /**
     * Starts the jmx agent
     */
    public void start() {
        checkDebug();

        this.agentConfig.validate();

        if (mBeanServer == null) {
            mBeanServer = MBeanUtil.start();
        }

        try {
            startHttpAdaptor();
        } catch (StartupException e) {
            AlertAppender.getInstance().shuttingDown();
            LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);
            LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
            throw e;
        }

        try {
            startRMIConnectorServer();
        } catch (StartupException e) {
            stopHttpAdaptor();
            AlertAppender.getInstance().shuttingDown();
            LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);
            LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
            throw e;
        }

        try {
            startSnmpAdaptor();
        } catch (StartupException e) {
            stopRMIConnectorServer();
            stopHttpAdaptor();
            AlertAppender.getInstance().shuttingDown();
            LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);
            LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
            throw e;
        }

        if (this.agentConfig.getAutoConnect()) {
            try {
                connectToSystem();
                /*
                 * Call Agent.stop() if connectToSystem() fails. This should clean up agent-DS connection &
                 * stop all the HTTP/RMI/SNMP adapters started earlier.
                 */
            } catch (AdminException ex) {
                logger.error(LocalizedMessage.create(LocalizedStrings.AgentImpl_AUTO_CONNECT_FAILED__0,
                        ex.getMessage()));
                this.stop();
                throw new StartupException(ex);
            } catch (MalformedObjectNameException ex) {
                StringId autoConnectFailed = LocalizedStrings.AgentImpl_AUTO_CONNECT_FAILED__0;
                logger.error(LocalizedMessage.create(autoConnectFailed, ex.getMessage()));
                this.stop();
                throw new StartupException(new AdminException(
                        autoConnectFailed.toLocalizedString(new Object[] { ex.getMessage() }), ex));
            }
        } // getAutoConnect

        logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_GEMFIRE_JMX_AGENT_IS_RUNNING));
        LogWriterAppenders.startupComplete(LogWriterAppenders.Identifier.MAIN);

        if (memberInfoWithStatsMBean == null) {
            initializeHelperMbean();
        }
    }

    /**
     * Deregisters everything this Agent registered and releases the MBeanServer.
     */
    public void stop() {
        try {
            logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_STOPPING_JMX_AGENT));
            AlertAppender.getInstance().shuttingDown();
            LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);

            // stop the GemFire Distributed System
            stopDistributedSystem();

            // stop all JMX Adaptors and Connectors...
            stopHttpAdaptor();
            stopRMIConnectorServer();
            memberInfoWithStatsMBean = null;
            stopSnmpAdaptor();

            // release the MBeanServer for cleanup...
            MBeanUtil.stop();
            mBeanServer = null;

            // remove the register shutdown hook which disconnects the Agent from the Distributed System
            // upon JVM shutdown
            removeShutdownHook();

            logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_AGENT_HAS_STOPPED));
        } finally {
            LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
            LoggingThreadGroup.cleanUpThreadGroups(); // bug35388 - logwriters accumulate, causing mem
                                                      // leak
        }

    }

    private void stopDistributedSystem() {
        // disconnect from the distributed system...
        try {
            disconnectFromSystem();
        } catch (Exception e) {
            // disconnectFromSystem already prints any Exceptions
        } catch (VirtualMachineError err) {
            SystemFailure.initiateFailure(err);
            throw err;
        } catch (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();
        }
    }

    public ObjectName manageDistributedSystem() throws MalformedObjectNameException {
        synchronized (CONN_SYNC) {
            if (isConnected()) {
                return ((AdminDistributedSystemJmxImpl) this.system).getObjectName();
            }
            return null;
        }
    }

    /**
     * Connects to the DistributedSystem currently described by this Agent's attributes for
     * administration and monitoring.
     *
     * @return the object name of the system that the Agent is now connected to
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "This is only a style warning.")
    public ObjectName connectToSystem() throws AdminException, MalformedObjectNameException {
        synchronized (CONN_SYNC) {
            try {
                if (isConnected()) {
                    return ((AdminDistributedSystemJmxImpl) this.system).getObjectName();
                }

                DistributionManager.isDedicatedAdminVM = true;

                AdminDistributedSystemJmxImpl systemJmx = (AdminDistributedSystemJmxImpl) this.system;
                if (systemJmx == null) {
                    systemJmx = (AdminDistributedSystemJmxImpl) createDistributedSystem(this.agentConfig);
                    this.system = systemJmx;
                }
                systemJmx.connect(this.logWriter);

                return new ObjectName(systemJmx.getMBeanName());
            } catch (AdminException e) {
                logger.warn(e.getMessage(), e);
                throw e;
            } catch (RuntimeException e) {
                logger.warn(e.getMessage(), 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 (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();
                logger.error(e.getMessage(), e);
                throw e;
            }
        }
    }

    /**
     * Disconnects from the current DistributedSystem (if connected to one).
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "This is only a style warning.")
    public void disconnectFromSystem() {
        synchronized (CONN_SYNC) {
            try {
                if (this.system == null || !this.system.isConnected()) {
                    return;
                }
                ((AdminDistributedSystemJmxImpl) this.system).disconnect();
                // this.system = null;
            } catch (RuntimeException e) {
                logger.warn(e.getMessage(), 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 (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();
                logger.warn(e.getMessage(), e);
                throw e;
            } finally {
                DistributionManager.isDedicatedAdminVM = false;
            }
        }
    }

    /**
     * Retrieves a displayable snapshot of this Agent's log.
     *
     * @return snapshot of the current log
     */
    public String getLog() {
        String childTail = tailFile(this.logWriterAppender.getChildLogFile());
        String mainTail = tailFile(new File(this.agentConfig.getLogFile()));
        if (childTail == null && mainTail == null) {
            return LocalizedStrings.AgentImpl_NO_LOG_FILE_CONFIGURED_LOG_MESSAGES_WILL_BE_DIRECTED_TO_STDOUT
                    .toLocalizedString();
        } else {
            StringBuffer result = new StringBuffer();
            if (mainTail != null) {
                result.append(mainTail);
            }
            if (childTail != null) {
                result.append("\n" + LocalizedStrings.AgentImpl_TAIL_OF_CHILD_LOG.toLocalizedString() + "\n");
                result.append(childTail);
            }
            return result.toString();
        }
    }

    /**
     * Retrieves display-friendly GemFire version information.
     */
    public String getVersion() {
        return GemFireVersion.asString();
    }

    // -------------------------------------------------------------------------
    // Public attribute accessors/mutators
    // -------------------------------------------------------------------------

    /** Returns true if this Agent is currently connected to a system. */
    public boolean isConnected() {
        boolean result = false;
        synchronized (CONN_SYNC) {
            result = ((this.system != null) && this.system.isConnected());
        }
        return result;
    }

    /**
     * Gets the agent's property file. This is the file it will use when saving its configuration. It
     * was also used when the agent started to initialize its configuration.
     * 
     * @return the agent's property file
     */
    public String getPropertyFile() {
        return this.propertyFile;
    }

    /**
     * Sets the agent's property file.
     *
     * @param value the name of the file to save the agent properties in.
     * @throws IllegalArgumentException if the specified file is a directory.
     * @throws IllegalArgumentException if the specified file's parent is not an existing directory.
     */
    public void setPropertyFile(String value) {
        File f = (new File(value)).getAbsoluteFile();
        if (f.isDirectory()) {
            throw new IllegalArgumentException(
                    LocalizedStrings.AgentImpl_THE_FILE_0_IS_A_DIRECTORY.toLocalizedString(f));
        }
        File parent = f.getParentFile();
        if (parent != null) {
            if (!parent.isDirectory()) {
                throw new IllegalArgumentException(
                        LocalizedStrings.AgentImpl_THE_DIRECTORY_0_DOES_NOT_EXIST.toLocalizedString(parent));
            }
        }
        this.propertyFile = f.getPath();
    }

    /**
     * Gets the mcastAddress of the distributed system that this Agent is managing.
     *
     * @return The mcastAddress value
     */
    public String getMcastAddress() {
        return this.agentConfig.getMcastAddress();
    }

    /**
     * Sets the mcastAddress of the distributed system that this Agent is managing.
     *
     * @param mcastAddress The new mcastAddress value
     */
    public void setMcastAddress(String mcastAddress) {
        this.agentConfig.setMcastAddress(mcastAddress);
    }

    /**
     * Gets the mcastPort of the distributed system that this Agent is managing.
     *
     * @return The mcastPort value
     */
    public int getMcastPort() {
        return this.agentConfig.getMcastPort();
    }

    /**
     * Sets the mcastPort of the distributed system that this Agent is managing.
     *
     * @param mcastPort The new mcastPort value
     */
    public void setMcastPort(int mcastPort) {
        this.agentConfig.setMcastPort(mcastPort);
    }

    /**
     * Gets the locators of the distributed system that this Agent is managing.
     * <p>
     * Format is a comma-delimited list of "host[port]" entries.
     *
     * @return The locators value
     */
    public String getLocators() {
        return this.agentConfig.getLocators();
    }

    /**
     * Sets the locators of the distributed system that this Agent is managing.
     * <p>
     * Format is a comma-delimited list of "host[port]" entries.
     *
     * @param locators The new locators value
     */
    public void setLocators(String locators) {
        this.agentConfig.setLocators(locators);
    }

    /**
     * Gets the membership UDP port range in the distributed system that this Agent is monitoring.
     * <p>
     * This range is given as two numbers separated by a minus sign like "min-max"
     *
     * @return membership UDP port range
     */
    public String getMembershipPortRange() {
        return this.agentConfig.getMembershipPortRange();
    }

    /**
     * Sets the membership UDP port range in the distributed system that this Agent is monitoring.
     * <p>
     * This range is given as two numbers separated by a minus sign like "min-max"
     *
     * @param membershipPortRange membership UDP port range
     */
    public void setMembershipPortRange(String membershipPortRange) {
        this.agentConfig.setMembershipPortRange(membershipPortRange);
    }

    /**
     * Gets the bindAddress of the distributed system that this Agent is managing.
     *
     * @return The bindAddress value
     */
    public String getBindAddress() {
        return this.agentConfig.getBindAddress();
    }

    /**
     * Sets the bindAddress of the distributed system that this Agent is managing.
     *
     * @param bindAddress The new bindAddress value
     */
    public void setBindAddress(String bindAddress) {
        this.agentConfig.setBindAddress(bindAddress);
    }

    /**
     * Retrieves the command that the DistributedSystem will use to perform remote manipulation of
     * config files and log files.
     *
     * @return the remote command for DistributedSystem
     */
    public String getRemoteCommand() {
        return this.agentConfig.getRemoteCommand();
    }

    /**
     * Sets the command that the DistributedSystem will use to perform remote manipulation of config
     * files and log files.
     *
     * @param remoteCommand the remote command for DistributedSystem
     */
    public void setRemoteCommand(String remoteCommand) {
        this.agentConfig.setRemoteCommand(remoteCommand);
    }

    /** Returns the system identity for the DistributedSystem */
    public String getSystemId() {
        return this.agentConfig.getSystemId();
    }

    /** Sets the system identity for the DistributedSystem */
    public void setSystemId(String systemId) {
        this.agentConfig.setSystemId(systemId);
    }

    /**
     * Gets the logFileSizeLimit in megabytes of this Agent. Zero indicates no limit.
     *
     * @return The logFileSizeLimit value
     */
    public int getLogFileSizeLimit() {
        return this.agentConfig.getLogFileSizeLimit();
    }

    /**
     * Sets the logFileSizeLimit in megabytes of this Agent. Zero indicates no limit.
     *
     * @param logFileSizeLimit The new logFileSizeLimit value
     */
    public void setLogFileSizeLimit(int logFileSizeLimit) {
        this.agentConfig.setLogFileSizeLimit(logFileSizeLimit);
        LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
    }

    /**
     * Gets the logDiskSpaceLimit in megabytes of this Agent. Zero indicates no limit.
     *
     * @return The logDiskSpaceLimit value
     */
    public int getLogDiskSpaceLimit() {
        return this.agentConfig.getLogDiskSpaceLimit();
    }

    /**
     * Sets the logDiskSpaceLimit in megabytes of this Agent. Zero indicates no limit.
     *
     * @param logDiskSpaceLimit The new logDiskSpaceLimit value
     */
    public void setLogDiskSpaceLimit(int logDiskSpaceLimit) {
        this.agentConfig.setLogDiskSpaceLimit(logDiskSpaceLimit);
        LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
    }

    /**
     * Gets the logFile name for this Agent to log to.
     *
     * @return The logFile value
     */
    public String getLogFile() {
        return this.agentConfig.getLogFile();
    }

    /**
     * Sets the logFile name for this Agent to log to.
     *
     * @param logFile The new logFile value
     */
    public void setLogFile(String logFile) {
        this.agentConfig.setLogFile(logFile);
        LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
    }

    /**
     * Gets the logLevel of this Agent.
     *
     * @return The logLevel value
     */
    public String getLogLevel() {
        return this.agentConfig.getLogLevel();
    }

    /**
     * Sets the logLevel of this Agent.
     *
     * @param logLevel The new logLevel value
     */
    public void setLogLevel(String logLevel) {
        this.agentConfig.setLogLevel(logLevel);
        LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
    }

    /** Returns true if the Agent is set to auto connect to a system. */
    public boolean getAutoConnect() {
        return this.agentConfig.getAutoConnect();
    }

    /** Returns true if the Agent is set to auto connect to a system. */
    public boolean isAutoConnect() {
        return this.agentConfig.getAutoConnect();
    }

    /** Sets or unsets the option to auto connect to a system. */
    public void setAutoConnect(boolean v) {
        this.agentConfig.setAutoConnect(v);
    }

    /**
     * Returns the address (URL) on which the RMI connector server runs or <code>null</code> if the
     * RMI connector server has not been started. This method is used primarily for testing purposes.
     *
     * @see JMXConnectorServer#getAddress()
     */
    public JMXServiceURL getRMIAddress() {
        if (this.rmiConnector != null) {
            return this.rmiConnector.getAddress();

        } else {
            return null;
        }
    }

    /**
     * Gets the configuration for this Agent.
     *
     * @return the configuration for this Agent
     */
    protected AgentConfig getAgentConfig() {
        return this.agentConfig;
    }

    // -------------------------------------------------------------------------
    // Internal implementation methods
    // -------------------------------------------------------------------------

    /** Returns the tail of the system log specified by <code>File</code>. */
    private String tailFile(File f) {
        try {
            return TailLogResponse.tailSystemLog(f);
        } catch (IOException ex) {
            return LocalizedStrings.AgentImpl_COULD_NOT_TAIL_0_BECAUSE_1.toLocalizedString(new Object[] { f, ex });
        }
    }

    /**
     * Returns the active MBeanServer which has any GemFire MBeans registered.
     *
     * @return the GemFire mbeanServer
     */
    public MBeanServer getMBeanServer() {
        return mBeanServer;
    }

    // /**
    // * Returns the active modeler Registry which has been initialized with all
    // * the ModelMBean descriptors needed for GemFire MBeans.
    // *
    // * @return the modeler registry
    // */
    // private Registry getRegistry() {
    // return MBeanUtil.getRegistry();
    // }

    /**
     * Gets the current instance of LogWriter for logging
     *
     * @return the logWriter
     */
    public LogWriter getLogWriter() {
        return this.logWriter;
    }

    private final Thread shutdownHook = new Thread(LoggingThreadGroup.createThreadGroup("Shutdown"), "Shutdown") {
        @Override
        public void run() {
            disconnectFromSystem();
        }
    };

    /**
     * Adds a ShutdownHook to the Agent for cleaning up any resources
     */
    private void addShutdownHook() {
        if (!Boolean.getBoolean(
                org.apache.geode.distributed.internal.InternalDistributedSystem.DISABLE_SHUTDOWN_HOOK_PROPERTY)) {
            Runtime.getRuntime().addShutdownHook(shutdownHook);
        }
    }

    private void removeShutdownHook() {
        if (!Boolean.getBoolean(
                org.apache.geode.distributed.internal.InternalDistributedSystem.DISABLE_SHUTDOWN_HOOK_PROPERTY)) {
            Runtime.getRuntime().removeShutdownHook(shutdownHook);
        }
    }

    /**
     * Creates a LogWriterI18n for this Agent to use in logging.
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification = "Return value for file delete is not important here.")
    private void initLogWriter() throws org.apache.geode.admin.AdminException {
        final LogConfig logConfig = this.agentConfig.createLogConfig();

        // LOG: create logWriterAppender here
        this.logWriterAppender = LogWriterAppenders.getOrCreateAppender(LogWriterAppenders.Identifier.MAIN, false,
                logConfig, false);

        // LOG: look in AgentConfigImpl for existing LogWriter to use
        InternalLogWriter existingLogWriter = this.agentConfig.getInternalLogWriter();
        if (existingLogWriter != null) {
            this.logWriter = existingLogWriter;
        } else {
            // LOG: create LogWriterLogger
            this.logWriter = LogWriterFactory.createLogWriterLogger(false, false, logConfig, false);
            // LOG: changed statement from config to info
            this.logWriter.info(Banner.getString(null));
            // Set this log writer in AgentConfigImpl
            this.agentConfig.setInternalLogWriter(this.logWriter);
        }

        // LOG: create logWriter here
        this.logWriter = LogWriterFactory.createLogWriterLogger(false, false, logConfig, false);

        // Set this log writer in AgentConfig
        this.agentConfig.setInternalLogWriter(this.logWriter);

        // Print Banner information
        logger.info(Banner.getString(this.agentConfig.getOriginalArgs()));

        // LOG:CONFIG: changed next three statements from config to info
        logger.info(LogMarker.CONFIG, LocalizedStrings.AgentImpl_AGENT_CONFIG_PROPERTY_FILE_NAME_0
                .toLocalizedString(AgentConfigImpl.retrievePropertyFile()));
        logger.info(LogMarker.CONFIG, this.agentConfig.getPropertyFileDescription());
        logger.info(LogMarker.CONFIG, this.agentConfig.toPropertiesAsString());
    }

    /**
     * Stops the HttpAdaptor and its XsltProcessor. Unregisters the associated MBeans.
     */
    private void stopHttpAdaptor() {
        if (!this.agentConfig.isHttpEnabled())
            return;

        // stop the adaptor...
        try {
            this.httpAdaptor.stop();
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }

        try {
            MBeanUtil.unregisterMBean(getHttpAdaptorName());
            MBeanUtil.unregisterMBean(getXsltProcessorName());
        } catch (MalformedObjectNameException e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /** Stops the RMIConnectorServer and unregisters its MBean. */
    private void stopRMIConnectorServer() {
        if (!this.agentConfig.isRmiEnabled())
            return;

        // stop the RMI Connector server...
        try {
            this.rmiConnector.stop();
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }

        try {
            ObjectName rmiRegistryNamingName = getRMIRegistryNamingName();
            if (this.agentConfig.isRmiRegistryEnabled() && mBeanServer.isRegistered(rmiRegistryNamingName)) {
                String[] empty = new String[0];
                mBeanServer.invoke(rmiRegistryNamingName, "stop", empty, empty);
                MBeanUtil.unregisterMBean(rmiRegistryNamingName);
            }
        } catch (MalformedObjectNameException e) {
            logger.warn(e.getMessage(), e);
        } catch (InstanceNotFoundException e) {
            logger.warn(e.getMessage(), e);
        } catch (ReflectionException e) {
            logger.warn(e.getMessage(), e);
        } catch (MBeanException e) {
            logger.warn(e.getMessage(), e);
        }

        try {
            ObjectName rmiConnectorServerName = getRMIConnectorServerName();
            if (mBeanServer.isRegistered(rmiConnectorServerName)) {
                MBeanUtil.unregisterMBean(rmiConnectorServerName);
            }
        } catch (MalformedObjectNameException e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /** Stops the SnmpAdaptor and unregisters its MBean. */
    private void stopSnmpAdaptor() {
        if (!this.agentConfig.isSnmpEnabled())
            return;

        // stop the SnmpAdaptor...
        try {
            getMBeanServer().invoke(getSnmpAdaptorName(), "unbind", new Object[0], new String[0]);
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }

        try {
            MBeanUtil.unregisterMBean(getSnmpAdaptorName());
        } catch (MalformedObjectNameException e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /** Returns the JMX ObjectName for the RMI registry Naming MBean. */
    private ObjectName getRMIRegistryNamingName() throws javax.management.MalformedObjectNameException {
        return ObjectName.getInstance("naming:type=rmiregistry");
    }

    /** Returns the JMX ObjectName for the HttpAdaptor. */
    private ObjectName getHttpAdaptorName() throws javax.management.MalformedObjectNameException {
        return new ObjectName("Server:name=HttpAdaptor");
    }

    /** Returns the JMX ObjectName for the RMIConnectorServer. */
    private ObjectName getRMIConnectorServerName() throws javax.management.MalformedObjectNameException {
        return new ObjectName("connectors:protocol=rmi");
    }

    /** Returns the JMX ObjectName for the SnmpAdaptor. */
    private ObjectName getSnmpAdaptorName() throws javax.management.MalformedObjectNameException {
        return new ObjectName("Adaptors:protocol=SNMP");
    }

    /** Returns the JMX ObjectName for the HttpAdaptor's XsltProcessor. */
    private ObjectName getXsltProcessorName() throws javax.management.MalformedObjectNameException {
        return new ObjectName("Server:name=XSLTProcessor");
    }

    // -------------------------------------------------------------------------
    // Factory method for creating DistributedSystem
    // -------------------------------------------------------------------------

    /**
     * Creates and connects to a <code>DistributedSystem</code>.
     *
     * @param config
     */
    private AdminDistributedSystem createDistributedSystem(AgentConfigImpl config)
            throws org.apache.geode.admin.AdminException {
        return new AdminDistributedSystemJmxImpl(config);
    }

    // -------------------------------------------------------------------------
    // Agent main
    // -------------------------------------------------------------------------

    /**
     * Command-line main for running the GemFire Management Agent.
     * <p>
     * Accepts command-line arguments matching the options in {@link AgentConfig} and
     * {@link org.apache.geode.admin.DistributedSystemConfig}.
     * <p>
     * <code>AgentConfig</code> will convert -Jarguments to System properties.
     */
    public static void main(String[] args) {
        SystemFailure.loadEmergencyClasses();

        AgentConfigImpl ac;
        try {
            ac = new AgentConfigImpl(args);
        } catch (RuntimeException ex) {
            System.err.println(LocalizedStrings.AgentImpl_FAILED_READING_CONFIGURATION_0.toLocalizedString(ex));
            System.exit(1);
            return;
        }

        try {
            Agent agent = AgentFactory.getAgent(ac);
            agent.start();

        } 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 (Throwable t) {
            // 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();
            t.printStackTrace();
            System.exit(1);
        }
    }

    // -------------------------------------------------------------------------
    // MX4J Connectors/Adaptors
    // -------------------------------------------------------------------------

    private void createRMIRegistry() throws Exception {
        if (!this.agentConfig.isRmiRegistryEnabled()) {
            return;
        }
        MBeanServer mbs = getMBeanServer();
        String host = this.agentConfig.getRmiBindAddress();
        int port = this.agentConfig.getRmiPort();

        /*
         * Register and start the rmi-registry naming MBean, which is needed by JSR 160
         * RMIConnectorServer
         */
        ObjectName registryName = getRMIRegistryNamingName();
        try {
            RMIRegistryService registryNamingService = null;
            if (host != null && !("".equals(host.trim()))) {
                registryNamingService = new RMIRegistryService(host, port);
            } else {
                registryNamingService = new RMIRegistryService(port);
            }
            mbs.registerMBean(registryNamingService, registryName);
        } catch (javax.management.InstanceAlreadyExistsException e) {
            logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_0__IS_ALREADY_REGISTERED, registryName));
        }
        mbs.invoke(registryName, "start", null, null);
    }

    /**
     * Defines and starts the JMX RMIConnector and service.
     * <p>
     * If {@link AgentConfig#isRmiEnabled} returns false, then this adaptor will not be started.
     */
    private void startRMIConnectorServer() {
        if (!this.agentConfig.isRmiEnabled())
            return;

        String rmiBindAddress = this.agentConfig.getRmiBindAddress();

        // Set RMI Stubs to use the given RMI Bind Address
        // Default bindAddress is "", if none is set - ignore if not set
        // If java.rmi.server.hostname property is specified then
        // that override is not changed
        String rmiStubServerNameKey = "java.rmi.server.hostname";
        String overrideHostName = System.getProperty(rmiStubServerNameKey);
        if ((overrideHostName == null || overrideHostName.trim().length() == 0)
                && (rmiBindAddress != null && rmiBindAddress.trim().length() != 0)) {
            System.setProperty(rmiStubServerNameKey, rmiBindAddress);
            logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_SETTING_0,
                    new StringBuilder(rmiStubServerNameKey).append(" = ").append(rmiBindAddress)));
        }

        try {
            createRMIRegistry();
            ObjectName objName = getRMIConnectorServerName();

            // make sure this adaptor is not already registered...
            if (getMBeanServer().isRegistered(objName)) {
                // dunno how we got here...
                logger.info(LocalizedMessage
                        .create(LocalizedStrings.AgentImpl_RMICONNECTORSERVER_ALREADY_REGISTERED_AS__0, objName));
                return;
            }

            /*
             * url defined as: service:jmx:protocol:sap where 1. protocol: rmi 2. sap is:
             * [host[:port]][url-path] where host: rmi-binding-address port: rmi-server-port url-path:
             * /jndi/rmi://<rmi-binding-address>:<rmi-port><JNDI_NAME>
             */
            String urlString = null;
            String connectorServerHost = "";
            int connectorServerPort = this.agentConfig.getRmiServerPort();
            String rmiRegistryHost = "";
            int rmiRegistryPort = this.agentConfig.getRmiPort();

            // Set registryHost to localhost if not specified
            // RMI stubs would use a default IP if namingHost is left empty
            if (rmiBindAddress == null || rmiBindAddress.trim().length() == 0) {
                connectorServerHost = "localhost";
                rmiRegistryHost = "";
            } else {
                connectorServerHost = applyRFC2732(rmiBindAddress);
                rmiRegistryHost = connectorServerHost;
            }

            urlString = MessageFormat.format(AgentImpl.JMX_SERVICE_URL, connectorServerHost,
                    String.valueOf(connectorServerPort), rmiRegistryHost, String.valueOf(rmiRegistryPort),
                    JNDI_NAME);

            logger.debug("JMX Service URL string is : \"{}\"", urlString);

            // The address of the connector
            JMXServiceURL url = new JMXServiceURL(urlString);

            Map<String, Object> env = new HashMap<String, Object>();
            // env.put(Context.INITIAL_CONTEXT_FACTORY,
            // "com.sun.jndi.rmi.registry.RegistryContextFactory");
            // env.put(Context.PROVIDER_URL, "rmi://localhost:1099");

            RMIServerSocketFactory ssf = new MX4JServerSocketFactory(this.agentConfig.isAgentSSLEnabled(), // true,
                    this.agentConfig.isAgentSSLRequireAuth(), // true,
                    this.agentConfig.getAgentSSLProtocols(), // "any",
                    this.agentConfig.getAgentSSLCiphers(), // "any",
                    this.agentConfig.getRmiBindAddress(), 10, // backlog
                    this.agentConfig.getGfSecurityProperties());
            env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);

            if (this.agentConfig.isAgentSSLEnabled()) {
                RMIClientSocketFactory csf = new SslRMIClientSocketFactory();
                env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
            }

            MBeanServer mbs = null; // will be set by registering w/ mbeanServer
            this.rmiConnector = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);

            // for cleanup
            this.rmiConnector.addNotificationListener(new ConnectionNotificationAdapter(),
                    new ConnectionNotificationFilterImpl(), this);

            // Register the JMXConnectorServer in the MBeanServer
            getMBeanServer().registerMBean(this.rmiConnector, objName);

            // Start the JMXConnectorServer
            this.rmiConnector.start();
        } 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 (Throwable t) {
            // 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();
            logger.error(LocalizedStrings.AgentImpl_FAILED_TO_START_RMICONNECTORSERVER, t);
            throw new StartupException(LocalizedStrings.AgentImpl_FAILED_TO_START_RMI_SERVICE.toLocalizedString(),
                    t);
        }
    }

    /**
     * Starts the optional third-party AdventNet SNMP Adaptor.
     * <p>
     * If {@link AgentConfig#isSnmpEnabled} returns false, then this adaptor will not be started.
     */
    private void startSnmpAdaptor() {
        if (!this.agentConfig.isSnmpEnabled())
            return;
        try {
            ObjectName objName = getSnmpAdaptorName();

            // make sure this adaptor is not already registered...
            if (getMBeanServer().isRegistered(objName)) {
                // dunno how we got here...
                logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_SNMPADAPTOR_ALREADY_REGISTERED_AS__0,
                        objName));
                return;
            }

            String className = "com.adventnet.adaptors.snmp.snmpsupport.SmartSnmpAdaptor";
            String snmpDir = this.agentConfig.getSnmpDirectory();
            // ex:/merry2/users/klund/agent

            // validate the directory...
            if (snmpDir == null || snmpDir.length() == 0) {
                throw new IllegalArgumentException(
                        LocalizedStrings.AgentImpl_SNMPDIRECTORY_MUST_BE_SPECIFIED_BECAUSE_SNMP_IS_ENABLED
                                .toLocalizedString());
            }
            File root = new File(snmpDir);
            if (!root.exists()) {
                throw new IllegalArgumentException(
                        LocalizedStrings.AgentImpl_SNMPDIRECTORY_DOES_NOT_EXIST.toLocalizedString());
            }

            // create the adaptor...
            String[] sigs = new String[] { "java.lang.String" };
            Object[] args = new Object[] { snmpDir };

            String bindAddress = this.agentConfig.getSnmpBindAddress();
            if (bindAddress != null && bindAddress.length() > 0) {
                sigs = new String[] { "java.lang.String", sigs[0] };
                args = new Object[] { bindAddress, args[0] };
            }

            // go...
            getMBeanServer().createMBean(className, objName, args, sigs);
        } 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 (Throwable t) {
            // 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();
            logger.error(LocalizedMessage.create(LocalizedStrings.AgentImpl_FAILED_TO_START_SNMPADAPTOR__0,
                    t.getMessage()));
            throw new StartupException(
                    LocalizedStrings.AgentImpl_FAILED_TO_START_SNMPADAPTOR__0.toLocalizedString(t.getMessage()), t);
        }
    }

    /**
     * Defines and starts the JMX Http Adaptor service from MX4J.
     * <p>
     * If {@link AgentConfig#isHttpEnabled} returns false, then this adaptor will not be started.
     */
    private void startHttpAdaptor() {
        if (!this.agentConfig.isHttpEnabled())
            return;
        try {
            ObjectName objName = getHttpAdaptorName();

            // make sure this adaptor is not already registered...
            if (getMBeanServer().isRegistered(objName)) {
                // dunno how we got here...
                logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_HTTPADAPTOR_ALREADY_REGISTERED_AS__0,
                        objName));
                return;
            }

            this.httpAdaptor = new HttpAdaptor();

            // validate and set host and port values...
            if (this.agentConfig.getHttpPort() > 0) {
                this.httpAdaptor.setPort(this.agentConfig.getHttpPort());
                logger.info(LogMarker.CONFIG,
                        LocalizedMessage.create(LocalizedStrings.AgentImpl_HTTP_ADAPTOR_LISTENING_ON_PORT__0,
                                this.agentConfig.getHttpPort()));
            } else {
                logger.error(LocalizedMessage.create(LocalizedStrings.AgentImpl_INCORRECT_PORT_VALUE__0,
                        this.agentConfig.getHttpPort()));
            }

            if (this.agentConfig.getHttpBindAddress() != null) {
                String host = this.agentConfig.getHttpBindAddress();
                logger.info(LogMarker.CONFIG, LocalizedMessage
                        .create(LocalizedStrings.AgentImpl_HTTP_ADAPTOR_LISTENING_ON_ADDRESS__0, host));
                this.httpAdaptor.setHost(host);
            } else {
                logger.error(LocalizedMessage.create(LocalizedStrings.AgentImpl_INCORRECT_NULL_HOSTNAME));
            }

            // SSL support...
            MX4JServerSocketFactory socketFactory = new MX4JServerSocketFactory(
                    this.agentConfig.isAgentSSLEnabled(), this.agentConfig.isHttpSSLRequireAuth(),
                    this.agentConfig.getAgentSSLProtocols(), this.agentConfig.getAgentSSLCiphers(),
                    this.agentConfig.getGfSecurityProperties());
            this.httpAdaptor.setSocketFactory(socketFactory);

            // authentication (user login) support...
            if (this.agentConfig.isHttpAuthEnabled()) {
                // this pops up a login dialog from the browser...
                this.httpAdaptor.setAuthenticationMethod(MX4J_HTTPADAPTOR_BASIC_AUTHENTICATION); // only
                                                                                                 // basic
                                                                                                 // works

                this.httpAdaptor.addAuthorization(this.agentConfig.getHttpAuthUser(),
                        this.agentConfig.getHttpAuthPassword());
            }

            // add the XsltProcessor...
            this.httpAdaptor.setProcessorName(createXsltProcessor());

            // register the HttpAdaptor and snap on the XsltProcessor...
            getMBeanServer().registerMBean(this.httpAdaptor, objName);
            this.httpAdaptor.start();
        } 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 (Throwable t) {
            // 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();
            logger.error(LocalizedMessage.create(LocalizedStrings.AgentImpl_FAILED_TO_START_HTTPADAPTOR__0,
                    t.getMessage()));
            throw new StartupException(
                    LocalizedStrings.AgentImpl_FAILED_TO_START_HTTPADAPTOR__0.toLocalizedString(t.getMessage()), t);
        }
    }

    /**
     * Defines and starts the Xslt Processor helper service for the Http Adaptor.
     */
    private ObjectName createXsltProcessor() throws javax.management.JMException {
        ObjectName objName = getXsltProcessorName();

        // make sure this mbean is not already registered...
        if (getMBeanServer().isRegistered(objName)) {
            // dunno how we got here...
            logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_XSLTPROCESSOR_ALREADY_REGISTERED_AS__0,
                    objName));
            return objName;
        }

        getMBeanServer().registerMBean(new mx4j.tools.adaptor.http.XSLTProcessor(), objName);
        return objName;
    }

    // -------------------------------------------------------------------------
    // Private support methods...
    // -------------------------------------------------------------------------

    // /** Not used anymore but seems moderately useful... */
    // private String[] parseSSLCiphers(String ciphers) {
    // List list = new ArrayList();
    // StringTokenizer st = new StringTokenizer(ciphers);
    // while (st.hasMoreTokens()) {
    // list.add(st.nextToken());
    // }
    // return (String[]) list.toArray(new String[list.size()]);
    // }

    // -------------------------------------------------------------------------
    // SSL configuration for GemFire
    // -------------------------------------------------------------------------
    public boolean isSSLEnabled() {
        return this.agentConfig.isSSLEnabled();
    }

    public void setSSLEnabled(boolean enabled) {
        this.agentConfig.setSSLEnabled(enabled);
    }

    public String getSSLProtocols() {
        return this.agentConfig.getSSLProtocols();
    }

    public void setSSLProtocols(String protocols) {
        this.agentConfig.setSSLProtocols(protocols);
    }

    public String getSSLCiphers() {
        return this.agentConfig.getSSLCiphers();
    }

    public void setSSLCiphers(String ciphers) {
        this.agentConfig.setSSLCiphers(ciphers);
    }

    public boolean isSSLAuthenticationRequired() {
        return this.agentConfig.isSSLAuthenticationRequired();
    }

    public void setSSLAuthenticationRequired(boolean authRequired) {
        this.agentConfig.setSSLAuthenticationRequired(authRequired);
    }

    public Properties getSSLProperties() {
        return this.agentConfig.getSSLProperties();
    }

    public void setSSLProperties(Properties sslProperties) {
        this.agentConfig.setSSLProperties(sslProperties);
    }

    public void addSSLProperty(String key, String value) {
        this.agentConfig.addSSLProperty(key, value);
    }

    public void removeSSLProperty(String key) {
        this.agentConfig.removeSSLProperty(key);
    }

    // -------------------------------------------------------------------------
    // ManagedResource implementation
    // -------------------------------------------------------------------------

    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.AGENT;
    }

    public void cleanupResource() {
    }

    static class StartupException extends GemFireException {
        private static final long serialVersionUID = 6614145962199330348L;

        StartupException(Throwable cause) {
            super(cause);
        }

        StartupException(String reason, Throwable cause) {
            super(reason, cause);
        }
    }

    // -------------------------------------------------------------------------
    // Other Support methods
    // -------------------------------------------------------------------------
    /**
     * Checks the no. of active RMI clients and updates a flag in the Admin Distributed System.
     *
     * @see AdminDistributedSystemJmxImpl#setRmiClientCountZero(boolean)
     * @since GemFire 6.0
     */
    void updateRmiClientsCount() {
        int noOfClientsConnected = 0;

        String[] connectionIds = this.rmiConnector.getConnectionIds();

        if (connectionIds != null) {
            noOfClientsConnected = connectionIds.length;
        }

        logger.info("No. of RMI clients connected :: {}", noOfClientsConnected);

        AdminDistributedSystemJmxImpl adminDSJmx = (AdminDistributedSystemJmxImpl) this.system;

        adminDSJmx.setRmiClientCountZero(noOfClientsConnected == 0);
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("AgentImpl[");
        sb.append("config=" + agentConfig.toProperties().toString());
        // sb.append("; adaptor=" + httpAdaptor.toString());
        sb.append("; mbeanName=" + mbeanName);
        sb.append("; modelMBean=" + modelMBean);
        sb.append("; objectName=" + objectName);
        sb.append("; propertyFile=" + propertyFile);
        sb.append(": rmiConnector=" + rmiConnector);
        // sb.append("; system=" + system);)
        sb.append("]");
        return sb.toString();
    }

    /**
     * Process the String form of a hostname to make it comply with Jmx URL restrictions. Namely wrap
     * IPv6 literal address with "[", "]"
     * 
     * @param hostname the name to safeguard.
     * @return a string representation suitable for use in a Jmx connection URL
     */
    private static String applyRFC2732(String hostname) {
        if (hostname.indexOf(":") != -1) {
            // Assuming an IPv6 literal because of the ':'
            return "[" + hostname + "]";
        }
        return hostname;
    }
}

/**
 * Adapter class for NotificationListener that listens to notifications of type
 * javax.management.remote.JMXConnectionNotification
 *
 * @since GemFire 6.0
 */
class ConnectionNotificationAdapter implements NotificationListener {
    private static final Logger logger = LogService.getLogger();

    /**
     * If the handback object passed is an AgentImpl, updates the JMX client count
     *
     * @param notification JMXConnectionNotification for change in client connection status
     * @param handback An opaque object which helps the listener to associate information regarding
     *        the MBean emitter. This object is passed to the MBean during the addListener call and
     *        resent, without modification, to the listener. The MBean object should not use or modify
     *        the object. (NOTE: copied from javax.management.NotificationListener)
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "BC_UNCONFIRMED_CAST", justification = "Only JMXConnectionNotification instances are used.")
    public void handleNotification(Notification notification, Object handback) {
        if (handback instanceof AgentImpl) {
            AgentImpl agent = (AgentImpl) handback;

            JMXConnectionNotification jmxNotifn = (JMXConnectionNotification) notification;

            if (logger.isDebugEnabled()) {
                logger.debug("Connection notification for connection id : '{}'", jmxNotifn.getConnectionId());
            }

            agent.updateRmiClientsCount();
        }
    }
}

/**
 * Filters out the notifications of the type JMXConnectionNotification.OPENED,
 * JMXConnectionNotification.CLOSED and JMXConnectionNotification.FAILED.
 *
 * @since GemFire 6.0
 */
class ConnectionNotificationFilterImpl implements NotificationFilter {

    /**
     * Default serialVersionUID
     */
    private static final long serialVersionUID = 1L;

    /**
     * Invoked before sending the specified notification to the listener. Returns whether the given
     * notification is to be sent to the listener.
     *
     * @param notification The notification to be sent.
     * @return true if the notification has to be sent to the listener, false otherwise.
     */
    public boolean isNotificationEnabled(Notification notification) {
        boolean isThisNotificationEnabled = false;
        if (notification.getType().equals(JMXConnectionNotification.OPENED)
                || notification.getType().equals(JMXConnectionNotification.CLOSED)
                || notification.getType().equals(JMXConnectionNotification.FAILED)) {
            isThisNotificationEnabled = true;
        }
        return isThisNotificationEnabled;
    }
}