All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jolokia.service.serializer.json.TabularDataExtractor Maven / Gradle / Ivy

package org.jolokia.service.serializer.json;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

import javax.management.*;
import javax.management.openmbean.*;

import org.jolokia.server.core.service.serializer.ValueFaultHandler;
import org.jolokia.service.serializer.object.StringToObjectConverter;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

/*
 * Copyright 2009-2013 Roland Huss
 *
 * 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.
 */


/**
 * @author roland
 * @since Apr 19, 2009
 */
public class TabularDataExtractor implements Extractor {

    /** {@inheritDoc} */
    public Class getType() {
        return TabularData.class;
    }

    /**
     * 

* Extract a {@link TabularData}. The JSON representation of a tabular data is different, * depending on whether it represents a map for an {@link javax.management.MXBean} or is a regular data. *

*

* I.e. for an tabular data which have a row type with two column "key" and "value", then * a map is returned (with the "key" values as map keys and "value" values as map values). *

*

* Otherwise a map of (one or more) maps is returned, where the map keys are taken * from {@link TabularType} of the presented data. E.g. if there is a single valued key * "key", then the returned JSON looks like *

     *      {
     *         "mykey1" : { "key" : "mkey1", "item" : "value1", .... }
     *         "mykey2" : { "key" : "mkey2", "item" : "value2", .... }
     *         ....
     *      }
     *  
* For multi valued keys of simple open types (i.e. {@link TabularType#getIndexNames()} is a list with more than one element), the * returned JSON structure looks like (index names here are "key" and "innerkey") *
     *      {
     *         "mykey1" : {
     *                       "myinner1" : { "key" : "mkey1", "innerkey" : "myinner1", "item" : "value1", .... }
     *                       "myinner2" : { "key" : "mkey1", "innerkey" : "myinner2", "item" : "value1", .... }
     *                       ....
     *                     }
     *         "mykey2" : {
     *                       "second1" : { "key" : "mkey2", "innerkey" : "second1", "item" : "value1", .... }
     *                       "second2" : { "key" : "mkey2", "innerkey" : "second2", "item" : "value1", .... }
     *                       ....
     *                    }
     *         ....
     *      }
     *  
* If keys are used, which themselves are complex objects (like composite data), this hierarchical map * structure can not be used. In this case an object with two keys is returned: "indexNames" holds the * name of the key index and "values" is an array of all rows which are represented as JSON objects: *
     *      {
     *        "indexNames" : [ "key", "innerkey" ],
     *        "values" : [
     *           { "key" : "mykey1", "innerkey" : { "name" : "a", "number" : 4711 }, "item" : "value1", .... },
     *           { "key" : "mykey2", "innerkey" : { "name" : "b", "number" : 815 }, "item" : "value2", .... },
     *           ...
     *        ]
     *      }
     *  
*

*

* Accessing {@link TabularData} with a path is only supported for simple type keys, i.e. each index name must point * to a string representation of a simple open type. As many path elements must be provided as index names for * the tabular type exists (i.e. pExtraArgs.size() >= pValue.getTabularType().getIndexNames().size()) * * For TabularData representing maps, a path access with the single "key" value will * return the content of the "value" value. For all other TabularData, the complete row to which the path points * is returned. *

* @param pConverter the global converter in order to be able do dispatch for * serializing inner data types * @param pValue the value to convert * @param pPathParts extra arguments which contain e.g. a path * @param pJsonify whether to convert to a JSON object/list or whether the plain object * should be returned. The later is required for writing an inner value * @return the extracted object * @throws AttributeNotFoundException */ public Object extractObject(ObjectToJsonConverter pConverter, Object pValue, Stack pPathParts,boolean pJsonify) throws AttributeNotFoundException { TabularData td = (TabularData) pValue; String tdPath = pPathParts.isEmpty() ? null : pPathParts.pop(); if (tdPath != null) { try { pPathParts.push(tdPath); // Need it later on for the index CompositeData cd = extractCompositeDataFromPath(td, pPathParts); return pConverter.extractObject( cd != null && checkForMxBeanMap(td.getTabularType()) ? cd.get("value") : cd, pPathParts, pJsonify); } catch (AttributeNotFoundException exp) { ValueFaultHandler faultHandler = pConverter.getValueFaultHandler(); return faultHandler.handleException(exp); } } else { if (pJsonify) { return checkForMxBeanMap(td.getTabularType()) ? convertMxBeanMapToJson(td,pPathParts,pConverter) : convertTabularDataToJson(td, pPathParts, pConverter); } else { return td; } } } // ==================================================================================================== /** * Check whether the given tabular type represents a MXBean map. See the * {@link javax.management.MXBean} specification for * details how a map is converted to {@link TabularData} by the MXBean framework. * * @param pType type of tabular data to convert * @return true if this type represents an MXBean map, false otherwise. */ private boolean checkForMxBeanMap(TabularType pType) { CompositeType rowType = pType.getRowType(); return rowType.containsKey("key") && rowType.containsKey("value") && rowType.keySet().size() == 2 // Only convert to map for simple types for all others use normal conversion. See #105 for details. && rowType.getType("key") instanceof SimpleType; } private Object convertTabularDataToJson(TabularData pTd, Stack pExtraArgs, ObjectToJsonConverter pConverter) throws AttributeNotFoundException { TabularType type = pTd.getTabularType(); if (hasComplexKeys(type)) { return convertTabularDataDirectly(pTd, pExtraArgs, pConverter); } else { return convertToMaps(pTd, pExtraArgs, pConverter); } } // Check, whether all keys are simple types or not private boolean hasComplexKeys(TabularType pType) { List indexes = pType.getIndexNames(); CompositeType rowType = pType.getRowType(); for (String index : indexes) { if ( ! (rowType.getType(index) instanceof SimpleType)) { return true; } } return false; } // Convert tabular data to (nested) maps. Path access is allowed here private Object convertToMaps(TabularData pTd, Stack pExtraArgs, ObjectToJsonConverter pConverter) throws AttributeNotFoundException { TabularType type = pTd.getTabularType(); List indexNames = type.getIndexNames(); JSONObject ret = new JSONObject(); boolean found = false; for (CompositeData cd : (Collection) pTd.values()) { Stack path = (Stack) pExtraArgs.clone(); try { JSONObject targetJSONObject = ret; // TODO: Check whether all keys can be represented as simple types. If not, well // we dont do any magic and return the tabular data as an array. for (int i = 0; i < indexNames.size() - 1; i++) { Object indexValue = pConverter.extractObject(cd.get(indexNames.get(i)), null, true); targetJSONObject = getNextMap(targetJSONObject, indexValue); } Object row = pConverter.extractObject(cd, path, true); String finalIndex = indexNames.get(indexNames.size() - 1); Object finalIndexValue = pConverter.extractObject(cd.get(finalIndex), null, true); targetJSONObject.put(finalIndexValue, row); found = true; } catch (ValueFaultHandler.AttributeFilteredException exp) { // Ignoring filtered attributes } } if (!found) { throw new ValueFaultHandler.AttributeFilteredException(); } return ret; } // Convert to a direct representation of the tabular data private Object convertTabularDataDirectly(TabularData pTd, Stack pExtraArgs, ObjectToJsonConverter pConverter) throws AttributeNotFoundException { if (!pExtraArgs.empty()) { throw new IllegalArgumentException("Cannot use a path for converting tabular data with complex keys (" + pTd.getTabularType().getRowType() + ")"); } JSONObject ret = new JSONObject(); JSONArray indexNames = new JSONArray(); TabularType type = pTd.getTabularType(); for (String index : type.getIndexNames()) { indexNames.add(index); } ret.put("indexNames",indexNames); JSONArray values = new JSONArray(); // Here no special handling for wildcard pathes since pathes are not supported for this use case (yet) for (CompositeData cd : (Collection) pTd.values()) { values.add(pConverter.extractObject(cd, pExtraArgs, true)); } ret.put("values",values); return ret; } private JSONObject getNextMap(JSONObject pJsonObject, Object pKey) { JSONObject ret = (JSONObject) pJsonObject.get(pKey); if (ret == null) { ret = new JSONObject(); pJsonObject.put(pKey, ret); } return ret; } private CompositeData extractCompositeDataFromPath(TabularData pTd, Stack pPathStack) throws AttributeNotFoundException { // We first try it as a key TabularType type = pTd.getTabularType(); List indexNames = type.getIndexNames(); checkPathFitsIndexNames(pPathStack, indexNames); Object keys[] = new Object[indexNames.size()]; CompositeType rowType = type.getRowType(); List pathPartsUsed = new ArrayList(); for (int i = 0; i < indexNames.size(); i++) { String path = pPathStack.pop(); pathPartsUsed.add(path); keys[i] = getKey(rowType, indexNames.get(i), path); } if (pTd.containsKey(keys)) { return pTd.get(keys); } else { throw new AttributeNotFoundException("No entry with " + pathPartsUsed + " found"); } } private void checkPathFitsIndexNames(Stack pPathStack, List pIndexNames) throws AttributeNotFoundException { if (pIndexNames.size() > pPathStack.size()) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < pIndexNames.size(); i++) { buf.append(pIndexNames.get(i)); if (i < pIndexNames.size() - 1) { buf.append(","); } } throw new AttributeNotFoundException("No enough keys on path stack provided for accessing tabular data with index names " + buf.toString()); } } // The key is tried to convert to the proper type. These checks are // a bit redundant, since this sort of conversion is already offered // in StringToOpenTypeConverter. Unfortunately, this converter is not // easily available here. For 2.0 the modularity aspects are refactored // from the ground up, so I can live with the solution here. // See also #97 for details. private Object getKey(CompositeType rowType, String key, String value) { OpenType keyType = rowType.getType(key); if (SimpleType.STRING == keyType) { return value; } else if (SimpleType.INTEGER == keyType) { return Integer.parseInt(value); } else if (SimpleType.LONG == keyType) { return Long.parseLong(value); } else if (SimpleType.SHORT == keyType) { return Short.parseShort(value); } else if (SimpleType.BYTE == keyType) { return Byte.parseByte(value); } else if (SimpleType.OBJECTNAME == keyType) { try { return new ObjectName(value); } catch (MalformedObjectNameException e) { throw new IllegalArgumentException("Can not convert " + value + " to an ObjectName",e); } } else { throw new IllegalArgumentException("All keys must be a string, integer, long, short, byte or ObjectName type for accessing TabularData via a path. " + "This is not the case for '" + key + "' which is of type " + keyType); } } private Object convertMxBeanMapToJson(TabularData pTd, Stack pExtraArgs, ObjectToJsonConverter pConverter) throws AttributeNotFoundException { JSONObject ret = new JSONObject(); for (Object rowObject : pTd.values()) { CompositeData row = (CompositeData) rowObject; Stack path = (Stack) pExtraArgs.clone(); Object keyObject = row.get("key"); if (keyObject != null) { try { Object value = pConverter.extractObject(row.get("value"), path, true); ret.put(keyObject.toString(), value); } catch (ValueFaultHandler.AttributeFilteredException exp) { // Skip to next object since attribute was filtered } } } if (ret.isEmpty()) { // Bubble up if not a single thingy has been found throw new ValueFaultHandler.AttributeFilteredException(); } return ret; } /** * Throws always {@link IllegalArgumentException} since tabular data is immutable */ public Object setObjectValue(StringToObjectConverter pConverter, Object pInner, String pAttribute, Object pValue) throws IllegalAccessException, InvocationTargetException { throw new IllegalArgumentException("TabularData cannot be written to"); } /** {@inheritDoc} */ public boolean canSetValue() { return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy