diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseDataExportTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseDataExportTest.java index 5fb6cfae2055..0dbb0ed99abc 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseDataExportTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/tools/pulse/PulseDataExportTest.java @@ -57,6 +57,6 @@ public void dataBrowserExportWorksAsExpected() throws Exception { .hasStatusCode(200) .hasResponseBody() .isEqualToIgnoringWhitespace( - "{\"result\":[[\"java.lang.String\",\"value1\"],[\"java.lang.String\",\"value2\"],[\"java.lang.String\",\"value3\"]]}"); + "{\"result\":[\"value1\",\"value2\",\"value3\"]}"); } } diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/QueryDataDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/QueryDataDUnitTest.java index e878e6b001e4..fa037cd3aa0c 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/management/QueryDataDUnitTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/management/QueryDataDUnitTest.java @@ -63,7 +63,7 @@ import org.apache.geode.internal.cache.partitioned.fixed.SingleHopQuarterPartitionResolver; import org.apache.geode.management.internal.SystemManagementService; import org.apache.geode.management.internal.beans.BeanUtilFuncs; -import org.apache.geode.management.internal.cli.json.TypedJson; +import org.apache.geode.management.internal.beans.QueryDataFunction; import org.apache.geode.pdx.PdxInstance; import org.apache.geode.pdx.PdxInstanceFactory; import org.apache.geode.pdx.internal.PdxInstanceFactoryImpl; @@ -237,7 +237,7 @@ public void testLimitForQuery() throws Exception { // Query With Default values assertThat(distributedSystemMXBean.getQueryCollectionsDepth()) - .isEqualTo(TypedJson.DEFAULT_COLLECTION_ELEMENT_LIMIT); + .isEqualTo(QueryDataFunction.DEFAULT_COLLECTION_ELEMENT_LIMIT); assertThat(distributedSystemMXBean.getQueryResultSetLimit()).isEqualTo(DEFAULT_QUERY_LIMIT); String jsonString = distributedSystemMXBean.queryData(QUERIES_FOR_LIMIT[0], null, 0); diff --git a/geode-core/src/integrationTest/java/org/apache/geode/cache/query/TypedJsonQueryIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/cache/query/QueryResultFormatterQueryIntegrationTest.java similarity index 65% rename from geode-core/src/integrationTest/java/org/apache/geode/cache/query/TypedJsonQueryIntegrationTest.java rename to geode-core/src/integrationTest/java/org/apache/geode/cache/query/QueryResultFormatterQueryIntegrationTest.java index 98992c6733fe..1e9373c9a1fb 100644 --- a/geode-core/src/integrationTest/java/org/apache/geode/cache/query/TypedJsonQueryIntegrationTest.java +++ b/geode-core/src/integrationTest/java/org/apache/geode/cache/query/QueryResultFormatterQueryIntegrationTest.java @@ -24,21 +24,21 @@ import org.apache.geode.cache.util.ObjectSizer; import org.apache.geode.management.internal.cli.json.GfJsonException; import org.apache.geode.management.internal.cli.json.GfJsonObject; -import org.apache.geode.management.internal.cli.json.TypedJson; -import org.apache.geode.management.internal.cli.json.TypedJsonPdxIntegrationTest; +import org.apache.geode.management.internal.cli.json.QueryResultFormatter; +import org.apache.geode.management.internal.cli.json.QueryResultFormatterPdxIntegrationTest; import org.apache.geode.test.junit.categories.OQLQueryTest; /** - * Integration tests for {@link TypedJson} querying {@link Portfolio}. + * Integration tests for {@link QueryResultFormatter} querying {@link Portfolio}. *

* - * Extracted from {@link TypedJsonPdxIntegrationTest}. + * Extracted from {@link QueryResultFormatterPdxIntegrationTest}. *

* * TODO: add real assertions */ @Category({OQLQueryTest.class}) -public class TypedJsonQueryIntegrationTest { +public class QueryResultFormatterQueryIntegrationTest { private static final String RESULT = "result"; @@ -46,18 +46,19 @@ public class TypedJsonQueryIntegrationTest { public void testUserObject() throws Exception { Portfolio p = new Portfolio(2); - TypedJson typedJson = new TypedJson(RESULT, p); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, p); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test public void testUserObjectArray() throws Exception { Portfolio[] portfolios = createPortfoliosAndPositions(2); - TypedJson typedJson = new TypedJson(RESULT, portfolios); + QueryResultFormatter queryResultFormatter = + new QueryResultFormatter(100).add(RESULT, portfolios); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test @@ -65,24 +66,25 @@ public void testMemUsage() throws Exception { Portfolio[] portfolios = createPortfoliosAndPositions(1000); System.out.println("Size Of port " + ObjectSizer.REFLECTION_SIZE.sizeof(portfolios)); - TypedJson typedJson = new TypedJson(RESULT, portfolios); - System.out.println("Size Of json " + ObjectSizer.REFLECTION_SIZE.sizeof(typedJson)); + QueryResultFormatter queryResultFormatter = + new QueryResultFormatter(100).add(RESULT, portfolios); + System.out.println("Size Of json " + ObjectSizer.REFLECTION_SIZE.sizeof(queryResultFormatter)); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test public void testQueryLike() throws Exception { Portfolio[] portfolios = createPortfoliosAndPositions(2); - TypedJson typedJson = new TypedJson(RESULT, null); - typedJson.add("member", "server1"); - // checkResult(typedJson); -- fails + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, null); + queryResultFormatter.add("member", "server1"); + // checkResult(queryResultFormatter); -- fails for (int i = 0; i < 2; i++) { - typedJson.add(RESULT, portfolios[i]); + queryResultFormatter.add(RESULT, portfolios[i]); } - checkResult(typedJson); + checkResult(queryResultFormatter); } private Portfolio[] createPortfoliosAndPositions(final int count) { @@ -94,8 +96,8 @@ private Portfolio[] createPortfoliosAndPositions(final int count) { return portfolios; } - private void checkResult(final TypedJson typedJson) throws GfJsonException { - GfJsonObject gfJsonObject = new GfJsonObject(typedJson.toString()); + private void checkResult(final QueryResultFormatter queryResultFormatter) throws GfJsonException { + GfJsonObject gfJsonObject = new GfJsonObject(queryResultFormatter.toString()); System.out.println(gfJsonObject); assertThat(gfJsonObject.get(RESULT)).isNotNull(); } diff --git a/geode-core/src/integrationTest/java/org/apache/geode/management/DataQueryEngineIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/management/DataQueryEngineIntegrationTest.java index 6a55a2b818c1..43be476df675 100644 --- a/geode-core/src/integrationTest/java/org/apache/geode/management/DataQueryEngineIntegrationTest.java +++ b/geode-core/src/integrationTest/java/org/apache/geode/management/DataQueryEngineIntegrationTest.java @@ -32,7 +32,7 @@ import org.apache.geode.management.internal.ManagementConstants; import org.apache.geode.management.internal.SystemManagementService; import org.apache.geode.management.internal.beans.DataQueryEngine; -import org.apache.geode.management.internal.cli.json.TypedJson; +import org.apache.geode.management.internal.beans.QueryDataFunction; import org.apache.geode.management.model.EmptyObject; import org.apache.geode.management.model.Item; import org.apache.geode.management.model.Order; @@ -61,7 +61,8 @@ public class DataQueryEngineIntegrationTest { * Number of elements to be shown in queryData operation if query results contain collections like * Map, List etc. */ - private static final int queryCollectionsDepth = TypedJson.DEFAULT_COLLECTION_ELEMENT_LIMIT; + private static final int queryCollectionsDepth = + QueryDataFunction.DEFAULT_COLLECTION_ELEMENT_LIMIT; @Rule public ServerStarterRule server = new ServerStarterRule().withNoCacheServer() @@ -97,7 +98,7 @@ public void testCyclicWithNestedObjectReference() throws Exception { region.put("order1", order); String expectedResult = - "{\"result\":[[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{\"0\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_1\"],\"order\":[\"org.apache.geode.management.model.Order\",\"org.apache.geode.management.model.Order\"]}],\"1\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_2\"],\"order\":[\"org.apache.geode.management.model.Order\",\"org.apache.geode.management.model.Order\"]}],\"2\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_3\"],\"order\":[\"org.apache.geode.management.model.Order\",\"org.apache.geode.management.model.Order\"]}],\"3\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_4\"],\"order\":[\"org.apache.geode.management.model.Order\",\"org.apache.geode.management.model.Order\"]}],\"4\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_5\"],\"order\":[\"org.apache.geode.management.model.Order\",\"org.apache.geode.management.model.Order\"]}]}],\"id\":[\"java.lang.String\",\"test\"]}]]}"; + "{\"result\":[[\"org.apache.geode.management.model.Order\",{\"id\":\"test\",\"items\":[\"java.util.ArrayList\",{\"0\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_1\",\"order\":\"duplicate org.apache.geode.management.model.Order\"}],\"1\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_2\",\"order\":\"duplicate org.apache.geode.management.model.Order\"}],\"2\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_3\",\"order\":\"duplicate org.apache.geode.management.model.Order\"}],\"3\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_4\",\"order\":\"duplicate org.apache.geode.management.model.Order\"}],\"4\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_5\",\"order\":\"duplicate org.apache.geode.management.model.Order\"}]}]}]]}"; Object result = queryEngine.queryForJsonResult(QUERY_1, 0, queryResultSetLimit, queryCollectionsDepth); String queryResult = (String) result; @@ -130,7 +131,7 @@ public void testCyclicWithNestedClasses() throws Exception { queryCollectionsDepth); String expectedResult = - "{\"result\":[[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{\"0\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_1\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"ORDER_ID_1\"]}]}],\"1\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_2\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"ORDER_ID_2\"]}]}],\"2\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_3\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"ORDER_ID_3\"]}]}],\"3\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_4\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"ORDER_ID_4\"]}]}],\"4\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_5\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"ORDER_ID_5\"]}]}]}],\"id\":[\"java.lang.String\",\"test\"]}]]}"; + "{\"result\":[[\"org.apache.geode.management.model.Order\",{\"id\":\"test\",\"items\":[\"java.util.ArrayList\",{\"0\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_1\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_1\",\"items\":[\"java.util.ArrayList\",{}]}]}],\"1\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_2\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_2\",\"items\":[\"java.util.ArrayList\",{}]}]}],\"2\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_3\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_3\",\"items\":[\"java.util.ArrayList\",{}]}]}],\"3\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_4\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_4\",\"items\":[\"java.util.ArrayList\",{}]}]}],\"4\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_5\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_5\",\"items\":[\"java.util.ArrayList\",{}]}]}]}]}]]}"; assertThat(queryResult).isEqualToIgnoringWhitespace(expectedResult); // If not correct JSON format this will throw a JSONException @@ -160,7 +161,7 @@ public void testCyclicWithNestedRefernce2ndLayer() throws Exception { String queryResult = queryEngine.queryForJsonResult(QUERY_1, 0, queryResultSetLimit, queryCollectionsDepth); String expectedResult = - "{\"result\":[[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",{\"0\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_1\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",\"java.util.ArrayList\"],\"id\":[\"java.lang.String\",\"ORDER_ID_1\"]}]}],\"1\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_2\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",\"java.util.ArrayList\"],\"id\":[\"java.lang.String\",\"ORDER_ID_2\"]}]}],\"2\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_3\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",\"java.util.ArrayList\"],\"id\":[\"java.lang.String\",\"ORDER_ID_3\"]}]}],\"3\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_4\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",\"java.util.ArrayList\"],\"id\":[\"java.lang.String\",\"ORDER_ID_4\"]}]}],\"4\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":[\"java.lang.String\",\"Book\"],\"itemId\":[\"java.lang.String\",\"ID_5\"],\"order\":[\"org.apache.geode.management.model.Order\",{\"items\":[\"java.util.Collection\",\"java.util.ArrayList\"],\"id\":[\"java.lang.String\",\"ORDER_ID_5\"]}]}]}],\"id\":[\"java.lang.String\",\"ORDER_TEST\"]}]]}"; + "{\"result\":[[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_TEST\",\"items\":[\"java.util.ArrayList\",{\"0\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_1\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_1\",\"items\":[\"java.util.ArrayList\",{\"0\":\"duplicate org.apache.geode.management.model.Item\",\"1\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_2\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_2\",\"items\":[\"java.util.ArrayList\",{\"0\":\"duplicate org.apache.geode.management.model.Item\",\"1\":\"duplicate org.apache.geode.management.model.Item\",\"2\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_3\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_3\",\"items\":[\"java.util.ArrayList\",{\"0\":\"duplicate org.apache.geode.management.model.Item\",\"1\":\"duplicate org.apache.geode.management.model.Item\",\"2\":\"duplicate org.apache.geode.management.model.Item\",\"3\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_4\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_4\",\"items\":[\"java.util.ArrayList\",{\"0\":\"duplicate org.apache.geode.management.model.Item\",\"1\":\"duplicate org.apache.geode.management.model.Item\",\"2\":\"duplicate org.apache.geode.management.model.Item\",\"3\":\"duplicate org.apache.geode.management.model.Item\",\"4\":[\"org.apache.geode.management.model.Item\",{\"itemDescription\":\"Book\",\"itemId\":\"ID_5\",\"order\":[\"org.apache.geode.management.model.Order\",{\"id\":\"ORDER_ID_5\",\"items\":[\"java.util.ArrayList\",{\"0\":\"duplicate org.apache.geode.management.model.Item\",\"1\":\"duplicate org.apache.geode.management.model.Item\",\"2\":\"duplicate org.apache.geode.management.model.Item\",\"3\":\"duplicate org.apache.geode.management.model.Item\",\"4\":\"duplicate org.apache.geode.management.model.Item\"}]}]}]}]}]}],\"4\":\"duplicate org.apache.geode.management.model.Item\"}]}]}],\"3\":\"duplicate org.apache.geode.management.model.Item\",\"4\":\"duplicate org.apache.geode.management.model.Item\"}]}]}],\"2\":\"duplicate org.apache.geode.management.model.Item\",\"3\":\"duplicate org.apache.geode.management.model.Item\",\"4\":\"duplicate org.apache.geode.management.model.Item\"}]}]}],\"1\":\"duplicate org.apache.geode.management.model.Item\",\"2\":\"duplicate org.apache.geode.management.model.Item\",\"3\":\"duplicate org.apache.geode.management.model.Item\",\"4\":\"duplicate org.apache.geode.management.model.Item\"}]}]]}"; assertThat(queryResult).isEqualToIgnoringWhitespace(expectedResult); // If not correct JSON format this will throw a JSONException @@ -276,7 +277,7 @@ public void testSubClassOverridingMethods() throws Exception { String queryResult = queryEngine.queryForJsonResult(QUERY_1, 0, queryResultSetLimit, queryCollectionsDepth); String expectedResult = - "{\"result\":[[\"org.apache.geode.management.model.SubOrder\",{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"null1\"]}]]}"; + "{\"result\":[[\"org.apache.geode.management.model.SubOrder\",{\"id\":\"null1\",\"items\":[\"java.util.ArrayList\",{}]}]]}"; assertThat(queryResult).isEqualToIgnoringWhitespace(expectedResult); // If not correct JSON format this will throw a JSONException @@ -299,7 +300,7 @@ public void testNestedPDXObject() throws Exception { String queryResult = queryEngine.queryForJsonResult(QUERY_1, 0, queryResultSetLimit, queryCollectionsDepth); String expectedResult = - "{\"result\":[[\"org.apache.geode.pdx.PdxInstance\",{\"ID\":[\"java.lang.Integer\",111],\"status\":[\"java.lang.String\",\"active\"],\"secId\":[\"java.lang.String\",\"IBM\"]}]]}"; + "{\"result\":[[\"org.apache.geode.pdx.internal.PdxInstanceImpl\",{\"ID\":111,\"status\":\"active\",\"secId\":\"IBM\"}]]}"; assertThat(queryResult).isEqualToIgnoringWhitespace(expectedResult); // If not correct JSON format this will throw a JSONException @@ -317,7 +318,7 @@ public void testArrayWithNullValues() throws Exception { String queryResult = queryEngine.queryForJsonResult(QUERY_1, 0, queryResultSetLimit, queryCollectionsDepth); String expectedResult = - "{\"result\":[[\"org.apache.geode.management.model.SubOrder[]\",[{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"null1\"]},null]]]}"; + "{\"result\":[[\"[Lorg.apache.geode.management.model.SubOrder;\",[[\"org.apache.geode.management.model.SubOrder\",{\"id\":\"null1\",\"items\":[\"java.util.ArrayList\",{}]}],null]]]}"; assertThat(queryResult).isEqualToIgnoringWhitespace(expectedResult); // If not correct JSON format this will throw a JSONException @@ -335,7 +336,7 @@ public void testWithSqlDate() throws Exception { String queryResult = queryEngine.queryForJsonResult(QUERY_1, 0, queryResultSetLimit, queryCollectionsDepth); String expectedResult = - "{\"result\":[[\"org.apache.geode.management.model.SubOrder[]\",[{\"items\":[\"java.util.Collection\",{}],\"id\":[\"java.lang.String\",\"null1\"]},null]]]}"; + "{\"result\":[[\"[Lorg.apache.geode.management.model.SubOrder;\",[[\"org.apache.geode.management.model.SubOrder\",{\"id\":\"null1\",\"items\":[\"java.util.ArrayList\",{}]}],null]]]}"; assertThat(queryResult).isEqualToIgnoringWhitespace(expectedResult); // If not correct JSON format this will throw a JSONException diff --git a/geode-core/src/integrationTest/java/org/apache/geode/management/internal/cli/json/TypedJsonPdxIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/management/internal/cli/json/QueryResultFormatterPdxIntegrationTest.java similarity index 85% rename from geode-core/src/integrationTest/java/org/apache/geode/management/internal/cli/json/TypedJsonPdxIntegrationTest.java rename to geode-core/src/integrationTest/java/org/apache/geode/management/internal/cli/json/QueryResultFormatterPdxIntegrationTest.java index bead9e680250..dfe9f8cb6ad2 100644 --- a/geode-core/src/integrationTest/java/org/apache/geode/management/internal/cli/json/TypedJsonPdxIntegrationTest.java +++ b/geode-core/src/integrationTest/java/org/apache/geode/management/internal/cli/json/QueryResultFormatterPdxIntegrationTest.java @@ -34,12 +34,12 @@ import org.apache.geode.pdx.internal.PdxInstanceFactoryImpl; /** - * Integration tests for {@link TypedJson}. + * Integration tests for {@link QueryResultFormatter}. *

* * TODO: add actual assertions */ -public class TypedJsonPdxIntegrationTest { +public class QueryResultFormatterPdxIntegrationTest { private static final String RESULT = "result"; @@ -71,9 +71,10 @@ public void supportsPdxInstance() throws Exception { pdxInstanceFactory.writeObject("object", new SerializableObject(2)); PdxInstance pdxInstance = pdxInstanceFactory.create(); - TypedJson typedJson = new TypedJson(RESULT, pdxInstance); + QueryResultFormatter queryResultFormatter = + new QueryResultFormatter(100).add(RESULT, pdxInstance); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test @@ -83,13 +84,14 @@ public void supportsObjectContainingPdxInstance() throws Exception { pdxInstanceFactory.writeString("secId", "IBM"); PdxContainer pdxContainer = new PdxContainer(pdxInstanceFactory.create(), 1); - TypedJson typedJson = new TypedJson(RESULT, pdxContainer); + QueryResultFormatter queryResultFormatter = + new QueryResultFormatter(100).add(RESULT, pdxContainer); - checkResult(typedJson); + checkResult(queryResultFormatter); } - private void checkResult(TypedJson typedJson) throws GfJsonException { - GfJsonObject gfJsonObject = new GfJsonObject(typedJson.toString()); + private void checkResult(QueryResultFormatter queryResultFormatter) throws GfJsonException { + GfJsonObject gfJsonObject = new GfJsonObject(queryResultFormatter.toString()); System.out.println(gfJsonObject); assertThat(gfJsonObject.get(RESULT)).isNotNull(); } diff --git a/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt b/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt index 9886bd5825be..23a7c421ef67 100644 --- a/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt +++ b/geode-core/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt @@ -77,6 +77,7 @@ org/apache/geode/internal/tcp/VersionedByteBufferInputStream org/apache/geode/internal/util/concurrent/StoppableReadWriteLock org/apache/geode/management/internal/cli/commands/ShowMetricsCommand$Category org/apache/geode/management/internal/cli/exceptions/UserErrorException +org/apache/geode/management/internal/cli/json/QueryResultFormatter$PreventReserializationModule org/apache/geode/security/ResourcePermission org/apache/geode/security/ResourcePermission$Operation org/apache/geode/security/ResourcePermission$Resource diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/beans/DistributedSystemBridge.java b/geode-core/src/main/java/org/apache/geode/management/internal/beans/DistributedSystemBridge.java index ff4405ff0f98..6b0d7fbe1af6 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/beans/DistributedSystemBridge.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/beans/DistributedSystemBridge.java @@ -85,7 +85,6 @@ import org.apache.geode.management.internal.beans.stats.MemberClusterStatsMonitor; import org.apache.geode.management.internal.beans.stats.ServerClusterStatsMonitor; import org.apache.geode.management.internal.cli.CliUtil; -import org.apache.geode.management.internal.cli.json.TypedJson; /** * This is the gateway to distributed system as a whole. Aggregated metrics and stats are shown @@ -208,7 +207,7 @@ public class DistributedSystemBridge { * NUmber of elements to be shown in queryData operation if query results contain collections like * Map, List etc. */ - private int queryCollectionsDepth = TypedJson.DEFAULT_COLLECTION_ELEMENT_LIMIT; + private int queryCollectionsDepth = QueryDataFunction.DEFAULT_COLLECTION_ELEMENT_LIMIT; /** * used to issue queries diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/beans/QueryDataFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/beans/QueryDataFunction.java index 997c283e1445..9b1fb91781ea 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/beans/QueryDataFunction.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/beans/QueryDataFunction.java @@ -43,7 +43,7 @@ import org.apache.geode.internal.cache.execute.InternalFunction; import org.apache.geode.internal.logging.LogService; import org.apache.geode.management.internal.ManagementConstants; -import org.apache.geode.management.internal.cli.json.TypedJson; +import org.apache.geode.management.internal.cli.json.QueryResultFormatter; /** * This function is executed on one or multiple members based on the member input to @@ -56,6 +56,11 @@ public class QueryDataFunction implements Function, InternalEntity { private static final Logger logger = LogService.getLogger(); + /** + * Limit of collection length to be serialized in JSON format. + */ + public static final int DEFAULT_COLLECTION_ELEMENT_LIMIT = 100; + private static final String MEMBER_KEY = "member"; private static final String RESULT_KEY = "result"; private static final String NO_DATA_FOUND = "No Data Found"; @@ -113,7 +118,7 @@ private byte[] selectWithType(final FunctionContext context, String queryString, queryString = applyLimitClause(queryString, limit, queryResultSetLimit); try { - TypedJson result = new TypedJson(queryCollectionsDepth); + QueryResultFormatter result = new QueryResultFormatter(queryCollectionsDepth); Region region = cache.getRegion(regionName); diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/QueryResultFormatter.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/QueryResultFormatter.java new file mode 100644 index 000000000000..ef5590e7df3c --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/QueryResultFormatter.java @@ -0,0 +1,456 @@ +/* + * 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.geode.management.internal.cli.json; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonStreamContext; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.type.WritableTypeId; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.ser.PropertyWriter; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; + +import org.apache.geode.cache.Region; +import org.apache.geode.cache.query.internal.StructImpl; +import org.apache.geode.pdx.PdxInstance; + +/** + * A JSON serializer that has special handling for collections to limit the number of elements + * written to the document. It also has special handling for PdxInstance and query Structs. + */ +public class QueryResultFormatter { + + private final ObjectMapper mapper; + + /** + * map contains the named objects to be serialized + */ + private final Map> map; + + /** + * serializedObjects is used to prevent recursive serialization in cases where + * there are cyclical references + */ + private final Map serializedObjects; + + /** + * Create a formatter that will limit collection sizes to maxCollectionElements + * and will limit object traversal to being the same but in depth. + * + * @param maxCollectionElements limit on collection elements and depth-first object traversal + */ + public QueryResultFormatter(int maxCollectionElements) { + this(maxCollectionElements, maxCollectionElements); + } + + /** + * Create a formatter that will limit collection sizes to maxCollectionElements + * + * @param maxCollectionElements limit on collection elements + * @param serializationDepth when traversing objects, how deep should we go? + */ + private QueryResultFormatter(int maxCollectionElements, int serializationDepth) { + this.map = new LinkedHashMap<>(); + + this.serializedObjects = new IdentityHashMap<>(); + this.mapper = new ObjectMapper(); + + SimpleModule mapperModule = + new PreventReserializationModule(serializedObjects, serializationDepth); + + // insert a collection serializer that limits the number of elements generated + mapperModule.addSerializer(Collection.class, new CollectionSerializer(maxCollectionElements)); + // insert a PdxInstance serializer that knows about PDX fields/values + mapperModule.addSerializer(PdxInstance.class, new PdxInstanceSerializer()); + // insert a Struct serializer that knows about its format + mapperModule.addSerializer(StructImpl.class, new StructSerializer()); + // insert a RegionEntry serializer because they're too messy looking + mapperModule.addSerializer(Region.Entry.class, new RegionEntrySerializer()); + + // mapper.setFilterProvider(new SimpleFilterProvider().setDefaultFilter(new DepthFilter(4))); + mapper.registerModule(mapperModule); + + // allow objects with no content + mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + // use toString on Enums + mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + // sort fields alphabetically + mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); + // add type information (Jackson has no way to force it to do this for all values) + mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + + } + + /** + * After instantiating a formatter add the objects you want to be formatted + * using this method. Typically this will be add("result", queryResult) + */ + public synchronized QueryResultFormatter add(String key, Object value) { + List list = this.map.get(key); + if (list != null) { + list.add(value); + } else { + list = new ArrayList<>(); + if (value != null) { + list.add(value); + } + this.map.put(key, list); + } + return this; + } + + /* non-javadoc use Jackson to serialize added objects into JSON format */ + @Override + public synchronized String toString() { + Writer writer = new StringWriter(); + try { + boolean addComma = false; + writer.write('{'); + for (Map.Entry> entry : this.map.entrySet()) { + if (addComma) { + writer.write(','); + } + mapper.writerFor(entry.getKey().getClass()).writeValue(writer, entry.getKey()); + writer.write(':'); + writeList(writer, entry.getValue()); + addComma = true; + } + writer.write('}'); + + return writer.toString(); + } catch (IOException exception) { + new GfJsonException(exception).printStackTrace(); + } catch (GfJsonException e) { + e.printStackTrace(); + } + return null; + } + + + private Writer writeList(Writer writer, List values) throws GfJsonException { + // for each object we clear out the serializedObjects recursion map so that + // we don't immediately see "duplicate" entries + serializedObjects.clear(); + try { + boolean addComma = false; + int length = values.size(); + writer.write('['); + + if (length == 0) { + mapper.writeValue(writer, null); + } else { + for (int i = 0; i < length; i += 1) { + if (addComma) { + writer.write(','); + } + mapper.writerFor(values.get(i).getClass()).writeValue(writer, values.get(i)); + + addComma = true; + } + } + writer.write(']'); + } catch (IOException e) { + throw new GfJsonException(e); + } + return writer; + } + + + private static class PreventReserializationSerializer extends JsonSerializer { + + private JsonSerializer defaultSerializer; + Map serializedObjects; + private final int serializationDepth; + int depth; + + PreventReserializationSerializer(JsonSerializer serializer, Map serializedObjects, + int serializationDepth) { + defaultSerializer = serializer; + this.serializedObjects = serializedObjects; + this.serializationDepth = serializationDepth; + } + + boolean isPrimitiveOrWrapper(Class klass) { + return klass.isAssignableFrom(Byte.class) || klass.isAssignableFrom(byte.class) + || klass.isAssignableFrom(Short.class) || klass.isAssignableFrom(short.class) + || klass.isAssignableFrom(Integer.class) || klass.isAssignableFrom(int.class) + || klass.isAssignableFrom(Long.class) || klass.isAssignableFrom(long.class) + || klass.isAssignableFrom(Float.class) || klass.isAssignableFrom(float.class) + || klass.isAssignableFrom(Double.class) || klass.isAssignableFrom(double.class) + || klass.isAssignableFrom(Boolean.class) || klass.isAssignableFrom(boolean.class) + || klass.isAssignableFrom(String.class) || klass.isAssignableFrom(char.class) + || klass.isAssignableFrom(Character.class) || klass.isAssignableFrom(java.sql.Date.class) + || klass.isAssignableFrom(java.util.Date.class) + || klass.isAssignableFrom(java.math.BigDecimal.class); + } + + @Override + public void serializeWithType(Object value, JsonGenerator gen, + SerializerProvider serializers, TypeSerializer typeSer) + throws IOException { + if (value == null || isPrimitiveOrWrapper(value.getClass())) { + defaultSerializer.serializeWithType(value, gen, serializers, typeSer); + return; + } + depth += 1; + try { + if (depth > serializationDepth) { + gen.writeString("{}"); + } else if (serializedObjects.containsKey(value)) { + gen.writeString("duplicate " + value.getClass().getName()); + } else { + serializedObjects.put(value, value); + defaultSerializer.serializeWithType(value, gen, serializers, typeSer); + } + } finally { + depth--; + } + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (value == null || isPrimitiveOrWrapper(value.getClass())) { + defaultSerializer.serialize(value, gen, serializers); + return; + } + if (serializedObjects.containsKey(value)) { + gen.writeStartObject(value); + gen.writeFieldName("duplicate"); + gen.writeString("reference@" + Integer.toHexString(System.identityHashCode(value))); + gen.writeEndObject(); + } else { + serializedObjects.put(value, value); + defaultSerializer.serialize(value, gen, serializers); + } + } + } + + + /* found on StackOverflow */ + static class DepthFilter extends SimpleBeanPropertyFilter { + private final int maxDepth; + + public DepthFilter(int maxDepth) { + super(); + this.maxDepth = maxDepth; + } + + private int calcDepth(JsonGenerator jgen) { + JsonStreamContext streamContext = jgen.getOutputContext(); + int depth = -1; + while (streamContext != null) { + streamContext = streamContext.getParent(); + depth++; + } + return depth; + } + + @Override + public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider provider, + PropertyWriter writer) + throws Exception { + int depth = calcDepth(gen); + System.out.println("depth of " + pojo.getClass().getSimpleName() + " is " + depth); + if (depth <= maxDepth) { + writer.serializeAsField(pojo, gen, provider); + } + // comment this if you don't want {} placeholders + else { + writer.serializeAsOmittedField(pojo, gen, provider); + } + } + + } + + private static class CollectionSerializer extends JsonSerializer { + private final int maxCollectionElements; + + public CollectionSerializer(int maxCollectionElements) { + this.maxCollectionElements = maxCollectionElements; + } + + @Override + public void serializeWithType(Collection value, JsonGenerator gen, + SerializerProvider serializers, TypeSerializer typeSer) + throws IOException { + gen.setCurrentValue(value); + WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, + typeSer.typeId(value, JsonToken.START_OBJECT)); + _serialize(value, gen); + typeSer.writeTypeSuffix(gen, typeIdDef); + } + + @Override + public void serialize(Collection value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeStartObject(); + _serialize(value, gen); + gen.writeEndObject(); + } + + void _serialize(Collection value, JsonGenerator gen) throws IOException { + Iterator objects = value.iterator(); + for (int i = 0; i < maxCollectionElements && objects.hasNext(); i++) { + Object nextObject = objects.next(); + gen.writeObjectField("" + i, nextObject); + } + } + + @Override + public Class handledType() { + return Collection.class; + } + } + + + private static class PdxInstanceSerializer extends JsonSerializer { + @Override + public void serializeWithType(PdxInstance value, JsonGenerator gen, + SerializerProvider serializers, TypeSerializer typeSer) + throws IOException { + WritableTypeId writableTypeId = typeSer.typeId(value, JsonToken.START_OBJECT); + typeSer.writeTypePrefix(gen, writableTypeId); + _serialize(value, gen); + typeSer.writeTypeSuffix(gen, writableTypeId); + } + + @Override + public void serialize(PdxInstance value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeStartObject(); + _serialize(value, gen); + gen.writeEndObject(); + } + + void _serialize(PdxInstance value, JsonGenerator gen) throws IOException { + for (String field : value.getFieldNames()) { + gen.writeObjectField(field, value.getField(field)); + } + } + + @Override + public Class handledType() { + return PdxInstance.class; + } + } + + private static class StructSerializer extends JsonSerializer { + @Override + public void serializeWithType(StructImpl value, JsonGenerator gen, + SerializerProvider serializers, TypeSerializer typeSer) + throws IOException { + typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.START_OBJECT)); + _serialize(value, gen); + typeSer.writeTypeSuffix(gen, typeSer.typeId(value, JsonToken.START_OBJECT)); + } + + @Override + public void serialize(StructImpl value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeStartObject(); + _serialize(value, gen); + gen.writeEndObject(); + } + + void _serialize(StructImpl value, JsonGenerator gen) throws IOException { + String[] fields = value.getFieldNames(); + Object[] values = value.getFieldValues(); + for (int i = 0; i < fields.length; i++) { + gen.writeObjectField(fields[i], values[i]); + } + } + + @Override + public Class handledType() { + return StructImpl.class; + } + } + + private static class RegionEntrySerializer extends JsonSerializer { + @Override + public void serializeWithType(Region.Entry value, JsonGenerator gen, + SerializerProvider serializers, TypeSerializer typeSer) + throws IOException { + typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.START_OBJECT)); + gen.writeObjectField(value.getKey().toString(), value.getValue()); + typeSer.writeTypeSuffix(gen, typeSer.typeId(value, JsonToken.START_OBJECT)); + } + + @Override + public void serialize(Region.Entry value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + gen.writeStartObject(); + gen.writeObjectField(value.getKey().toString(), value.getValue()); + gen.writeEndObject(); + } + + @Override + public Class handledType() { + return Region.Entry.class; + } + } + + /** + * A Jackson module that installs a serializer-modifier to detect and prevent + * reserialization of objects that have already been serialized. W/o this + * Jackson would throw infinite-recursion exceptions. + */ + private static class PreventReserializationModule extends SimpleModule { + private final Map serializedObjects; + private final int serializationDepth; + + PreventReserializationModule(Map serializedObjects, int serializationDepth) { + this.serializedObjects = serializedObjects; + this.serializationDepth = serializationDepth; + } + + @Override + public void setupModule(SetupContext context) { + // install a modifier that prevents recursive serialization in cases where + // there are cyclical references + super.setupModule(context); + context.addBeanSerializerModifier(new BeanSerializerModifier() { + @Override + public JsonSerializer modifySerializer( + SerializationConfig config, BeanDescription desc, JsonSerializer serializer) { + return new PreventReserializationSerializer(serializer, serializedObjects, + serializationDepth); + } + }); + } + + } +} diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/TypedJson.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/TypedJson.java deleted file mode 100644 index 5e64d33a0c67..000000000000 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/TypedJson.java +++ /dev/null @@ -1,806 +0,0 @@ -/* - * 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.geode.management.internal.cli.json; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.apache.geode.cache.Region; -import org.apache.geode.cache.query.Struct; -import org.apache.geode.cache.query.internal.StructImpl; -import org.apache.geode.pdx.PdxInstance; - -/** - * A limited functionality JSON parser. Its a DSF based JSON parser. It does not create Object maps - * and serialize them like JSONObject. It just traverses an Object graph in depth first search - * manner and appends key values to a String writer. Hence we prevent creating a lot of garbage. - * - * Although it has limited functionality,still a simple use of add() method should suffice for most - * of the simple JSON use cases. - */ -public class TypedJson { - - /** - * Limit of collection length to be serialized in JSON format. - */ - public static final int DEFAULT_COLLECTION_ELEMENT_LIMIT = 100; - - public static final Object NULL = GfJsonObject.NULL; - - /** - * If Integer of Float is NAN - */ - static final String NONFINITE = "Non-Finite"; - - Map> forbidden = new java.util.IdentityHashMap>(); - - boolean commanate; - - private Map> map; - - private int queryCollectionsDepth; - - public TypedJson(String key, Object value, int queryCollectionsDepth) { - List list = new ArrayList(); - this.map = new LinkedHashMap>(); - if (value != null) { - list.add(value); - } - this.map.put(key, list); - this.queryCollectionsDepth = queryCollectionsDepth; - } - - public TypedJson(int queryCollectionsDepth) { - this.map = new LinkedHashMap>(); - this.queryCollectionsDepth = queryCollectionsDepth; - } - - public TypedJson() { - this.map = new LinkedHashMap>(); - this.queryCollectionsDepth = DEFAULT_COLLECTION_ELEMENT_LIMIT; - } - - public TypedJson(String key, Object value) { - List list = new ArrayList(); - this.map = new LinkedHashMap>(); - if (value != null) { - list.add(value); - } - this.map.put(key, list); - this.queryCollectionsDepth = DEFAULT_COLLECTION_ELEMENT_LIMIT; - } - - void bfs(Writer w, Object root) throws IOException { - if (root == null || isPrimitiveOrWrapper(root.getClass())) { - return; - } - LinkedList queue = new LinkedList(); - Map seen = new java.util.IdentityHashMap(); - - seen.put(root, null); - - // Adds to end of queue - queue.addFirst(root); - - while (!queue.isEmpty()) { - - // removes from front of queue - Object r = queue.pollFirst(); - List childrens = getChildrens(w, r); - // Visit child first before grand child - for (Object n : childrens) { - - if (n == null) { - continue; - } - if (!isPrimitiveOrWrapper(n.getClass())) { - if (!seen.containsKey(n)) { - queue.addFirst(n); - seen.put(n, null); - } else { - List list = forbidden.get(r); - if (list != null) { - list.add(n); - forbidden.put(r, list); - } else { - List newList = new ArrayList(); - newList.add(n); - forbidden.put(r, newList); - } - - } - } - } - } - } - - List getChildrens(Writer w, Object object) throws IOException { - if (isSpecialObject(object)) { - return this.visitSpecialObjects(w, object, false); - } else { - return this.visitChildrens(w, object, false); - } - } - - /** - * User can build on this object by adding Objects against a key. - * - * TypedJson result = new TypedJson(); result.add(KEY,object); If users add more objects against - * the same key the newly added object will be appended to the existing key forming an array of - * objects. - * - * If the KEY is a new one then it will be a key map value. - * - * @param key Key against which an object will be added - * @param value Object to be added - * @return TypedJson object - */ - public TypedJson add(String key, Object value) { - List list = this.map.get(key); - if (list != null) { - list.add(value); - } else { - list = new ArrayList(); - list.add(value); - this.map.put(key, list); - } - return this; - } - - public String toString() { - StringWriter w = new StringWriter(); - synchronized (w.getBuffer()) { - try { - return this.write(w).toString(); - } catch (Exception e) { - return null; - } - } - } - - public int length() { - return this.map.size(); - } - - Writer write(Writer writer) throws GfJsonException { - try { - boolean addComma = false; - final int length = this.length(); - Iterator keys = map.keySet().iterator(); - writer.write('{'); - - if (length == 1) { - Object key = keys.next(); - writer.write(quote(key.toString())); - writer.write(':'); - - writeList(writer, this.map.get(key)); - } else if (length != 0) { - while (keys.hasNext()) { - Object key = keys.next(); - if (addComma) { - writer.write(','); - } - writer.write(quote(key.toString())); - writer.write(':'); - - writeList(writer, this.map.get(key)); - commanate = false; - addComma = true; - } - } - - writer.write('}'); - - return writer; - } catch (IOException exception) { - throw new GfJsonException(exception); - } - } - - Writer writeList(Writer writer, List myArrayList) throws GfJsonException { - try { - boolean addComma = false; - int length = myArrayList.size(); - - if (length == 0) { - writer.write(']'); - writeValue(writer, null); - writer.write(']'); - } - if (length == 1) { - writer.write('['); - writeValue(writer, myArrayList.get(0)); - writer.write(']'); - - } else if (length != 0) { - writer.write('['); - for (int i = 0; i < length; i += 1) { - if (addComma) { - writer.write(','); - } - writeValue(writer, myArrayList.get(i)); - commanate = false; - addComma = true; - } - writer.write(']'); - } - - return writer; - } catch (IOException e) { - throw new GfJsonException(e); - } - } - - static String quote(String string) { - StringWriter sw = new StringWriter(); - synchronized (sw.getBuffer()) { - try { - return quote(string, sw).toString(); - } catch (IOException ignored) { - // will never happen - we are writing to a string writer - return ""; - } - } - } - - static boolean shouldVisitChildren(Object object) { - Class type = object.getClass(); - if (isPrimitiveOrWrapper(type)) { - return false; - } - if (isSpecialObject(object)) { - return false; - } - return true; - } - - static boolean isPrimitiveOrWrapper(Class klass) { - return klass.isAssignableFrom(Byte.class) || klass.isAssignableFrom(byte.class) - || klass.isAssignableFrom(Short.class) || klass.isAssignableFrom(short.class) - || klass.isAssignableFrom(Integer.class) || klass.isAssignableFrom(int.class) - || klass.isAssignableFrom(Long.class) || klass.isAssignableFrom(long.class) - || klass.isAssignableFrom(Float.class) || klass.isAssignableFrom(float.class) - || klass.isAssignableFrom(Double.class) || klass.isAssignableFrom(double.class) - || klass.isAssignableFrom(Boolean.class) || klass.isAssignableFrom(boolean.class) - || klass.isAssignableFrom(String.class) || klass.isAssignableFrom(char.class) - || klass.isAssignableFrom(Character.class) || klass.isAssignableFrom(java.sql.Date.class) - || klass.isAssignableFrom(java.util.Date.class) - || klass.isAssignableFrom(java.math.BigDecimal.class); - } - - static boolean isSpecialObject(Object object) { - Class type = object.getClass(); - if (type.isArray() || type.isEnum()) { - return true; - } - if ((object instanceof Collection) || (object instanceof Map) || (object instanceof PdxInstance) - || (object instanceof Struct) || (object instanceof Region.Entry)) { - return true; - } - return false; - } - - void writeVal(Writer w, Object value) throws IOException { - w.write('{'); - addVal(w, value); - w.write('}'); - } - - void addVal(Writer w, Object object) { - if (object == null) { - return; - } - if (shouldVisitChildren(object)) { - visitChildrens(w, object, true); - } - } - - void writeKeyValue(Writer w, Object key, Object value, Class type) throws IOException { - if (commanate) { - w.write(","); - } - - if (value == null || value.equals(null)) { - w.write(quote(key.toString())); - w.write(':'); - w.write("null"); - commanate = true; - return; - } - Class clazz = value.getClass(); - w.write(quote(key.toString())); - w.write(':'); - - if (type != null) { - writeType(w, type, value); - } - - if (isPrimitiveOrWrapper(clazz)) { - writePrimitives(w, value); - commanate = true; - } else if (isSpecialObject(value)) { - commanate = false; - visitSpecialObjects(w, value, true); - commanate = true; - } else { - commanate = false; - writeVal(w, value); - commanate = true; - } - endType(w, clazz); - return; - } - - void writePrimitives(Writer w, Object value) throws IOException { - if (value instanceof Number) { - w.write(numberToString((Number) value)); - return; - } - - if (value instanceof String || value instanceof Character || value instanceof java.sql.Date - || value instanceof java.util.Date) { - w.write(quote(value.toString())); - return; - } - w.write(value.toString()); - } - - void writeArray(Writer w, Object object) throws IOException { - if (commanate) { - w.write(","); - } - w.write('['); - int length = Array.getLength(object); - int elements = 0; - for (int i = 0; i < length && elements < queryCollectionsDepth; i += 1) { - Object item = Array.get(object, i); - if (i != 0) { - w.write(","); - } - if (item != null) { - Class clazz = item.getClass(); - - if (isPrimitiveOrWrapper(clazz)) { - writePrimitives(w, item); - } else if (isSpecialObject(item)) { - visitSpecialObjects(w, item, true); - } else { - writeVal(w, item); - } - } else { - w.write("null"); - } - elements++; - commanate = false; - } - w.write(']'); - commanate = true; - return; - } - - List getArrayChildren(Object object) { - List items = new ArrayList(); - int length = Array.getLength(object); - int elements = 0; - for (int i = 0; i < length && elements < queryCollectionsDepth; i += 1) { - Object item = Array.get(object, i); - items.add(item); - } - return items; - } - - void writeEnum(Writer w, Object object) throws IOException { - if (commanate) { - w.write(","); - } - w.write(quote(object.toString())); - commanate = true; - return; - } - - void writeTypedJson(Writer w, TypedJson object) throws IOException { - - if (commanate) { - w.write(","); - } - w.write(quote(object.toString())); - commanate = true; - return; - } - - void writeValue(Writer w, Object value) { - try { - if (value == null || value.equals(null)) { - w.write("null"); - return; - } - this.bfs(w, value); - Class rootClazz = value.getClass(); - writeType(w, rootClazz, value); - - if (isPrimitiveOrWrapper(rootClazz)) { - writePrimitives(w, value); - } else if (isSpecialObject(value)) { - visitSpecialObjects(w, value, true); - } else { - writeVal(w, value); - } - endType(w, rootClazz); - } catch (IOException e) { - } - } - - void startKey(Writer writer, String key) throws IOException { - if (key != null) { - writer.write('{'); - writer.write(quote(key.toString())); - writer.write(':'); - } - } - - void endKey(Writer writer, String key) throws IOException { - if (key != null) { - writer.write('}'); - } - } - - List visitSpecialObjects(Writer w, Object object, boolean write) throws IOException { - - List elements = new ArrayList(); - - Class clazz = object.getClass(); - - if (clazz.isArray()) { - if (write) { - writeArray(w, object); - } else { - return getArrayChildren(object); - } - } - - if (clazz.isEnum()) { - if (write) { - writeEnum(w, object); - } else { - elements.add(object); - } - return elements; - } - - if (object instanceof TypedJson) { - this.writeTypedJson(w, (TypedJson) object); - return elements; - } - - if (object instanceof Collection) { - Collection collection = (Collection) object; - Iterator iter = collection.iterator(); - int i = 0; - if (write) - w.write('{'); - while (iter.hasNext() && i < queryCollectionsDepth) { - Object item = iter.next(); - if (write) { - writeKeyValue(w, i, item, item != null ? item.getClass() : null); - } else { - elements.add(item); - } - - i++; - } - if (write) - w.write('}'); - return elements; - } - - if (object instanceof Map) { - Map map = (Map) object; - Iterator it = map.entrySet().iterator(); - int i = 0; - if (write) - w.write('{'); - while (it.hasNext() && i < queryCollectionsDepth) { - Map.Entry e = (Map.Entry) it.next(); - Object value = e.getValue(); - if (write) { - writeKeyValue(w, e.getKey(), value, value != null ? value.getClass() : null); - } else { - elements.add(value); - } - - i++; - } - if (write) - w.write('}'); - return elements; - } - - if (object instanceof PdxInstance) { - PdxInstance pdxInstance = (PdxInstance) object; - if (write) - w.write('{'); - for (String field : pdxInstance.getFieldNames()) { - Object fieldValue = pdxInstance.getField(field); - if (write) { - writeKeyValue(w, field, fieldValue, fieldValue != null ? fieldValue.getClass() : null); - } else { - elements.add(fieldValue); - } - } - if (write) - w.write('}'); - return elements; - } - - if (object instanceof Struct) { - StructImpl impl = (StructImpl) object; - String fields[] = impl.getFieldNames(); - Object[] values = impl.getFieldValues(); - - if (write) - w.write('{'); - for (int i = 0; i < fields.length; i++) { - Object fieldValue = values[i]; - if (write) { - writeKeyValue(w, fields[i], fieldValue, - fieldValue != null ? fieldValue.getClass() : null); - } else { - elements.add(fieldValue); - } - - } - if (write) - w.write('}'); - return elements; - } - - if (object instanceof Region.Entry) { - Region.Entry entry = (Region.Entry) object; - Object key = entry.getKey(); - Object value = entry.getValue(); - - - if (write) { - w.write('{'); - writeKeyValue(w, key, value, value != null ? value.getClass() : null); - w.write('}'); - } else { - elements.add(value); - } - - return elements; - } - - return elements; - } - - void writeType(Writer w, Class clazz, Object value) throws IOException { - if (clazz != TypedJson.class) { - w.write('['); - w.write(quote(internalToExternal(clazz, value))); - w.write(","); - } - } - - /** - * Handle some special GemFire classes. We don't want to expose some of the internal classes. - * Hence corresponding interface or external classes should be shown. - */ - String internalToExternal(Class clazz, Object value) { - if (value != null && value instanceof Region.Entry) { - return Region.Entry.class.getCanonicalName(); - } - if (value != null && value instanceof PdxInstance) { - return PdxInstance.class.getCanonicalName(); - } - return clazz.getCanonicalName(); - } - - void endType(Writer w, Class clazz) throws IOException { - if (clazz != TypedJson.class) { - w.write(']'); - } - - } - - List visitChildrens(Writer w, Object object, boolean write) { - - List elements = new ArrayList(); - - Method[] methods = getMethods(object); - - for (int i = 0; i < methods.length; i += 1) { - try { - Method method = methods[i]; - if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) { - String name = method.getName(); - String key = ""; - if (name.startsWith("get")) { - if ("getClass".equals(name) || "getDeclaringClass".equals(name)) { - key = ""; - } else { - key = name.substring(3); - } - } else if (name.startsWith("is")) { - key = name.substring(2); - } - if (key.length() > 0 && Character.isUpperCase(key.charAt(0)) - && method.getParameterTypes().length == 0) { - if (key.length() == 1) { - key = key.toLowerCase(); - } else if (!Character.isUpperCase(key.charAt(1))) { - key = key.substring(0, 1).toLowerCase() + key.substring(1); - } - method.setAccessible(true); - Object result = method.invoke(object, (Object[]) null); - if (write) { - List forbiddenList = forbidden.get(object); - if (forbiddenList != null && forbiddenList.contains(result)) { - writeKeyValue(w, key, result.getClass().getCanonicalName(), method.getReturnType()); - } else { - writeKeyValue(w, key, result, method.getReturnType()); - } - } else { - elements.add(result); - } - - - } - } - } catch (Exception ignore) { - } - } - return elements; - } - - - /** - * This method returns method declared in a Class as well as all the super classes in the - * hierarchy. If class is a system class it wont include super class methods - */ - Method[] getMethods(Object object) { - Class klass = object.getClass(); - - // If klass is a System class then set includeSuperClass to false. - - boolean includeSuperClass = klass.getClassLoader() != null; - - Method[] decMethods = klass.getDeclaredMethods(); - Map decMethodMap = new HashMap(); - for (Method method : decMethods) { - decMethodMap.put(method.getName(), method); - } - - if (includeSuperClass) { - Method[] allMethods = klass.getMethods(); - List allMethodList = Arrays.asList(allMethods); - for (Method method : allMethodList) { - if (decMethodMap.get(method.getName()) != null) { - // skip. This will ensure overriden methods wont be added again. - } else { - decMethodMap.put(method.getName(), method); - } - } - } - - Method[] methodArr = new Method[decMethodMap.size()]; - return decMethodMap.values().toArray(methodArr); - } - - - /** - * Produce a string from a Number. - * - * @param number A Number - * @return A String. - */ - public static String numberToString(Number number) { - if (number == null) { - return ""; - } - if (number instanceof Double) { - if (((Double) number).isInfinite() || ((Double) number).isNaN()) { - return "Non-Finite"; - } - } else if (number instanceof Float) { - if (((Float) number).isInfinite() || ((Float) number).isNaN()) { - return "Non-Finite"; - } - } - - // Shave off trailing zeros and decimal point, if possible. - - String string = number.toString(); - if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && string.indexOf('E') < 0) { - while (string.endsWith("0")) { - string = string.substring(0, string.length() - 1); - } - if (string.endsWith(".")) { - string = string.substring(0, string.length() - 1); - } - } - return string; - } - - public static Writer quote(String string, Writer w) throws IOException { - if (string == null || string.length() == 0) { - w.write("\"\""); - return w; - } - - char b; - char c = 0; - String hhhh; - int i; - int len = string.length(); - - w.write('"'); - for (i = 0; i < len; i += 1) { - b = c; - c = string.charAt(i); - switch (c) { - case '\\': - case '"': - w.write('\\'); - w.write(c); - break; - case '/': - if (b == '<') { - w.write('\\'); - } - w.write(c); - break; - case '\b': - w.write("\\b"); - break; - case '\t': - w.write("\\t"); - break; - case '\n': - w.write("\\n"); - break; - case '\f': - w.write("\\f"); - break; - case '\r': - w.write("\\r"); - break; - default: - if (c < ' ' || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) { - hhhh = "000" + Integer.toHexString(c); - w.write("\\u" + hhhh.substring(hhhh.length() - 4)); - } else { - w.write(c); - } - } - } - w.write('"'); - return w; - } -} diff --git a/geode-core/src/main/java/org/apache/geode/pdx/internal/json/PdxToJSON.java b/geode-core/src/main/java/org/apache/geode/pdx/internal/json/PdxToJSON.java index b1305dc29f35..ea7b7fa07d88 100644 --- a/geode-core/src/main/java/org/apache/geode/pdx/internal/json/PdxToJSON.java +++ b/geode-core/src/main/java/org/apache/geode/pdx/internal/json/PdxToJSON.java @@ -52,7 +52,6 @@ public PdxToJSON(PdxInstance pdx) { public String getJSON() { JsonFactory jf = new JsonFactory(); - // OutputStream os = new ByteArrayOutputStream(); HeapDataOutputStream hdos = new HeapDataOutputStream(org.apache.geode.internal.Version.CURRENT); try { JsonGenerator jg = jf.createJsonGenerator(hdos, JsonEncoding.UTF8); @@ -155,7 +154,7 @@ private void getJSONStringFromMap(JsonGenerator jg, Map map, String pf) jg.writeStartObject(); - Iterator iter = (Iterator) map.entrySet().iterator(); + Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); @@ -227,21 +226,10 @@ private void getJSONStringFromArray(JsonGenerator jg, Object value, String pf) jg.writeEndArray(); } else { throw new IllegalStateException( - "PdxInstance returns unknwon pdxfield " + pf + " for type " + value); + "PdxInstance returns unknown pdxfield " + pf + " for type " + value); } } - private void getJSONStringFromArray1(JsonGenerator jg, T[] array, String pf) - throws JsonGenerationException, IOException { - jg.writeStartArray(); - - for (T obj : array) { - writeValue(jg, obj, pf); - } - jg.writeEndArray(); - } - - private void getJSONStringFromCollection(JsonGenerator jg, Collection coll, String pf) throws JsonGenerationException, IOException { jg.writeStartArray(); diff --git a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt index 25aee8908fe0..4d466936ff26 100644 --- a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt +++ b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt @@ -837,4 +837,4 @@ org/apache/geode/redis/internal/executor/sortedset/GeoRadiusParameters$SortOrder org/apache/geode/security/AuthenticationFailedException,true,-8202866472279088879 org/apache/geode/security/AuthenticationRequiredException,true,4675976651103154919 org/apache/geode/security/GemFireSecurityException,true,3814254578203076926,cause:java/lang/Throwable -org/apache/geode/security/NotAuthorizedException,true,419215768216387745,principal:java/security/Principal \ No newline at end of file +org/apache/geode/security/NotAuthorizedException,true,419215768216387745,principal:java/security/Principal diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/json/TypedJsonTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/json/QueryResultFormatterTest.java similarity index 61% rename from geode-core/src/test/java/org/apache/geode/management/internal/cli/json/TypedJsonTest.java rename to geode-core/src/test/java/org/apache/geode/management/internal/cli/json/QueryResultFormatterTest.java index 625cc26f626c..b076d22640da 100644 --- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/json/TypedJsonTest.java +++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/json/QueryResultFormatterTest.java @@ -19,7 +19,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import java.io.Writer; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; @@ -35,19 +34,18 @@ * * TODO: add actual assertions */ -public class TypedJsonTest { +public class QueryResultFormatterTest { private static final String RESULT = "result"; @Test public void canBeMocked() throws Exception { - TypedJson mockTypedJson = mock(TypedJson.class); - Writer writer = null; + QueryResultFormatter mockQueryResultFormatter = mock(QueryResultFormatter.class); Object value = new Object(); - mockTypedJson.writeVal(writer, value); + mockQueryResultFormatter.add("key", value); - verify(mockTypedJson, times(1)).writeVal(writer, value); + verify(mockQueryResultFormatter, times(1)).add("key", value); } @Test @@ -57,9 +55,9 @@ public void testArrayList() throws Exception { list.add("TWO"); list.add("THREE"); - TypedJson typedJson = new TypedJson(RESULT, list); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, list); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test @@ -69,9 +67,9 @@ public void testArray() throws Exception { intArray[i] = i; } - TypedJson typedJson = new TypedJson(RESULT, intArray); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, intArray); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test @@ -81,25 +79,27 @@ public void testBigList() throws Exception { list.add("BIG_COLL_" + i); } - TypedJson typedJson = new TypedJson(RESULT, list); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, list); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test public void testEnumContainer() throws Exception { EnumContainer enumContainer = new EnumContainer(Currency.DIME); - TypedJson typedJson = new TypedJson(RESULT, enumContainer); + QueryResultFormatter queryResultFormatter = + new QueryResultFormatter(100).add(RESULT, enumContainer); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test public void testEnum() throws Exception { - TypedJson typedJson = new TypedJson(RESULT, Currency.DIME); + QueryResultFormatter queryResultFormatter = + new QueryResultFormatter(100).add(RESULT, Currency.DIME); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test @@ -110,9 +110,9 @@ public void testEnumList() throws Exception { list.add(Currency.QUARTER); list.add(Currency.NICKLE); - TypedJson typedJson = new TypedJson(RESULT, list); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, list); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test @@ -123,38 +123,40 @@ public void testMap() throws Exception { map.put("3", "THREE"); map.put("4", "FOUR"); - TypedJson typedJson = new TypedJson(RESULT, map); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, map); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test public void testBigDecimal() throws Exception { BigDecimal dc = new BigDecimal(20); - TypedJson typedJson = new TypedJson(RESULT, dc); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100).add(RESULT, dc); - checkResult(typedJson); + checkResult(queryResultFormatter); } @Test public void testObjects() throws Exception { Object object = new Object(); - TypedJson typedJson = new TypedJson(RESULT, object); - - checkResult(typedJson); + QueryResultFormatter queryResultFormatter = new QueryResultFormatter(100); + queryResultFormatter.add(RESULT, object); + checkResult(queryResultFormatter); } - private void checkResult(final TypedJson typedJson) throws GfJsonException { - GfJsonObject gfJsonObject = new GfJsonObject(typedJson.toString()); - System.out.println(gfJsonObject); + private void checkResult(final QueryResultFormatter queryResultFormatter) throws GfJsonException { + String jsonString = queryResultFormatter.toString(); + System.out.println("queryResultFormatter.toString=" + jsonString); + GfJsonObject gfJsonObject = new GfJsonObject(jsonString); + System.out.println("gfJsonObject=" + gfJsonObject); assertThat(gfJsonObject.get(RESULT)).isNotNull(); } private enum Currency { PENNY, NICKLE, DIME, QUARTER - }; + } private static class EnumContainer {