summaryrefslogtreecommitdiff
path: root/qpid/tools/src/java/qpid-qmf2-rest/src/main/java/org/apache/qpid/restapi/JSON.java
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/tools/src/java/qpid-qmf2-rest/src/main/java/org/apache/qpid/restapi/JSON.java')
-rw-r--r--qpid/tools/src/java/qpid-qmf2-rest/src/main/java/org/apache/qpid/restapi/JSON.java265
1 files changed, 265 insertions, 0 deletions
diff --git a/qpid/tools/src/java/qpid-qmf2-rest/src/main/java/org/apache/qpid/restapi/JSON.java b/qpid/tools/src/java/qpid-qmf2-rest/src/main/java/org/apache/qpid/restapi/JSON.java
new file mode 100644
index 0000000000..dfc1e0c85d
--- /dev/null
+++ b/qpid/tools/src/java/qpid-qmf2-rest/src/main/java/org/apache/qpid/restapi/JSON.java
@@ -0,0 +1,265 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ */
+package org.apache.qpid.restapi;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+// QMF2 imports
+import org.apache.qpid.qmf2.common.Handle;
+import org.apache.qpid.qmf2.common.ObjectId;
+import org.apache.qpid.qmf2.common.QmfData;
+import org.apache.qpid.qmf2.common.QmfDescribed;
+import org.apache.qpid.qmf2.common.SchemaClassId;
+import org.apache.qpid.qmf2.common.WorkItem;
+import org.apache.qpid.qmf2.console.QmfConsoleData;
+
+/**
+ * This class provides a number of convenience methods to serialise and deserialise JSON strings to/from Java
+ * Collections or QmfData objects.
+ *
+ * The JSONMapParser class used here is largely a direct copy of org.apache.qpid.messaging.util.AddressParser
+ * as it provides a handy mechanism to parse a JSON String into a Map which is the only JSON requirement that
+ * we really need for QMF. Originally this code simply did "import org.apache.qpid.messaging.util.AddressParser;"
+ * but there's a restriction/bug on the core AddressParser whereby it serialises integers into Java Integer
+ * which means that long integer values aren't correctly stored. It's this restriction that gives Java Address
+ * Strings a defacto 2GB queue size. I should really provide a patch for the *real* AddressParser but it's better
+ * to add features covering "shorthand" forms for large values (e.g. k/K, m/M, g/G for kilo, mega, giga etc.)
+ * to both the Java and C++ AddressParser to ensure maximum consistency.
+ *
+ * Because the JSON requirements for the REST API are relatively modest a fairly simple serialisation/deserialisation
+ * mechanism is included here and in the modified AddressParser classes rather than incorporating a full-blown
+ * Java JSON parser. This helps avoid a bot of bloat and keeps dependencies limited to the core qpid classes.
+ *
+ * @author Fraser Adams
+ */
+public final class JSON
+{
+ /**
+ * Serialise an Object to JSON. Note this isn't a full JSON serialisation of java.lang.Object, rather it only
+ * includes types that are relevant to QmfData Objects and the types that may be contained therein.
+ * @param item the Object that we wish to serialise to JSON.
+ * @return the JSON String encoding.
+ */
+ public final static String fromObject(final Object item)
+ {
+ if (item == null)
+ {
+ return "";
+ }
+ else
+ {
+ if (item instanceof Map)
+ { // Check if the value part is an ObjectId and serialise appropriately
+ Map map = (Map)item;
+ if (map.containsKey("_object_name"))
+ { // Serialise "ref" properties as String versions of ObjectId to match encoding used in fromQmfData()
+ return "\"" + new ObjectId(map) + "\"";
+ }
+ else
+ {
+ return fromMap(map);
+ }
+ }
+ else if (item instanceof List)
+ {
+ return fromList((List)item);
+ }
+ else if (item instanceof QmfData)
+ {
+ return fromQmfData((QmfData)item);
+ }
+ else if (item instanceof WorkItem)
+ {
+ return fromWorkItem((WorkItem)item);
+ }
+ else if (item instanceof String)
+ {
+ return "\"" + item + "\"";
+ }
+ else if (item instanceof byte[])
+ {
+ return "\"" + new String((byte[])item) + "\"";
+ }
+ else if (item instanceof UUID)
+ {
+ return "\"" + item.toString() + "\"";
+ }
+ else
+ {
+ return item.toString();
+ }
+ }
+ }
+
+ /**
+ * Encode the Map contents so we can use the same code for fromMap and fromQmfData as the latter also needs
+ * to encode _object_id and _schema_id. This returns a String that needs to be topped and tailed with braces.
+ * @param m the Map that we wish to serialise to JSON.
+ * @return the String encoding of the contents.
+ */
+ @SuppressWarnings("unchecked")
+ private final static String encodeMapContents(final Map m)
+ {
+ Map<String, Object> map = (Map<String, Object>)m;
+ StringBuilder buffer = new StringBuilder(512);
+ int size = map.size();
+ int count = 1;
+ for (Map.Entry<String, Object> entry : map.entrySet())
+ {
+ String key = (String)entry.getKey();
+ buffer.append("\"" + key + "\":");
+
+ Object value = entry.getValue();
+ buffer.append(fromObject(value));
+ if (count++ < size)
+ {
+ buffer.append(",");
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Serialise a Map to JSON.
+ * @param m the Map that we wish to serialise to JSON.
+ * @return the JSON String encoding.
+ */
+ @SuppressWarnings("unchecked")
+ public final static String fromMap(final Map m)
+ {
+ return "{" + encodeMapContents(m) + "}";
+ }
+
+ /**
+ * Serialise a List to JSON.
+ * @param list the List that we wish to serialise to JSON.
+ * @return the JSON String encoding.
+ */
+ public final static String fromList(final List list)
+ {
+ StringBuilder buffer = new StringBuilder(512);
+ buffer.append("[");
+ int size = list.size();
+ int count = 1;
+ for (Object item : list)
+ {
+ buffer.append(fromObject(item));
+ if (count++ < size)
+ {
+ buffer.append(",");
+ }
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+ /**
+ * Serialise a QmfData Object to JSON. If the Object is a QmfConsoleData we serialise the ObjectId as a String
+ * which is the same encoding used for the various "ref" properies in fromObject().
+ * @param data the QmfData that we wish to serialise to JSON.
+ * @return the JSON String encoding.
+ */
+ public final static String fromQmfData(final QmfData data)
+ {
+ String consoleDataInfo = "";
+
+ if (data instanceof QmfConsoleData)
+ {
+ QmfConsoleData consoleData = (QmfConsoleData)data;
+ SchemaClassId sid = consoleData.getSchemaClassId();
+ long[] ts = consoleData.getTimestamps();
+
+ String objectId = "\"_object_id\":\"" + consoleData.getObjectId().toString() + "\",";
+ String schemaId = "\"_schema_id\":{" +
+ "\"_package_name\":\"" + sid.getPackageName() +
+ "\",\"_class_name\":\"" + sid.getClassName() +
+ "\",\"_type\":\"" + sid.getType() +
+ "\",\"_hash\":\"" + sid.getHashString() +
+ "\"},";
+
+ String timestamps = "\"_update_ts\":" + ts[0] + "," +
+ "\"_create_ts\":" + ts[1] + "," +
+ "\"_delete_ts\":" + ts[2] + ",";
+
+ consoleDataInfo = objectId + schemaId + timestamps;
+ }
+
+ return "{" + consoleDataInfo + encodeMapContents(data.mapEncode()) + "}";
+ }
+
+ /**
+ * Serialise a WorkItem Object to JSON.
+ * @param data the WorkItem that we wish to serialise to JSON.
+ * @return the JSON String encoding.
+ */
+ public final static String fromWorkItem(final WorkItem data)
+ {
+ // TODO There are a couple of WorkItem types that won't serialise correctly - SubscriptionIndicationWorkItem
+ // and MethodCallWorkItem. Their params require a custom serialiser - though they probably won't be used
+ // from a REST API so they've been parked for now.
+ String type = "\"_type\":\"" + data.getType() + "\",";
+ Handle handle = data.getHandle();
+ String handleString = handle == null ? "" : "\"_handle\":\"" + handle.getCorrelationId() + "\",";
+ String params = "\"_params\":" + fromObject(data.getParams());
+
+ return "{" + type + handleString + params + "}";
+ }
+
+ /**
+ * Create a Map from a JSON String.
+ * The JSONMapParser class used here is largely a direct copy of org.apache.qpid.messaging.util.AddressParser
+ * as it provides a handy mechanism to parse a JSON String into a Map which is the only JSON requirement that
+ * we really need for QMF. Originally this code simply did "import org.apache.qpid.messaging.util.AddressParser;"
+ * but there's a restriction/bug on the core AddressParser whereby it serialises integers into Java Integer
+ * which means that long integer values aren't correctly stored. It's this restriction that gives Java Address
+ * Strings a defacto 2GB queue size. I should really provide a patch for the *real* AddressParser but it's better
+ * to add features covering "shorthand" forms for large values (e.g. k/K, m/M, g/G for kilo, mega, giga etc.)
+ * to both the Java and C++ AddressParser to ensure maximum consistency.
+ * @param json the JSON String that we wish to decode into a Map.
+ * @return the Map encoding of the JSON String.
+ */
+ public final static Map toMap(final String json)
+ {
+ if (json == null || json.equals(""))
+ {
+ return Collections.EMPTY_MAP;
+ }
+ else
+ {
+ return new JSONMapParser(json).map();
+ }
+ }
+
+ /**
+ * Create a QmfData from a JSON String.
+ * @param json the JSON String that we wish to decode into a QmfData.
+ * @return the QmfData encoding of the JSON String.
+ */
+ public final static QmfData toQmfData(final String json)
+ {
+ return new QmfData(toMap(json));
+ }
+}
+
+