/** * The MIT License * Copyright © 2010 JmxTrans team * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.googlecode.jmxtrans.model; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import javax.management.Attribute; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; import javax.management.openmbean.TabularDataSupport; import java.lang.reflect.Array; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import static com.google.common.collect.ImmutableList.Builder; import static com.google.common.collect.Maps.newHashMap; public class JmxResultProcessor { private final Query query; private final ObjectInstance objectInstance; private final String className; private final String objDomain; private final List<Attribute> attributes; public JmxResultProcessor(Query query, ObjectInstance objectInstance, List<Attribute> attributes, String className, String objDomain) { this.query = query; this.objectInstance = objectInstance; this.className = className; this.objDomain = objDomain; this.attributes = attributes; } public ImmutableList<Result> getResults() { Builder<Result> accumulator = ImmutableList.builder(); for (Attribute attribute : attributes) { getResult(accumulator, attribute); } return accumulator.build(); } /** * Used when the object is effectively a java type */ private void getResult(Builder<Result> accumulator, Attribute attribute) { Object value = attribute.getValue(); if (value == null) { return; } if (value instanceof CompositeData) { getResult(accumulator, attribute.getName(), (CompositeData) value); } else if (value instanceof CompositeData[]) { for (CompositeData cd : (CompositeData[]) value) { getResult(accumulator, attribute.getName(), cd); } } else if (value instanceof ObjectName[]) { Map<String, Object> values = newHashMap(); for (ObjectName obj : (ObjectName[]) value) { values.put(obj.getCanonicalName(), obj.getKeyPropertyListString()); } Result r = getNewResultObject(attribute.getName(), values); accumulator.add(r); } else if (value.getClass().isArray()) { // OMFG: this is nutty. some of the items in the array can be // primitive! great interview question! Map<String, Object> values = newHashMap(); for (int i = 0; i < Array.getLength(value); i++) { Object val = Array.get(value, i); values.put(attribute.getName() + "." + i, val); } accumulator.add(getNewResultObject(attribute.getName(), values)); } else if (value instanceof TabularDataSupport) { TabularDataSupport tds = (TabularDataSupport) value; Map<String, Object> values = Collections.emptyMap(); Result r = getNewResultObject(attribute.getName(), values); processTabularDataSupport(accumulator, attribute.getName(), tds); accumulator.add(r); } else if (value instanceof Map) { Result r = getNewResultObject(attribute.getName(), convertKeysToString((Map<Object, Object>) value)); accumulator.add(r); } else { Map<String, Object> values = newHashMap(); values.put(attribute.getName(), value); Result r = getNewResultObject(attribute.getName(), values); accumulator.add(r); } } private <K, V> ImmutableMap<String, V> convertKeysToString(Map<K, V> value) { ImmutableMap.Builder<String, V> values = ImmutableMap.builder(); for (Map.Entry<K, V> entry : value.entrySet()) { values.put(entry.getKey().toString(), entry.getValue()); } return values.build(); } /** * Populates the Result objects. This is a recursive function. Query * contains the keys that we want to get the values of. */ private void getResult(Builder<Result> accumulator, String attributeName, CompositeData cds) { CompositeType t = cds.getCompositeType(); Map<String, Object> values = newHashMap(); Set<String> keys = t.keySet(); for (String key : keys) { Object value = cds.get(key); if (value instanceof TabularDataSupport) { TabularDataSupport tds = (TabularDataSupport) value; processTabularDataSupport(accumulator, attributeName + "." + key, tds); // continue because we added tabular contents within above, but need primitives at this level } else if (value instanceof CompositeDataSupport) { // now recursively go through everything. CompositeDataSupport cds2 = (CompositeDataSupport) value; getResult(accumulator, attributeName, cds2); return; // because we don't want to add to the list yet. } else { values.put(key, value); } } Result r = getNewResultObject(attributeName, values); accumulator.add(r); } private void processTabularDataSupport( Builder<Result> accumulator, String attributeName, TabularDataSupport tds) { Set<Map.Entry<Object, Object>> entries = tds.entrySet(); for (Map.Entry<Object, Object> entry : entries) { Object entryKeys = entry.getKey(); if (entryKeys instanceof List) { // ie: attributeName=LastGcInfo.Par Survivor Space // i haven't seen this be smaller or larger than List<1>, but // might as well loop it. StringBuilder sb = new StringBuilder(); for (Object entryKey : (List<?>) entryKeys) { sb.append("."); sb.append(entryKey); } String attributeName2 = sb.toString(); Object entryValue = entry.getValue(); if (entryValue instanceof CompositeDataSupport) { getResult(accumulator, attributeName + attributeName2, (CompositeDataSupport) entryValue); } else { throw new RuntimeException("!!!!!!!!!! Please file a bug: https://github.com/jmxtrans/jmxtrans/issues entryValue is: " + entryValue.getClass().getCanonicalName()); } } else { throw new RuntimeException("!!!!!!!!!! Please file a bug: https://github.com/jmxtrans/jmxtrans/issues entryKeys is: " + entryKeys.getClass().getCanonicalName()); } } } /** * Builds up the base Result object */ private Result getNewResultObject(String attributeName, Map<String, Object> values) { return new Result(System.currentTimeMillis(), attributeName, className, objDomain, query.getResultAlias(), objectInstance.getObjectName().getKeyPropertyListString(), values); } }