com.zenoss.jmx.ValueExtractor.java Source code

Java tutorial

Introduction

Here is the source code for com.zenoss.jmx.ValueExtractor.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.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.regex.Pattern;

import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;

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

public class ValueExtractor {

    private static final Log _logger = LogFactory.getLog(ValueExtractor.class);

    /**
     * Traverses a TabularData or CompositeData structure to return nested data
     * 
     * e.g. To get the the used perm gen memory before last garbage collection
     * value from the result of the lastGcInfo attribute of the
     * java.lang:type=GarbageCollector,name=Copy mbean the path used would be
     * 
     * memoryUsageBeforeGc.[Perm Gen].used
     * 
     * In general the non bracketed values are keys into CompositeData and the
     * bracketed values are indexes into TabularData. For TabularData indexes
     * with more than one value a comma separated list without spaces should be
     * used, spaces are treated as part of the values in the index array.
     * 
     * e.g. [key1,key2]
     * 
     * The brackets aren't mandatory for indexes but are included for clarity.
     * 
     * Curly brackets can be used after a table index to specify a column name.
     * 
     * e.g memoryUsageBeforeGc.[Perm Gen].{value}.used
     * 
     * The column name is only necessary when the table has more than two
     * columns. If the table has two columns then the value is read from the
     * column not used for the index.
     * 
     * @param obj
     *            TabularData, CompositeData, or Map
     * @param path
     *            dot separated string that represents a path through the object
     * @return Object the value at the end of the path
     * @throws JmxException
     *             if a path element doesn't exist
     */
    public static Object getDataValue(final Object obj, String path) throws JmxException {
        if (!(obj instanceof TabularData) && !(obj instanceof CompositeData) && !(obj instanceof Map)) {

            throw new IllegalArgumentException("Cannot process object of type " + obj.getClass().getName());

        }

        _logger.debug("getDataValue: path is " + path);

        List<String> pathList = split(path);
        _logger.debug("getDataValue: pathList " + pathList);

        Object currentObj = obj;
        Iterator<String> pathElements = pathList.iterator();
        try {
            while (pathElements.hasNext()) {
                _logger.debug("getDataValue: current object is " + obj);
                String currentKey = pathElements.next();
                pathElements.remove();

                _logger.debug("getDataValue: currentKey: " + currentKey);

                if (currentObj instanceof TabularData) {
                    _logger.debug("getDataValue: dealing with tabularData");
                    TabularData tData = (TabularData) currentObj;

                    String[] index = createTableIndex(currentKey);
                    currentObj = getDataByTableIndex(tData, index);
                    CompositeData cData = (CompositeData) currentObj;

                    int columnCount = cData.values().size();
                    String nextKey = null;

                    // look ahead and look for explicit column
                    if (!pathList.isEmpty()) {
                        nextKey = pathList.get(0);
                    }

                    if (nextKey != null && (isColumn(nextKey) || columnCount > 2)) {

                        String columnKey = pathElements.next();
                        pathElements.remove();
                        if (isColumn(columnKey)) {
                            _logger.debug("using explicit column key " + columnKey + " for tabular data");
                            // remove first and last char - should be curly
                            // brackets
                            columnKey = columnKey.substring(1, columnKey.length() - 1);
                        } else {
                            _logger.debug(
                                    "using column key " + columnKey + " for tabular data. No curly brackets found");
                        }
                        currentObj = getData(cData, columnKey);
                    } else if (cData.values().size() == 2) {
                        currentObj = getTableRowData(cData, index);
                    }
                } else if (currentObj instanceof CompositeData) {
                    _logger.debug("getDataValue: dealing with CompositeData");
                    CompositeData cData = (CompositeData) currentObj;
                    currentObj = getData(cData, currentKey);
                } else if (currentObj instanceof Map) {
                    _logger.debug("getDataValue: dealing with Map");
                    Map mData = (Map) currentObj;
                    currentObj = getData(mData, currentKey);
                } else {
                    // we still have a path but the object isn't composite or
                    // tabluar
                    String remainingPath = currentKey;
                    for (String val : pathList) {
                        remainingPath += ".";
                        remainingPath += val;

                    }
                    _logger.warn(
                            "getDataValue: we still have a path but the " + "object isn't composite or tabluar");
                    _logger.warn("getDataValue: remaining path is " + remainingPath);
                    throw new JmxException("we still have a path but the "
                            + "object isn't composite or tabluar, remaining " + "" + "path is " + remainingPath);

                }

            }
        } catch (Exception e) {
            _logger.warn("could not get object for path " + path, e);
            throw new JmxException("could not get object for path " + path + "; " + e.getMessage(), e);
        }
        return currentObj;
    }

