de.jgoldhammer.alfresco.jscript.jmx.JmxDumpUtil.java Source code

Java tutorial

Introduction

Here is the source code for de.jgoldhammer.alfresco.jscript.jmx.JmxDumpUtil.java

Source

/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package de.jgoldhammer.alfresco.jscript.jmx;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;

import org.apache.commons.lang.StringUtils;

/**
 * A utility class providing a method to dump a local or remote MBeanServer's
 * entire object tree for support purposes. Nested arrays and CompositeData
 * objects in MBean attribute values are handled.
 * 
 * @author dward
 */
public class JmxDumpUtil {
    /** Table header for attribute names. */
    private static final String NAME_HEADER = "Attribute Name";

    /** Table header for attribute values. */
    private static final String VALUE_HEADER = "Attribute Value";

    /** Place holder for nulls. */
    private static final String NULL_VALUE = "<null>";

    /** Place holder for unreadable values. */
    private static final String UNREADABLE_VALUE = "<not readable>";

    /**
     * Dumps a local or remote MBeanServer's entire object tree for support
     * purposes. Nested arrays and CompositeData objects in MBean attribute
     * values are handled.
     * 
     * @param connection
     *            the server connection (or server itself)
     * @param out
     *            PrintWriter to write the output to
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static void dumpConnection(MBeanServerConnection connection, PrintWriter out) throws IOException {
        JmxDumpUtil.showStartBanner(out);

        // Get all the object names
        Set<ObjectName> objectNames = connection.queryNames(null, null);

        // Sort the names (don't assume ObjectName implements Comparable in JDK
        // 1.5)
        Set<ObjectName> newObjectNames = new TreeSet<ObjectName>(new Comparator<ObjectName>() {
            public int compare(ObjectName o1, ObjectName o2) {
                return o1.toString().compareTo(o2.toString());
            }
        });
        newObjectNames.addAll(objectNames);
        objectNames = newObjectNames;

        // Dump each MBean
        for (ObjectName objectName : objectNames) {
            try {
                printMBeanInfo(connection, objectName, out, null);
            } catch (JMException e) {
                // Sometimes beans can disappear while we are examining them
            }
        }
    }

    public static List<String> getAllAttributeNames(MBeanServerConnection connection, ObjectName objectName)
            throws IOException, JMException {
        List<String> allProperties = new ArrayList<String>();
        MBeanInfo info = connection.getMBeanInfo(objectName);
        for (MBeanAttributeInfo element : info.getAttributes()) {
            allProperties.add(element.getName());
        }
        return allProperties;
    }

    /**
     * Dumps the details of a single MBean.
     * 
     * @param connection
     *            the server connection (or server itself)
     * @param objectName
     *            the object name
     * @param out
     *            PrintWriter to write the output to
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws JMException
     *             Signals a JMX error
     */
    public static void printMBeanInfo(MBeanServerConnection connection, ObjectName objectName, PrintWriter out,
            String attributeName) throws IOException, JMException {
        Map<String, Object> attributes = new TreeMap<String, Object>();
        MBeanInfo info = connection.getMBeanInfo(objectName);
        attributes.put("** Object Name", objectName.toString());
        attributes.put("** Object Type", info.getClassName());

        if (StringUtils.isNotBlank(attributeName)) {
            Object value;
            try {
                value = connection.getAttribute(objectName, attributeName);
            } catch (Exception e) {
                value = JmxDumpUtil.UNREADABLE_VALUE;
            }
            outputValue(out, value, 10);
        } else {

            for (MBeanAttributeInfo element : info.getAttributes()) {
                Object value;
                if (element.isReadable()) {
                    try {
                        value = connection.getAttribute(objectName, element.getName());
                    } catch (Exception e) {
                        value = JmxDumpUtil.UNREADABLE_VALUE;
                    }
                } else {
                    value = JmxDumpUtil.UNREADABLE_VALUE;
                }
                attributes.put(element.getName(), value);
            }
            tabulate(JmxDumpUtil.NAME_HEADER, JmxDumpUtil.VALUE_HEADER, attributes, out, 0);
        }
    }

    /**
     * Dumps the details of a single MBean.
     * 
     * @param connection
     *            the server connection (or server itself)
     * @param objectName
     *            the object name
     * @param out
     *            PrintWriter to write the output to
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws JMException
     *             Signals a JMX error
     */
    public static Map<Object, Object> getSimpleMBeanInfo(MBeanServerConnection connection, ObjectName objectName)
            throws IOException, JMException {
        Map<Object, Object> attributes = new TreeMap<Object, Object>();
        MBeanInfo info = connection.getMBeanInfo(objectName);
        attributes.put("** Object Name", objectName.toString());
        attributes.put("** Object Type", info.getClassName());

        for (MBeanAttributeInfo element : info.getAttributes()) {
            Object value;
            if (element.isReadable()) {
                try {
                    value = connection.getAttribute(objectName, element.getName());
                } catch (Exception e) {
                    value = JmxDumpUtil.UNREADABLE_VALUE;
                }
            } else {
                value = JmxDumpUtil.UNREADABLE_VALUE;
            }
            attributes.put(element.getName(), value);
        }
        return attributes;
    }

