Package io.airlift.jmx

Source Code of io.airlift.jmx.JmxAgent

/*
* Copyright 2010 Proofpoint, Inc.
*
* Licensed 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 io.airlift.jmx;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import com.google.common.net.HostAndPort;
import com.google.inject.Inject;
import io.airlift.log.Logger;
import sun.management.Agent;
import sun.management.jmxremote.ConnectorBootstrap;
import sun.rmi.server.UnicastRef;

import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXServiceURL;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.rmi.server.RemoteObject;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;

public class JmxAgent
{
    private static final Logger log = Logger.get(JmxAgent.class);

    private final JMXServiceURL url;

    @Inject
    public JmxAgent(JmxConfig config)
            throws IOException
    {
        // first, see if the jmx agent is already started (e.g., via command line properties passed to the jvm)
        HostAndPort address = getRunningAgentAddress(config.getRmiRegistryPort(), config.getRmiServerPort());
        if (address != null) {
            log.info("JMX agent already running and listening on %s", address);
        }
        else {
            // otherwise, start it manually
            int registryPort;
            if (config.getRmiRegistryPort() == null) {
                registryPort = NetUtils.findUnusedPort();
            }
            else {
                registryPort = config.getRmiRegistryPort();
            }

            int serverPort = 0;
            if (config.getRmiServerPort() != null) {
                serverPort = config.getRmiServerPort();
            }

            // This is somewhat of a hack, but the jmx agent in Oracle/OpenJDK doesn't
            // have a programmatic API for starting it and controlling its parameters
            System.setProperty("com.sun.management.jmxremote", "true");
            System.setProperty("com.sun.management.jmxremote.port", Integer.toString(registryPort));
            System.setProperty("com.sun.management.jmxremote.rmi.port", Integer.toString(serverPort));
            System.setProperty("com.sun.management.jmxremote.authenticate", "false");
            System.setProperty("com.sun.management.jmxremote.ssl", "false");

            try {
                Agent.startAgent();
            }
            catch (Exception e) {
                throw Throwables.propagate(e);
            }

            try {
                // This is how the jdk jmx agent constructs its url
                JMXServiceURL url = new JMXServiceURL("rmi", null, registryPort);
                address = HostAndPort.fromParts(url.getHost(), url.getPort());
            }
            catch (MalformedURLException e) {
                // should not happen...
                throw new AssertionError(e);
            }

            log.info("JMX agent started and listening on %s", address);
        }

        this.url = new JMXServiceURL(String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", address.getHostText(), address.getPort()));
    }

    public JMXServiceURL getUrl()
    {
        return url;
    }

    @VisibleForTesting
    static HostAndPort getRunningAgentAddress(Integer registryPort, Integer serverPort)
    {
        JMXConnectorServer jmxServer;
        RemoteObject registry;
        int actualRegistryPort;
        try {
            jmxServer = getField(Agent.class, JMXConnectorServer.class, "jmxServer");
            registry = getField(ConnectorBootstrap.class, RemoteObject.class, "registry");

            if (jmxServer == null || registry == null) {
                log.warn("Cannot determine if JMX agent is already running (not an Oracle JVM?). Will try to start it manually.");
                return null;
            }

            actualRegistryPort = ((UnicastRef) registry.getRef()).getLiveRef().getPort();
        }
        catch (Exception e) {
            log.warn(e, "Cannot determine if JMX agent is already running. Will try to start it manually.");
            return null;
        }

        checkState(actualRegistryPort > 0, "Expected actual RMI registry port to be > 0, actual: %s", actualRegistryPort);

        // if registry port and server port were configured and the agent is already running, make sure
        // the configuration agrees to avoid surprises
        if (registryPort != null && registryPort != 0) {
            checkArgument(actualRegistryPort == registryPort,
                    "JMX agent is already running, but actual RMI registry port (%s) doesn't match configured port (%s)",
                    actualRegistryPort,
                    registryPort);
        }

        if (serverPort != null && serverPort != 0) {
            int actualServerPort = jmxServer.getAddress().getPort();
            checkArgument(actualServerPort == serverPort,
                    "JMX agent is already running, but actual RMI server port (%s) doesn't match configured port (%s)",
                    actualServerPort,
                    serverPort);
        }

        return HostAndPort.fromParts(jmxServer.getAddress().getHost(), actualRegistryPort);
    }

    private static <T> T getField(Class<?> clazz, Class<T> returnType, String name)
            throws Exception
    {
        Field field = clazz.getDeclaredField(name);
        field.setAccessible(true);
        try {
            return returnType.cast(field.get(clazz));
        }
        catch (ClassCastException e) {
            throw new IllegalArgumentException(format("Field %s in class %s is not of type %s, actual: %s", name, clazz.getName(), returnType.getName(), field.getType().getName()), e);
        }
    }
}
TOP

Related Classes of io.airlift.jmx.JmxAgent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.