    private static boolean isColumn(String key) {
        return key.startsWith("{") && key.endsWith("}");
    }

    private static Object getData(CompositeData cData, String key) {
        _logger.debug("composite data is: " + cData);
        _logger.debug("getting '" + key + "' from composite data");
        Object result = cData.get(key);
        _logger.debug("value from composite data is " + result);
        return result;
    }

    private static Object getData(Map mData, String key) {
        _logger.debug("map data is: " + mData);
        _logger.debug("getting '" + key + "' from map data");
        Object result = mData.get(key);
        _logger.debug("value from map data is " + result);
        return result;
    }

    private static String[] createTableIndex(String currentKey) {
        String index = null;
        if (currentKey.startsWith("[") && currentKey.endsWith("]")) {
            _logger.debug("getDataValue: looks like an explicit index: " + currentKey);
            // remove first and last char - should be brackets
            index = currentKey.substring(1, currentKey.length() - 1);
        } else {
            _logger.debug("getDataValue: no explicit index: " + currentKey);
            // no explicit table index,
            // assume index is the same as the name of the value
            index = currentKey;
        }

        _logger.debug("spliting " + index + " for index ");
        String[] indexValues = index.split(",");

        _logger.debug("index is " + Arrays.toString(indexValues));

        return indexValues;
    }

    private static Object getDataByTableIndex(TabularData tData, String[] tableIndex) throws JmxException {
        _logger.debug("TablularData is: " + tData);

        _logger.debug("extracting composite data from tabulardata with index " + Arrays.toString(tableIndex));
        CompositeData composite = (CompositeData) tData.get(tableIndex);
        if (composite == null) {
            throw new JmxException(
                    Arrays.toString(tableIndex) + " is not an existing Index for this tabular data ");
        }
        _logger.debug("extracted composite data: " + composite);
        return composite;
    }

    private static Object getTableRowData(CompositeData cData, String[] index) throws JmxException {
        Object result = null;
        // This gets the data with a key not in index
        Set<String> keys = new HashSet<String>(Arrays.asList(index));

        for (String key : keys) {
            if (!cData.values().contains(key)) {
                _logger.warn(key + " not found in composite data row for tabular data");
                throw new JmxException(key + " not found in composite data row for tabular data");

            }
        }

        // find the first value that isn't a part of the index
        for (Object value : cData.values()) {
            if (!keys.contains(value)) {
                result = value;
            }
        }
        return result;
    }

    /**
     * split a string on dots but leave values within brackets ([,}) that have dots intact
     * for example the string "test.{bracket.stuff}" should be split to an array 
     * [test, {bracket.stuff}]
     * 
     * @param path
     * @return
     */
    public static LinkedList<String> split(String path) {

        String[] splitPath = dotPattern.split(path);
        Iterator<String> pathElements = Arrays.asList(splitPath).iterator();
        LinkedList<String> resultPath = new LinkedList<String>();
        while (pathElements.hasNext()) {
            String pathElement = pathElements.next();

            while (bracketStart.matcher(pathElement).matches() && notBracketEnd.matcher(pathElement).matches()
                    && pathElements.hasNext()) {
                pathElement = pathElement + "." + pathElements.next();

            }
            resultPath.add(pathElement);
        }
        return resultPath;
    }

    private static Pattern dotPattern = Pattern.compile("\\.");
    private static Pattern bracketStart = Pattern.compile("^[\\{\\[].*");
    private static Pattern notBracketEnd = Pattern.compile(".*[^\\}\\]]$");

}