org.rhq.plugins.jmx.util.JvmUtility.java Source code

Java tutorial

Introduction

Here is the source code for org.rhq.plugins.jmx.util.JvmUtility.java

Source

/*
 * RHQ Management Platform
 * Copyright (C) 2011-2012 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, version 2, as
 * published by the Free Software Foundation, and/or the GNU Lesser
 * General Public License, version 2.1, also as published by the Free
 * Software Foundation.
 *
 * 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 and the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * and the GNU Lesser General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.rhq.plugins.jmx.util;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.management.remote.JMXServiceURL;

import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.system.ProcessInfo;
import org.rhq.plugins.jmx.MBeanResourceComponent;

/**
 * @author Ian Springer
 */
public class JvmUtility {

    private static final Log LOG = LogFactory.getLog(MBeanResourceComponent.class);

    private static final String AGENT_PROP_JMXREMOTE_LOCAL_CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";

    private static boolean attachApiAvailable;

    static {
        try {
            Class.forName("com.sun.tools.attach.VirtualMachine");
            attachApiAvailable = true;
        } catch (ClassNotFoundException e) {
            LOG.warn("JDK tools.jar not found on system classpath - cannot discover JVMs using Sun JVM Attach API; "
                    + "to fix this, run the RHQ Agent on a JDK, rather than a JRE.");
        }
    }

    /**
     * TODO
     *
     * @param process a java process
     * @return the JMX service URL that can be used to obtain the java process' MBeanServer, or null if connecting to
     *         the process is not possible
     */
    public static JMXServiceURL extractJMXServiceURL(ProcessInfo process) {
        if (!attachApiAvailable) {
            LOG.debug("Returning null, since the Attach API is not available...");
            return null;
        }

        JMXServiceURL url;
        try {
            VirtualMachine vm = attachToVirtualMachine(process);

            if (vm == null) {
                return null;
            }

            String jmxConnectorAddress = getJmxConnectorAddress(vm);

            try {
                vm.detach();
            } catch (Exception e) {
                // We already succeeded in obtaining the connector address, so just log this, rather than throwing an exception.
                LOG.error("Failed to detach from JVM [" + vm + "].", e);
            }

            url = new JMXServiceURL(jmxConnectorAddress);
        } catch (Exception e) {
            throw new RuntimeException(
                    "Failed to extract JMX service URL for process with PID [" + process.getPid() + "].", e);
        }

        LOG.debug("JMX service URL for java process with PID [" + process.getPid() + "]: " + url);
        return url;
    }

    private static VirtualMachine attachToVirtualMachine(ProcessInfo process)
            throws AttachNotSupportedException, IOException {
        VirtualMachine vm = null;
        List<VirtualMachineDescriptor> vmDescriptors = VirtualMachine.list();
        for (VirtualMachineDescriptor vmDescriptor : vmDescriptors) {
            if (Long.valueOf(vmDescriptor.id()) == process.getPid()) {
                String vmUserName = process.getCredentialsName().getUser();
                String agentUserName = System.getProperty("user.name");
                if (vmUserName.equals(agentUserName)) {
                    LOG.debug("Attaching to JVM for java process with PID [" + process.getPid() + "]...");
                    vm = VirtualMachine.attach(vmDescriptor);
                    LOG.debug("Attached to JVM [" + vm + "].");
                } else {
                    LOG.debug("Cannot attach to JVM for java process with PID [" + process.getPid()
                            + "], because it is running as a different user (" + vmUserName
                            + ") than the user the Agent is running as (" + agentUserName + ").");
                }
                break;
            }
        }
        return vm;
    }

    private static String getJmxConnectorAddress(VirtualMachine vm) throws IOException {
        String jmxConnectorAddress = vm.getAgentProperties()
                .getProperty(AGENT_PROP_JMXREMOTE_LOCAL_CONNECTOR_ADDRESS);
        LOG.debug("Connector address for JVM [" + vm + "] is [" + jmxConnectorAddress + "].");
        if (jmxConnectorAddress == null) {
            // java.home always points to the jre dir (e.g. /usr/java/default/jre).
            String jreDir = vm.getSystemProperties().getProperty("java.home");
            // management-agent.jar is included with the v6 JRE, so we can rely on it always being there.
            File jmxAgentJarFile = new File(jreDir, "lib/management-agent.jar");
            String jmxAgentJar = jmxAgentJarFile.getCanonicalPath();
            try {
                vm.loadAgent(jmxAgentJar);
            } catch (Exception e) {
                throw new RuntimeException("Failed to load JVM agent from [" + jmxAgentJar + "].", e);
            }

            // JMX agent is started - get the connector address.
            LOG.debug("JMX agent started - getting the connector address...");
            jmxConnectorAddress = vm.getAgentProperties().getProperty(AGENT_PROP_JMXREMOTE_LOCAL_CONNECTOR_ADDRESS);
            if (jmxConnectorAddress == null) {
                throw new RuntimeException("Failed to determine JMX connector address.");
            }
        }
        return jmxConnectorAddress;
    }

    private JvmUtility() {
    }

}