Skip to content

Commit

Permalink
7393: Add vanilla Java JSON serializer for IItemCollections
Browse files Browse the repository at this point in the history
Reviewed-by: hirt, jpbempel
  • Loading branch information
cimi authored and thegreystone committed Sep 9, 2021
1 parent ff29931 commit 2844db9
Show file tree
Hide file tree
Showing 5 changed files with 6,704 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Datadog, Inc. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The contents of this file are subject to the terms of either the Universal Permissive License
* v 1.0 as shown at http://oss.oracle.com/licenses/upl
*
* or the following license:
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.openjdk.jmc.flightrecorder.serializers.json;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.openjdk.jmc.common.IDescribable;
import org.openjdk.jmc.common.IMCFrame;
import org.openjdk.jmc.common.IMCMethod;
import org.openjdk.jmc.common.IMCStackTrace;
import org.openjdk.jmc.common.item.IAccessorKey;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemIterable;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.IType;
import org.openjdk.jmc.common.item.ItemToolkit;

/**
* Utility methods to convert an IItemCollection to a JSON object containing the serialised array of
* events.
*/
public class IItemCollectionJsonSerializer extends JsonWriter {
private final static Logger LOGGER = Logger.getLogger("org.openjdk.jmc.flightrecorder.json");

public static String toJsonString(IItemCollection items) {
StringWriter sw = new StringWriter();
IItemCollectionJsonSerializer marshaller = new IItemCollectionJsonSerializer(sw);
try {
marshaller.writeRecording(items);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Failed to serialize recording to JSON", e);
}
return sw.getBuffer().toString();
}

public static String toJsonString(Iterable<IItem> items) {
StringWriter sw = new StringWriter();
IItemCollectionJsonSerializer marshaller = new IItemCollectionJsonSerializer(sw);
try {
marshaller.writeEvents(items);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Failed to serialize items to JSON", e);
}
return sw.getBuffer().toString();
}

private IItemCollectionJsonSerializer(Writer w) {
super(w);
}

private void writeRecording(IItemCollection recording) throws IOException {
writeObjectBegin();
nextField(true, "events");
writeArrayBegin();
int count = 0;
for (IItemIterable events : recording) {
for (IItem event : events) {
nextElement(count == 0);
writeEvent(event);
count++;
}
}
writeArrayEnd();
writeObjectEnd();
flush();
}

void writeEvents(Iterable<IItem> events) throws IOException {
writeObjectBegin();
nextField(true, "events");
writeArrayBegin();
int count = 0;
for (IItem event : events) {
nextElement(count == 0);
writeEvent(event);
count++;
}
writeArrayEnd();
writeObjectEnd();
flush();
}

private void writeEvent(IItem event) {
writeObjectBegin();
IType<?> type = event.getType();
writeField(true, "eventType", type.getIdentifier());
nextField(false, "attributes");
writeObjectBegin();
writeEventAttributes(event);
writeObjectEnd();
writeObjectEnd();
}

private void writeStackTrace(boolean first, IMCStackTrace trace) {
nextField(first, "stackTrace");
writeObjectBegin();
nextField(true, "frames");
writeArrayBegin();
boolean firstFrame = true;
for (IMCFrame frame : trace.getFrames()) {
nextElement(firstFrame);
writeFrame(frame); //$NON-NLS-1$
firstFrame = false;
}
writeArrayEnd();
writeObjectEnd();
}

private void writeFrame(IMCFrame frame) {
Integer lineNumber = frame.getFrameLineNumber();
IMCMethod method = frame.getMethod();

writeObjectBegin();
writeField(true, "name", method != null ? stringifyMethod(method) : null);
writeField(false, "line", lineNumber);
writeField(false, "type", frame.getType());
writeObjectEnd();
}

private void writeEventAttributes(IItem event) {
IType<IItem> itemType = ItemToolkit.getItemType(event);
boolean first = true;
for (Map.Entry<IAccessorKey<?>, ? extends IDescribable> e : itemType.getAccessorKeys().entrySet()) {
IMemberAccessor<?, IItem> accessor = itemType.getAccessor(e.getKey());
IAccessorKey<?> attribute = e.getKey();
Object value = accessor.getMember(event);
if (value instanceof IMCStackTrace) {
writeStackTrace(first, (IMCStackTrace) value);
} else {
writeField(first, attribute.getIdentifier(), value);
}
first = false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Datadog, Inc. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The contents of this file are subject to the terms of either the Universal Permissive License
* v 1.0 as shown at http://oss.oracle.com/licenses/upl
*
* or the following license:
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.openjdk.jmc.flightrecorder.serializers.json;

import java.io.Writer;

/**
* This class provides a set of utility methods for serialising arbitrary objects to JSON, with
* special logic for stringifying JMC types and Java primitives inherited from
* {@link StructuredWriter}.
* <p/>
* It's a slightly modified version of the `jfr` command's JSONWriter.
* <p/>
*
* @see <a href=
* "https://github.com/openjdk/jdk11/blob/master/src/jdk.jfr/share/classes/jdk/jfr/internal/cmd/JSONWriter.java">jdk.jfr.internal.cmd.JSONWriter</a>
*/
abstract class JsonWriter extends StructuredWriter {

JsonWriter(Writer p) {
super(p);
}

protected void writeField(boolean first, String fieldName, Object value) {
nextField(first, fieldName);
if (!writeIfNull(value)) {
if (value instanceof Boolean) {
writeAsString(value);
return;
}
if (value instanceof Double) {
double dValue = (Double) value;
if (Double.isNaN(dValue) || Double.isInfinite(dValue)) {
writeNull();
return;
}
writeAsString(value);
return;
}
if (value instanceof Float) {
float fValue = (Float) value;
if (Float.isNaN(fValue) || Float.isInfinite(fValue)) {
writeNull();
return;
}
writeAsString(value);
return;
}
if (value instanceof Number) {
write(stringify("", value, false));
return;
}
writeStringValue(stringify(value));
}
}

protected void nextElement(boolean first) {
if (!first) {
write(", ");
}
}

protected void nextField(boolean first, String fieldName) {
if (!first) {
writeln(", ");
}
writeFieldName(fieldName);
}

protected boolean writeIfNull(Object value) {
if (value == null) {
writeNull();
return true;
}
return false;
}

protected void writeNull() {
write("null");
}

protected void writeObjectBegin() {
writeln("{");
indent();
}

protected void writeObjectEnd() {
retract();
writeln();
writeIndent();
write("}");
}

protected void writeArrayEnd() {
write("]");
}

protected void writeArrayBegin() {
write("[");
}

protected void writeStringValue(String value) {
write("\"");
writeEscaped(value);
write("\"");
}

private void writeFieldName(String text) {
writeIndent();
write("\"");
write(text);
write("\": ");
}

private void writeEscaped(String text) {
for (int i = 0; i < text.length(); i++) {
writeEscaped(text.charAt(i));
}
}

private void writeEscaped(char c) {
if (c == '\b') {
write("\\b");
return;
}
if (c == '\n') {
write("\\n");
return;
}
if (c == '\t') {
write("\\t");
return;
}
if (c == '\f') {
write("\\f");
return;
}
if (c == '\r') {
write("\\r");
return;
}
if (c == '\"') {
write("\\\"");
return;
}
if (c == '\\') {
write("\\\\");
return;
}
/*
* we don't need to escape slashes if (c == '/') { print("\\/"); return; }
*/
if (c > 0x7F || c < 32) {
write("\\u");
// 0x10000 will pad with zeros.
write(Integer.toHexString(0x10000 + c).substring(1));
return;
}
write(c);
}
}
Loading

0 comments on commit 2844db9

Please sign in to comment.