com.zenoss.jmx.JmxClient.java Source code

Java tutorial

Introduction

Here is the source code for com.zenoss.jmx.JmxClient.java

Source

/*****************************************************************************
 * 
 * Copyright (C) Zenoss, Inc. 2008, all rights reserved.
 * 
 * This content is made available according to terms specified in
 * License.zenoss under the directory where your Zenoss product is installed.
 * 
 ****************************************************************************/

package com.zenoss.jmx;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.InvalidKeyException;
import javax.management.openmbean.TabularData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <p>
 * JmxClient is a Java client to JMX Agents. It supports authentication
 * information.
 * </p>
 * 
 * <p>
 * Users of JmxClient can connect to JMX Agents, authenticate, and obtain copies
 * of attribute values that exist on the server. Multi-value attributes are
 * supported via key attributes that can be passed.
 * </p>
 * 
 * <p>
 * $Author: chris $<br>
 * $Date: 2005/03/13 18:45:25 $
 * </p>
 * 
 * @author Christopher Blunck
 * @version $Revision: 1.6 $
 */
public class JmxClient {
    // url that describes the agent to connect to
    private JMXServiceURL _url;

    // credentials to offer when connecting to the JMX Agent
    private String[] _creds;

    // factory used to create connections to the url
    private JMXConnector _connector;

    // connection to remote JMX Agent
    private MBeanServerConnection _server;

    // flag used to track if we are connected to the server or not
    private boolean _connected;

    // logger
    private static final Log _logger = LogFactory.getLog(JmxClient.class);

    /**
     * Creates a JmxClient for interacting with the local Platform MBeanServer
     * 
     */
    public JmxClient() {
        _server = ManagementFactory.getPlatformMBeanServer();
        _connected = true;
    }