    /**
     * Dumps the details of a single CompositeData object.
     * 
     * @param composite
     *            the composite object
     * @param out
     *            PrintWriter to write the output to
     * @param nestLevel
     *            the nesting level
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    @SuppressWarnings("unchecked")
    public static void printCompositeInfo(CompositeData composite, PrintWriter out, int nestLevel)
            throws IOException {
        Map<String, Object> attributes = new TreeMap<String, Object>();
        for (String key : (Set<String>) composite.getCompositeType().keySet()) {
            Object value;
            try {
                value = composite.get(key);
            } catch (Exception e) {
                value = JmxDumpUtil.UNREADABLE_VALUE;
            }
            attributes.put(key, value);
        }
        tabulate(JmxDumpUtil.NAME_HEADER, JmxDumpUtil.VALUE_HEADER, attributes, out, nestLevel);
    }

    /**
     * Tabulates a given String -> Object Map.
     * 
     * @param keyHeader
     *            the key header
     * @param valueHeader
     *            the value header
     * @param rows
     *            Map containing key value pairs forming the rows
     * @param out
     *            PrintWriter to write the output to
     * @param nestLevel
     *            the nesting level
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static void tabulate(String keyHeader, String valueHeader, Map<String, Object> rows, PrintWriter out,
            int nestLevel) throws IOException {
        if (rows.isEmpty()) {
            return;
        }
        // Calculate column lengths
        int maxKeyLength = keyHeader.length(), maxValLength = valueHeader.length();
        for (Map.Entry<String, Object> entry : rows.entrySet()) {
            maxKeyLength = Math.max(maxKeyLength, entry.getKey().length());
            maxValLength = Math.max(maxValLength, getValueLength(entry.getValue()));
        }
        // Output Header
        outputRow(out, maxKeyLength, keyHeader, valueHeader, nestLevel);
        indent(out, nestLevel);
        for (int col = 0; col < maxKeyLength; col++) {
            out.print('-');
        }
        out.print(' ');
        for (int col = 0; col < maxValLength; col++) {
            out.print('-');
        }
        out.println();

        // Output Body
        for (Map.Entry<String, Object> entry : rows.entrySet()) {
            outputRow(out, maxKeyLength, entry.getKey(), entry.getValue(), nestLevel);
        }
        out.println();
    }

    /**
     * Outputs spaces in the left hand margin appropriate for the given nesting
     * level.
     * 
     * @param out
     *            PrintWriter to write the output to
     * @param nestLevel
     *            the nesting level
     */
    private static void indent(PrintWriter out, int nestLevel) {
        int size = nestLevel * 3;
        for (int i = 0; i < size; i++) {
            out.print(' ');
        }
    }

    /**
     * Outputs a single row in a two-column table. The first column is padded
     * with spaces so that the second column is aligned.
     * 
     * @param out
     *            PrintWriter to write the output to
     * @param maxKeyLength
     *            maximum number of characters in the first column
     * @param key
     *            the first column value
     * @param value
     *            the second column value
     * @param nestLevel
     *            the nesting level
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    private static void outputRow(PrintWriter out, int maxKeyLength, String key, Object value, int nestLevel)
            throws IOException {
        indent(out, nestLevel);
        out.print(key);
        for (int i = key.length() - 1; i < maxKeyLength; i++) {
            out.print(' ');
        }
        outputValue(out, value, nestLevel);
    }

    /**
     * Outputs a single value, dealing with nested arrays and CompositeData
     * objects.
     * 
     * @param out
     *            PrintWriter to write the output to
     * @param value
     *            the value to output
     * @param nestLevel
     *            the nesting level
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public static void outputValue(PrintWriter out, Object value, int nestLevel) throws IOException {
        if (value == null) {
            out.println(JmxDumpUtil.NULL_VALUE);
        } else if (value.getClass().isArray()) {
            int length = Array.getLength(value);
            if (length == 0) {
                out.println("[]");
            } else {
                out.println();
                indent(out, nestLevel + 1);
                out.println('[');
                for (int i = 0; i < length; i++) {
                    indent(out, nestLevel + 2);
                    outputValue(out, Array.get(value, i), nestLevel + 2);
                    if (i + 1 < length) {
                        indent(out, nestLevel + 1);
                        out.println(',');
                    }
                }
                indent(out, nestLevel + 1);
                out.println(']');
            }
        } else if (value instanceof CompositeData) {
            out.println();
            indent(out, nestLevel + 1);
            out.println('[');
            printCompositeInfo((CompositeData) value, out, nestLevel + 2);
            indent(out, nestLevel + 1);
            out.println(']');
        } else {
            out.println(value.toString());
        }
    }

    /**
     * Gets the number of characters required to encode a value.
     * 
     * @param value
     *            the value to be encoded
     * @return the number of characters
     */
    private static int getValueLength(Object value) {
        if (value == null) {
            return JmxDumpUtil.NULL_VALUE.length();
        } else if (value.getClass().isArray() || value instanceof CompositeData) {
            // We continue arrays and composites on a new line
            return 0;
        } else {
            return value.toString().length();
        }
    }

    /**
     * Show a message stating the JmxDumper has been started, with the current
     * date and time.
     */
    private static void showStartBanner(PrintWriter out) {
        DateFormat df = SimpleDateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
        out.println(JmxDumpUtil.class.getSimpleName() + " started: " + df.format(new Date()));
        out.println();
    }
}