Attribute.java
package com.github.mk23.jmxproxy.core;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.List;
import java.util.ArrayList;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
/**
* <p>JMX Attribute tracker and serializer.</p>
*
* Saves a JMX Attribute value object. Implements JsonSerializable
* interface to convert the stored value into JSON. On serialization,
* recursively inspects the value type and marshals it to JSON using
* the supplied JsonGenerator. For {@link Array}, {@link Iterable},
* {@link TabularData}, builds a JSON array. For {@link CompositeData},
* builds a JSON object. For any other native types or null values, builds
* a JSON equivalent.
*
* <p>Special cases:</p>
*
* <ul>
* <li>
* Any {@link String} that contains valid JSON, will also be recursively
* serialized using a dynamic JsonParser.
* </li>
* <li>
* Any <code>NaN</code> {@link Double} or {@link Float} value will yield
* a JSON string <code>"NaN"</code>.
* </li>
* <li>
* Any <code>infinite</code> {@link Double} or {@link Float} value will
* yield a JSON string <code>"Infinity"</code>.
* </li>
* </ul>
*
* @see <a href="http://fasterxml.github.io/jackson-core/javadoc/2.6/com/fasterxml/jackson/core/JsonGenerator.html">com.fasterxml.jackson.core.JsonGenerator</a>
* @see <a href="http://fasterxml.github.io/jackson-core/javadoc/2.6/com/fasterxml/jackson/core/JsonParser.html">com.fasterxml.jackson.core.JsonParser</a>
* @see <a href="https://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/JsonSerializable.html">com.fasterxml.jackson.databind.JsonSerializable</a>
*
* @since 2015-05-11
* @author mk23
* @version 3.2.0
*/
public class Attribute implements JsonSerializable {
private Object attributeValue;
/**
* <p>Default constructor.</p>
*
* Saves the JMX Attribute object for later serialization.
*
* @param attributeValue object for later serialization.
*/
public Attribute(final Object attributeValue) {
this.attributeValue = attributeValue;
}
/** {@inheritDoc} */
@Override
public final void serialize(
final JsonGenerator jgen,
final SerializerProvider sp
) throws IOException, JsonProcessingException {
serializeWithType(jgen, sp, null);
}
/** {@inheritDoc} */
@Override
public final void serializeWithType(
final JsonGenerator jgen,
final SerializerProvider sp,
final TypeSerializer ts
) throws IOException, JsonProcessingException {
buildJson(jgen, attributeValue);
}
/**
* <p>JMX Attribute value JSON serializer.</p>
*
* Inspects the stored JMX Attribute value type and serializes it to
* JSON using the supplied JsonGenerator. Recursively calls itself
* if finding JMX collections. For an {@link Array}, {@link Iterable},
* or {@link TabularData}, builds a JSON array. For {@link CompositeData},
* builds a JSON object. For any other native types or null values, builds
* a JSON equivalent.
*
* <p>Special cases:</p>
*
* <ul>
* <li>
* Any {@link String} that contains valid JSON, will also be recursively
* serialized using a dynamic JsonParser.
* </li>
* <li>
* Any <code>NaN</code> {@link Double} or {@link Float} value will yield
* a JSON string <code>"NaN"</code>.
* </li>
* <li>
* Any <code>infinite</code> {@link Double} or {@link Float} value will
* yield a JSON string <code>"Infinity"</code>.
* </li>
* </ul>
*
* @see <a href="http://fasterxml.github.io/jackson-core/javadoc/2.6/com/fasterxml/jackson/core/JsonGenerator.html">com.fasterxml.jackson.core.JsonGenerator</a>
* @see <a href="http://fasterxml.github.io/jackson-core/javadoc/2.6/com/fasterxml/jackson/core/JsonParser.html">com.fasterxml.jackson.core.JsonParser</a>
*
* @param jgen The jersey-supplied JSON generator to use for serialization.
* @param objectValue The JMX Attribute value or an element of a collection to serialize.
*/
private void buildJson(
final JsonGenerator jgen,
final Object objectValue
) throws IOException, JsonProcessingException {
if (objectValue == null) {
jgen.writeNull();
} else if (objectValue instanceof Boolean) {
jgen.writeBoolean((Boolean) objectValue);
} else if (objectValue instanceof JsonNode) {
jgen.writeTree((JsonNode) objectValue);
} else if (objectValue.getClass().isArray()) {
jgen.writeStartArray();
int length = Array.getLength(objectValue);
for (int i = 0; i < length; i++) {
buildJson(jgen, Array.get(objectValue, i));
}
jgen.writeEndArray();
} else if (objectValue instanceof Iterable) {
Iterable data = (Iterable) objectValue;
jgen.writeStartArray();
for (Object objectEntry : data) {
buildJson(jgen, objectEntry);
}
jgen.writeEndArray();
} else if (objectValue instanceof TabularData) {
TabularData data = (TabularData) objectValue;
jgen.writeStartArray();
for (Object objectEntry : data.values()) {
buildJson(jgen, objectEntry);
}
jgen.writeEndArray();
} else if (objectValue instanceof CompositeData) {
CompositeData data = (CompositeData) objectValue;
jgen.writeStartObject();
for (String objectEntry : data.getCompositeType().keySet()) {
jgen.writeFieldName(objectEntry);
buildJson(jgen, data.get(objectEntry));
}
jgen.writeEndObject();
} else if (objectValue instanceof Number) {
Double data = ((Number) objectValue).doubleValue();
if (data.isNaN()) {
jgen.writeString("NaN");
} else if (data.isInfinite()) {
jgen.writeString("Infinity");
} else {
jgen.writeNumber(((Number) objectValue).toString());
}
} else {
try {
String input = objectValue.toString();
List<JsonNode> parts = new ArrayList<JsonNode>();
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getFactory().createParser(input);
for (parser.nextToken(); parser.hasCurrentToken(); parser.nextToken()) {
JsonToken tok = parser.getCurrentToken();
long pos = parser.getTokenLocation().getCharOffset();
if (tok == JsonToken.START_OBJECT || tok == JsonToken.START_ARRAY) {
parser.skipChildren();
}
long end = parser.getTokenLocation().getCharOffset()
+ parser.getTextLength()
+ (tok == JsonToken.VALUE_STRING ? 2 : 0);
parts.add(mapper.readTree(input.substring((int) pos, (int) end)));
}
if (parts.isEmpty()) {
jgen.writeString("");
} else if (parts.size() == 1) {
buildJson(jgen, parts.get(0));
} else {
buildJson(jgen, parts);
}
} catch (JsonParseException e) {
jgen.writeString(objectValue.toString());
}
}
}
}