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

org.rhq.enterprise.agent.AgentManagement Maven / Gradle / Ivy

The newest version!
/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.agent;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.prefs.Preferences;

import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;

import org.rhq.core.clientapi.descriptor.AgentPluginDescriptorUtil;
import org.rhq.core.clientapi.descriptor.plugin.PluginDescriptor;
import org.rhq.core.clientapi.server.core.CoreServerService;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.PluginContainerConfiguration;
import org.rhq.core.pc.inventory.InventoryManager;
import org.rhq.core.pc.inventory.ResourceContainer;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.enterprise.agent.AgentRestartCounter.AgentRestartReason;
import org.rhq.enterprise.communications.ServiceContainerMetricsMBean;
import org.rhq.enterprise.communications.command.client.ClientCommandSender;
import org.rhq.enterprise.communications.command.client.ClientCommandSenderMetrics;
import org.rhq.enterprise.communications.command.client.ClientRemotePojoFactory;

/**
 * This is the management layer for the agent. This is the MBean that is used to manage the agent itself. It emits the
 * agent's metric data.
 *
 * @author John Mazzitelli
 */
public class AgentManagement implements AgentManagementMBean, MBeanRegistration {
    /**
     * The agent being monitored.
     */
    private AgentMain m_agent;

    /**
     * Where this MBean is registered.
     */
    private MBeanServer m_mbs;

    /**
     * The name this MBean instance is registered under.
     */
    private ObjectName m_objectName;

    /**
     * Constructor for {@link AgentManagement}.
     *
     * @param agent the agent to be monitored
     */
    public AgentManagement(AgentMain agent) {
        m_agent = agent;
    }

    public void updateAgent() {
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Void run() {
                ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
                try {
                    Thread.sleep(5000L); // give our updateAgent() caller a chance to return and finish
                    Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
                    AgentUpdateThread.updateAgentNow(m_agent, false);
                } catch (Exception e) {
                    if (e instanceof RuntimeException) {
                        throw (RuntimeException) e;
                    } else {
                        throw new RuntimeException(e);
                    }
                } finally {
                    Thread.currentThread().setContextClassLoader(originalCL);
                }

                return null;
            }
        });
    }

    public void switchToServer(String server) {
        m_agent.switchToServer(server);
    }

    public void restart() {
        // restarting the agent is a suicidal act - this MBean instance will
        // be unregistered after we shutdown.  Therefore, we must do this in a
        // separate thread so as to allow this method to return successfully
        // first. Therefore, this method must inherently do its thing asynchronously.

        // Another important fact is that if this method is called from the rhq-agent plugin
        // which is co-located in the same JVM, restarting the plugin container actually creates
        // a whole bunch of new classloaders that ALL inherit the access control context of the
        // agent plugin executing thread and classloader. The access control context contains
        // a reference to the agent plugin's classloader through its protection domain and thus,
        // by using the rhq-agent's restart, shutdown or restartPluginContainer operations we
        // create a classloader leak.
        //
        // The old rhq-agent's plugin classloader will never be released and will carry along with
        // it all the classes, slowly contributing to the eventual permgen depletion and OOMEs.
        //
        // To solve this problem, we run the restart, shutdown and restartPluginContainer methods with
        // the access control context of this class. This class is defined in the agent itself and thus
        // its access control context doesn't contain the "baggage" from the plugin classloaders.

        new Thread(new Runnable() {
            public void run() {
                AccessController.doPrivileged(new PrivilegedAction() {
                    @Override
                    public Void run() {
                        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
                        try {
                            Thread.sleep(5000L); // give our restart() caller a chance to return and finish
                            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
                            m_agent.shutdown();
                            m_agent.start();
                            m_agent.getAgentRestartCounter().restartedAgent(AgentRestartReason.OPERATION);
                        } catch (Exception e) {
                            e.printStackTrace(); // TODO what do to here?
                        } finally {
                            Thread.currentThread().setContextClassLoader(originalCL);
                        }

                        return null;
                    }
                });
            }
        }, "RHQ Agent Restart Thread").start();
    }

    public void shutdown() {
        // shutting down the agent is a suicidal act - this MBean instance will
        // be unregistered after we shutdown.  Therefore, we must do this in a
        // separate thread so as to allow this method to return successfully
        // first. Therefore, this method must inherently do its thing asynchronously.

        // see the explanation in the restart() method for why we're running this as
        // a privileged action

        new Thread(new Runnable() {
            public void run() {
                AccessController.doPrivileged(new PrivilegedAction() {
                    @Override
                    public Void run() {
                        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
                        try {
                            Thread.sleep(5000L); // give our shutdown() caller a chance to return and finish
                            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
                            m_agent.shutdown();
                        } catch (InterruptedException e) {
                            // exit the thread
                        } finally {
                            Thread.currentThread().setContextClassLoader(originalCL);
                        }

                        return null;
                    }
                });
            }
        }, "RHQ Agent Shutdown Thread").start();
    }

    public void downloadLatestFailoverList() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            m_agent.performPrimaryServerSwitchoverCheck();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public void updatePlugins() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            m_agent.updatePlugins();
            restartPluginContainer();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public OperationResult retrieveAllPluginInfo() {
        List plugins;
        OperationResult info = new OperationResult();
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            PluginUpdate updater = getPluginUpdateObject();
            plugins = updater.getCurrentPluginFiles();
            PluginContainerConfiguration pcConfig = m_agent.getConfiguration().getPluginContainerConfiguration();
            List enabledPlugins = pcConfig.getEnabledPlugins();
            List disabledPlugins = pcConfig.getDisabledPlugins();

            PropertyList list = new PropertyList("plugins".intern());
            info.getComplexResults().put(list);

            if (plugins.size() > 0) {
                for (File plugin : plugins) {
                    String pluginName;
                    String pluginDisplayName;
                    try {
                        URL url = plugin.toURI().toURL();
                        PluginDescriptor descriptor = AgentPluginDescriptorUtil.loadPluginDescriptorFromUrl(url);
                        pluginName = descriptor.getName();
                        pluginDisplayName = descriptor.getDisplayName();
                    } catch (Exception t) {
                        pluginName = "?cannot-parse-descriptor?".intern();
                        pluginDisplayName = "?cannot-parse-descriptor?".intern();
                    }

                    PropertyMap map = new PropertyMap("plugin".intern());
                    map.put(new PropertySimple(PLUGIN_INFO_NAME, pluginName));
                    map.put(new PropertySimple(PLUGIN_INFO_DISPLAY_NAME, pluginDisplayName));
                    map.put(new PropertySimple(PLUGIN_INFO_PATH, plugin.getAbsoluteFile()));
                    map.put(new PropertySimple(PLUGIN_INFO_TIMESTAMP, new Date(plugin.lastModified())));
                    map.put(new PropertySimple(PLUGIN_INFO_SIZE, plugin.length()));
                    // plugin is either whitelisted or the white list is empty
                    boolean isEnabled = enabledPlugins.isEmpty() || enabledPlugins.contains(pluginName);
                    // ..and is not on the black list
                    isEnabled &= !disabledPlugins.contains(pluginName);
                    map.put(new PropertySimple(PLUGIN_INFO_ENABLED, isEnabled));

                    try {
                        map.put(new PropertySimple(PLUGIN_INFO_MD5, MessageDigestGenerator.getDigestString(plugin)));
                    } catch (IOException e) {
                        map.put(new PropertySimple(PLUGIN_INFO_MD5, e.toString()));
                    }

                    list.add(map);
                }
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
        return info;
    }

    public OperationResult retrievePluginInfo(String pluginName) {
        List plugins;

        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            PluginUpdate updater = getPluginUpdateObject();
            plugins = updater.getCurrentPluginFiles();

            if (plugins.size() > 0) {
                for (File plugin : plugins) {
                    String pluginDisplayName;
                    String pluginNameToReturn;
                    try {
                        URL url = plugin.toURI().toURL();
                        PluginDescriptor pluginDescriptor = AgentPluginDescriptorUtil.loadPluginDescriptorFromUrl(url);
                        pluginDisplayName = pluginDescriptor.getDisplayName();
                        pluginNameToReturn = pluginDescriptor.getName();
                    } catch (Exception t) {
                        continue;
                    }

                    if (pluginNameToReturn.toLowerCase().equals(pluginName.toLowerCase())) {
                        OperationResult opResults = new OperationResult();
                        Configuration info = opResults.getComplexResults();
                        info.put(new PropertySimple(PLUGIN_INFO_NAME, pluginNameToReturn));
                        info.put(new PropertySimple(PLUGIN_INFO_DISPLAY_NAME, pluginDisplayName));
                        info.put(new PropertySimple(PLUGIN_INFO_PATH, plugin.getAbsoluteFile()));
                        info.put(new PropertySimple(PLUGIN_INFO_TIMESTAMP, new Date(plugin.lastModified())));
                        info.put(new PropertySimple(PLUGIN_INFO_SIZE, plugin.length()));
                        try {
                            info.put(new PropertySimple(PLUGIN_INFO_MD5, MessageDigestGenerator.getDigestString(plugin)));
                        } catch (IOException e) {
                            info.put(new PropertySimple(PLUGIN_INFO_MD5, e.toString()));
                        }

                        return opResults;
                    }
                }
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
        throw new IllegalArgumentException("There is no plugin named [" + pluginName + "]");
    }

    public void restartPluginContainer() {
        // see the explanation in the restart() method for why we're running this as
        // a privileged action

        new Thread(new Runnable() {
            public void run() {
                AccessController.doPrivileged(new PrivilegedAction() {
                    @Override
                    public Void run() {
                        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
                        try {
                            Thread.sleep(5000L); // give our caller a chance to return and finish
                            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
                            m_agent.shutdownPluginContainer();
                            m_agent.startPluginContainer(500L);
                        } catch (InterruptedException e) {
                            // exit the thread
                        } finally {
                            Thread.currentThread().setContextClassLoader(originalCL);
                        }

                        return null;
                    }
                });
            }
        }, "RHQ Agent Plugin Container Restart Thread").start();
    }

    public OperationResult executeAvailabilityScan(Boolean changesOnly) {
        boolean changes = (changesOnly != null) ? changesOnly.booleanValue() : false;

        AvailabilityReport report;
        InventoryManager inventoryManager = PluginContainer.getInstance().getInventoryManager();

        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            report = inventoryManager.executeAvailabilityScanImmediately(changes);
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }

        OperationResult opResult = new OperationResult();
        Configuration complexResults = opResult.getComplexResults();

        PropertyList list = new PropertyList("resourceAvailabilities");
        complexResults.put(list);

        String agentName;
        Boolean changesOnlyFromReport;

        if (report != null) {
            agentName = report.getAgentName();
            changesOnlyFromReport = Boolean.valueOf(report.isChangesOnlyReport());

            List avails = report.getResourceAvailability();

            if (avails.size() > 0) {
                for (AvailabilityReport.Datum avail : avails) {
                    boolean isUp = avail.getAvailabilityType() == AvailabilityType.UP;

                    // lookup the heavy-weight resource object
                    int resourceId = avail.getResourceId();
                    ResourceContainer resourceContainer = inventoryManager.getResourceContainer(resourceId);
                    Resource resource = resourceContainer.getResource();

                    PropertyMap map = new PropertyMap("resourceAvailability");
                    map.put(new PropertySimple("resourceId", Integer.valueOf(resource.getId())));
                    map.put(new PropertySimple("resourceName", resource.getName()));
                    map.put(new PropertySimple("isAvailable", Boolean.valueOf(isUp)));
                    list.add(map);
                }
            }
        } else {
            // report was null - this means there are no committed resources in inventory
            agentName = m_agent.getConfiguration().getAgentName();
            changesOnlyFromReport = changesOnly;
        }

        complexResults.put(new PropertySimple("agentName", agentName));
        complexResults.put(new PropertySimple("isChangesOnly", changesOnlyFromReport));

        return opResult;
    }

    public String getVersion() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return Version.getProductVersion();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getCurrentTime() {
        return System.currentTimeMillis();
    }

    public String retrieveCurrentDateTime(String timeZone) {
        DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.FULL);

        if (timeZone == null || timeZone.length() == 0) {
            df.setTimeZone(TimeZone.getDefault());
        } else {
            df.setTimeZone(TimeZone.getTimeZone(timeZone));
        }

        return df.format(new Date());
    }

    public void setDebugMode(Boolean enabled, Boolean traceMessaging) throws ExecutionException {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            if (enabled != null && enabled.booleanValue()) {
                executePromptCommand("debug -f log4j-debug.xml");
                if (traceMessaging != null && traceMessaging.booleanValue()) {
                    executePromptCommand("debug -c true");
                } else {
                    executePromptCommand("debug -c false");
                }
            } else {
                executePromptCommand("debug -f log4j.xml");
                executePromptCommand("debug -c false");
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
        return;
    }

    public String executePromptCommand(final String command) throws ExecutionException {
        // we don't know what command will get executed, so let's proactively run it in the privileged action
        // for why it is a good idea, see the comments in the restart() method

        if (command.startsWith("update ")) {
            throw new ExecutionException(new IllegalArgumentException(
                "Cannot invoke 'update' prompt command - use 'Update Agent' operation instead"));
        }

        try {
            return AccessController.doPrivileged(new PrivilegedExceptionAction() {
                @Override
                public String run() throws Exception {
                    ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
                    try {
                        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
                        CharArrayWriter listener = new CharArrayWriter();
                        AgentPrintWriter apw = m_agent.getOut();
                        try {
                            apw.addListener(listener);
                            m_agent.executePromptCommand(
                                command); // TODO should we do something if false is returned? (i.e. kill agent?)
                        } catch (Exception e) {
                            throw new ExecutionException(listener.toString(),
                                e); // the message is the output, cause is the thrown exception
                        } finally {
                            apw.removeListener(listener);
                        }

                        String output = listener.toString();
                        return output;
                    } finally {
                        Thread.currentThread().setContextClassLoader(originalCL);
                    }
                }
            });
        } catch (PrivilegedActionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof ExecutionException) {
                throw (ExecutionException) cause;
            } else {
                throw new ExecutionException(e);
            }
        }
    }

    public String getAgentHomeDirectory() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return m_agent.getAgentHomeDirectory();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public int getNumberAgentRestarts() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return m_agent.getAgentRestartCounter().getNumberOfRestarts();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public String getReasonForLastRestart() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return m_agent.getAgentRestartCounter().getLastAgentRestartReason().toString();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getAgentServerClockDifference() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return m_agent.getAgentServerClockDifference();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getUptime() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            long start_time = m_agent.getStartTime();

            if (start_time > 0) {
                return (System.currentTimeMillis() - start_time) / 1000L; // we want units in seconds
            }

            return 0L;
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberSuccessfulCommandsReceived() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getServerSideMetrics().getNumberSuccessfulCommandsReceived();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberFailedCommandsReceived() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getServerSideMetrics().getNumberFailedCommandsReceived();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberTotalCommandsReceived() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getServerSideMetrics().getNumberTotalCommandsReceived();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getAverageExecutionTimeReceived() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getServerSideMetrics().getAverageExecutionTimeReceived();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getAverageExecutionTimeSent() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getClientSideMetrics().getAverageExecutionTimeSent();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberSuccessfulCommandsSent() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getClientSideMetrics().getNumberSuccessfulCommandsSent();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberFailedCommandsSent() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getClientSideMetrics().getNumberFailedCommandsSent();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberTotalCommandsSent() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            ClientCommandSenderMetrics metrics = getClientSideMetrics();
            return metrics.getNumberSuccessfulCommandsSent() + metrics.getNumberFailedCommandsSent();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberCommandsActiveSent() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getClientSideMetrics().getNumberCommandsActive();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberCommandsInQueue() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getClientSideMetrics().getNumberCommandsInQueue();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getNumberCommandsSpooled() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            return getClientSideMetrics().getNumberCommandsSpooled();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public boolean isSending() {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            ClientCommandSenderMetrics metrics = getClientSideMetrics();
            return metrics.isSending();
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }
    }

    public long getJVMFreeMemory() {
        return Runtime.getRuntime().freeMemory();
    }

    public long getJVMTotalMemory() {
        return Runtime.getRuntime().totalMemory();
    }

    public int getJVMActiveThreads() {
        return ManagementFactory.getThreadMXBean().getThreadCount();
    }

    public Properties getAgentConfiguration() {
        Properties properties = new Properties();

        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            try {
                Preferences prefs = m_agent.getConfiguration().getPreferences();
                String[] keys = prefs.keys();
                for (String key : keys) {
                    properties.setProperty(key, prefs.get(key, ""));
                }
            } catch (Exception e) {
                properties.setProperty("ERROR", e.getMessage()); // this should really never happen
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }

        return properties;
    }

    public void mergeIntoAgentConfiguration(Properties config) {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            if ((config != null) && (config.size() > 0)) {
                Preferences prefs = m_agent.getConfiguration().getPreferences();

                Set names = config.keySet();
                for (Object name : names) {
                    Object value = config.get(name);
                    prefs.put(name.toString(), value.toString()); // this persists the new config setting
                }
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }

        return;
    }

    public void removeFromAgentConfiguration(List preferenceNames) {
        ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
            if ((preferenceNames != null) && (preferenceNames.size() > 0)) {
                Preferences prefs = m_agent.getConfiguration().getPreferences();

                for (String doomedPreferenceName : preferenceNames) {
                    prefs.remove(doomedPreferenceName);
                }
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalCL);
        }

        return;
    }

    /**
     * Returns the MBeanServer where this MBean is registered; null if this MBean is not registered.
     *
     * @return the hosting MBeanServer
     */
    public MBeanServer getMBeanServer() {
        return m_mbs;
    }

    /**
     * This is the name that the MBean is registered under.
     *
     * @return mbean name
     */
    public ObjectName getObjectName() {
        return m_objectName;
    }

    /**
     * @see javax.management.MBeanRegistration#preRegister(MBeanServer, ObjectName)
     */
    public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
        singletonObjectName = name; // see the comments on this var's declaration statement for why this is needed

        m_mbs = server;
        m_objectName = name;
        return name;
    }

    /**
     * @see javax.management.MBeanRegistration#postRegister(Boolean)
     */
    public void postRegister(Boolean registrationDone) {
        // NO-OP
    }

    /**
     * @see javax.management.MBeanRegistration#postDeregister()
     */
    public void postDeregister() {
        m_mbs = null;
        m_objectName = null;
    }

    /**
     * @see javax.management.MBeanRegistration#preDeregister()
     */
    public void preDeregister() throws Exception {
        // NO-OP
    }

    /**
     * Returns the client-side metrics from the agent's sender.
     *
     * @return client side metrics
     */
    private ClientCommandSenderMetrics getClientSideMetrics() {
        ClientCommandSender sender = m_agent.getClientCommandSender();
        ClientCommandSenderMetrics metrics;

        if (sender != null) {
            metrics = sender.getMetrics();
        } else {
            metrics = new ClientCommandSenderMetrics(); // simulate an empty and idle sender (all metrics are zeroed out)
        }

        return metrics;
    }

    /**
     * Obtains a proxy the MBean that emits the server-side (i.e. incoming commands) metrics.
     *
     * @return server side metric mbean proxy
     *
     * @throws IllegalStateException if for some reason the MBean is not available or its proxy could not be created
     */
    private ServiceContainerMetricsMBean getServerSideMetrics() {
        try {
            MBeanServer mbs = m_agent.getServiceContainer().getMBeanServer();
            Object mbean = MBeanServerInvocationHandler.newProxyInstance(mbs,
                ServiceContainerMetricsMBean.OBJECTNAME_METRICS, ServiceContainerMetricsMBean.class, false);
            return (ServiceContainerMetricsMBean) mbean;
        } catch (Exception e) {
            throw new IllegalStateException(e); // should never happen
        }
    }

    /**
     * Builds a {@link PluginUpdate} object that can be used to update the plugins or get information about the plugins.
     *
     * @return plugin updater
     */
    private PluginUpdate getPluginUpdateObject() {
        ClientCommandSender sender = m_agent.getClientCommandSender();
        CoreServerService server = null;

        if (sender != null) {
            ClientRemotePojoFactory factory = sender.getClientRemotePojoFactory();
            server = factory.getRemotePojo(CoreServerService.class);
        }

        PluginContainerConfiguration pc_config = m_agent.getConfiguration().getPluginContainerConfiguration();
        PluginUpdate plugin_update = new PluginUpdate(server, pc_config);

        return plugin_update;
    }

    // this is here to support the agent plugin - the agent plugin will
    // get the singleton object name to determine which MBean it needs to
    // use.  This was needed because under testing, we like to embed multiple
    // agents in the same VM, but in different classloaders.  This enables
    // the agent plugin to be able to run in both the "normal" mode
    // and in the perf testing mode. We set it to a default name just because
    // we want it a non-null (avoid possible NPEs later) but the default
    // name is one that never exists (its missing the name key property).
    public static ObjectName singletonObjectName = AgentManagementMBean.BASE_OBJECT_NAME;
}