    /**
     * Creates a JmxClient that will interrogate the JMX Agent at the URL
     * provided.
     * 
     * @param url
     *            an Abstract Service URL for SLP, as defined in RFC 2609 and
     *            amended by RFC3111 (e.g. service:jmx:protocol:sap)
     * @throws IllegalArgumentException
     *             if the URL provided is malformed
     */
    public JmxClient(String url) throws IllegalArgumentException {

        try {
            _url = new JMXServiceURL(url);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Sets authentication and authorization credentials
     */
    public void setCredentials(String[] creds) {
        _creds = creds;
    }

    /**
     * Connects to the JMX Agent
     * 
     * @throws JMXException
     *             if an exception occurs during connection
     */
    public void connect() throws JmxException {

        // short-circuit to avoid re-connecting
        if (_connected) {
            return;
        }

        try {
            // honor the authentication credentials
            Map<String, Object> env = new HashMap<String, Object>();
            env.put(JMXConnector.CREDENTIALS, _creds);

            _connector = JMXConnectorFactory.connect(_url, env);
            _server = _connector.getMBeanServerConnection();
        } catch (IOException e) {
            throw new JmxException("Failed to connect to " + _url, e);
        }

        _connected = true;
    }

    /**
     * Closes the conncetion to the JMX Agent
     * 
     * @throws JmxException
     *             if an exception occurs while closing down the connection
     */
    public void close() throws JmxException {
        try {
            if (_connected) {
                try {
                    if (_connector != null) {
                        _connector.close();
                    }
                } catch (IOException e) {
                    throw new JmxException(e);
                }
            }
        } finally {
            _connected = false;
            _connector = null;
            _server = null;
            _creds = null;
        }
    }

    /**
     * Builds an ObjectName using the name provided
     * 
     * @param name
     *            the MBean name
     * @throws JmxException
     *             if the mbean name is not a properly formatted object name.
     */
    private ObjectName buildObjectName(String name) throws JmxException {

        // construct the object name, making sure it is properly formatted
        ObjectName on = null;
        try {
            on = new ObjectName(name);
        } catch (MalformedObjectNameException e) {
            throw new JmxException("object name is malformed: " + name);
        }

        return on;
    }

    /**
     * Queries the JMX Agent and retrieves the attribute requested
     * 
     * @param objectName
     *            the name of the MBean
     * @param attribute
     *            the attribute to query
     * @return the Attribute that was read from the server
     * @throws JMXException
     *             if an error occurs while querying
     */
    public Object query(String objectName, String attributeName) throws JmxException {

        // make sure we're connected
        if (!_connected) {
            throw new JmxException("not connected");
        }

        // make sure the mbean described by the object name is registered
        ObjectName on = buildObjectName(objectName);
        checkRegistration(on);

        // get the attribute on the mbean requested
        Object attribute = null;
        try {
            attribute = _server.getAttribute(on, attributeName);
        } catch (Exception e) {
            String message = "error occurred while accessing attribute '" + attributeName + "' on object '" + on
                    + "'";
            throw new JmxException(message, e);
        }

        return attribute;
    }

    /**
     * Invokes the requested operation on the MBean provided, returning the
     * result as an Object.
     * 
     * @param objectName
     *            the name of the MBean
     * @param operation
     *            the name of the method to invoke on the MBean
     * @param params
     *            parameters to be passed to the operation
     * @param types
     *            array of class names (in String format) that represent the
     *            types of the parameters.
     * @throws JmxException
     *             if any exception occurs during processing
     */
    public Object invoke(String objectName, String operation, Object[] params, String[] types) throws JmxException {

        // make sure we're connected
        if (!_connected) {
            throw new JmxException("not connected");
        }

        // make sure the mbean described by the object name is registered
        ObjectName on = buildObjectName(objectName);
        checkRegistration(on);

        // invoke the operation
        Object result = null;
        try {
            result = _server.invoke(on, operation, params, types);
        } catch (Exception e) {
            throw new JmxException("error occurred while invoking method", e);
        }

        return result;
    }

    /**
     * Verifies that the ObjectName provided is registered in the JMX Agent. If
     * the ObjectName is not registered a JmxException is raised
     * 
     * @param on
     *            the MBean name to check
     * @throws JmxException
     *             if no MBean is located with the name provided in the
     *             ObjectName
     */
    private void checkRegistration(ObjectName on) throws JmxException {

        try {
            if (!_server.isRegistered(on)) {
                throw new JmxException("no MBean registered with name: " + on);
            }
        } catch (IOException e) {
            String msg = "error occurred while checking if mbean with name '" + on + "' is registered";
            throw new JmxException(msg, e);
        }
    }

    /**
     * Queries the Jmx Agent and retrieves a multi-value attribute. Extracts the
     * attribute with the key provided.
     * 
     * @param objectName
     *            the name of the MBean
     * @param attribute
     *            the multi-value attribute to query
     * @param keys
     *            the keys of the multi-value attributes to query
     * @return a Map<String, Object> where the key is the attribute name and the
     *         value is the value retreived from the JMX Agent
     * @throws JmxException
     *             if an error occurs while querying
     */
    public Map<String, Object> query(String objectName, String attribute, List<String> keys, String dataPath)
            throws JmxException {

        _logger.debug("using the following keys: " + keys);
        _logger.debug("using the following attribute: " + attribute);
        _logger.debug("using the following string: " + objectName);
        String path = dataPath;
        // issue the query
        Object value = query(objectName, attribute);

        if (path != null && path.length() > 0) {
            _logger.debug("Extracting data with path " + path);
            try {
                value = ValueExtractor.getDataValue(value, path);
            } catch (IllegalArgumentException e) {

                _logger.warn("Could not process path " + path + " on object " + value, e);
                throw new JmxException(
                        "Could not process path " + path + " on object of type " + value.getClass().getName());
            } catch (RuntimeException e) {
                _logger.warn("error processing path " + path + " on object " + value, e);
                throw new JmxException("error processing path " + path, e);
            }
        }

        // marshal composite values into
        Map<String, Object> values = null;

        if (value instanceof CompositeDataSupport || value instanceof TabularData || value instanceof Map) {
            values = mapValues(value, keys);
        }
        // if the attribute wasn't multi-value just return the attribute
        else {
            _logger.debug("dealing with other data");
            values = new HashMap<String, Object>();
            values.put(keys.iterator().next(), value);
        }

        return values;
    }

    private Map<String, Object> mapValues(Object obj, List<String> dataPointKeys) {
        HashMap<String, Object> values = new HashMap<String, Object>();
        for (String dataPoint : dataPointKeys) {
            try {
                _logger.debug("Extracting value for datapoint '" + dataPoint + "'");
                Object value = ValueExtractor.getDataValue(obj, dataPoint);
                values.put(dataPoint, value);
            } catch (Exception e) {
                _logger.warn("Failed to extract value for datapoint '" + dataPoint + "'; " + e.getMessage());
            }
        }
        return values;
    }

}