METRON-1511 Unable to Serialize Profiler Configuration (nickwallen) closes apache/metron#982
Project: http://git-wip-us.apache.org/repos/asf/metron/repo Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/b5bf9a98 Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/b5bf9a98 Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/b5bf9a98 Branch: refs/heads/feature/METRON-1090-stellar-assignment Commit: b5bf9a98725f866a7fee6470a8e763d17cc69ffd Parents: a41611b Author: nickwallen <n...@nickallen.org> Authored: Mon Apr 23 09:36:06 2018 -0400 Committer: nickallen <nickal...@apache.org> Committed: Mon Apr 23 09:36:06 2018 -0400 ---------------------------------------------------------------------- .../configuration/profiler/ProfileConfig.java | 57 ++++++++-- .../profiler/ProfileResultExpressions.java | 4 +- .../profiler/ProfileTriageExpressions.java | 8 ++ .../configuration/profiler/ProfilerConfig.java | 81 ++++++++++++-- .../profiler/ProfileConfigTest.java | 102 ++++++++++++++--- .../profiler/ProfilerConfigTest.java | 109 +++++++++++++++++-- 6 files changed, 310 insertions(+), 51 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metron/blob/b5bf9a98/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileConfig.java ---------------------------------------------------------------------- diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileConfig.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileConfig.java index f5b46e6..f2272c3 100644 --- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileConfig.java +++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileConfig.java @@ -18,12 +18,15 @@ package org.apache.metron.common.configuration.profiler; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.metron.common.utils.JSONUtils; +import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -264,15 +267,47 @@ public class ProfileConfig implements Serializable { @Override public String toString() { - return "ProfileConfig{" + - "profile='" + profile + '\'' + - ", foreach='" + foreach + '\'' + - ", onlyif='" + onlyif + '\'' + - ", init=" + init + - ", update=" + update + - ", groupBy=" + groupBy + - ", result=" + result + - ", expires=" + expires + - '}'; + return new ToStringBuilder(this) + .append("profile", profile) + .append("foreach", foreach) + .append("onlyif", onlyif) + .append("init", init) + .append("update", update) + .append("groupBy", groupBy) + .append("result", result) + .append("expires", expires) + .toString(); + } + + /** + * Deserialize a {@link ProfileConfig}. + * + * @param bytes Raw bytes containing a UTF-8 JSON String. + * @return The Profile definition. + * @throws IOException + */ + public static ProfileConfig fromBytes(byte[] bytes) throws IOException { + return JSONUtils.INSTANCE.load(new String(bytes), ProfileConfig.class); + } + + /** + * Deserialize a {@link ProfileConfig}. + * + * @param json A String containing JSON. + * @return The Profile definition. + * @throws IOException + */ + public static ProfileConfig fromJSON(String json) throws IOException { + return JSONUtils.INSTANCE.load(json, ProfileConfig.class); + } + + /** + * Serialize the profile definition to a JSON string. + * + * @return The Profiler configuration serialized as a JSON string. + * @throws JsonProcessingException + */ + public String toJSON() throws JsonProcessingException { + return JSONUtils.INSTANCE.toJSON(this, true); } } http://git-wip-us.apache.org/repos/asf/metron/blob/b5bf9a98/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java ---------------------------------------------------------------------- diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java index 82af223..5bcec72 100644 --- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java +++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileResultExpressions.java @@ -18,7 +18,7 @@ package org.apache.metron.common.configuration.profiler; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonValue; /** * A Stellar expression that is executed to produce a single @@ -26,7 +26,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; */ public class ProfileResultExpressions { - @JsonIgnore private String expression; @JsonCreator @@ -34,6 +33,7 @@ public class ProfileResultExpressions { this.expression = expression; } + @JsonValue public String getExpression() { return expression; } http://git-wip-us.apache.org/repos/asf/metron/blob/b5bf9a98/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileTriageExpressions.java ---------------------------------------------------------------------- diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileTriageExpressions.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileTriageExpressions.java index fbe1706..da02cb2 100644 --- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileTriageExpressions.java +++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfileTriageExpressions.java @@ -17,6 +17,8 @@ */ package org.apache.metron.common.configuration.profiler; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -61,10 +63,16 @@ public class ProfileTriageExpressions { return expressions.get(name); } + @JsonAnyGetter public Map<String, String> getExpressions() { return expressions; } + @JsonAnySetter + public void setExpressions(Map<String, String> expressions) { + this.expressions = expressions; + } + @Override public String toString() { return "ProfileTriageExpressions{" + http://git-wip-us.apache.org/repos/asf/metron/blob/b5bf9a98/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfilerConfig.java ---------------------------------------------------------------------- diff --git a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfilerConfig.java b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfilerConfig.java index 0bdb7e2..e4fa99a 100644 --- a/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfilerConfig.java +++ b/metron-platform/metron-common/src/main/java/org/apache/metron/common/configuration/profiler/ProfilerConfig.java @@ -17,6 +17,17 @@ */ package org.apache.metron.common.configuration.profiler; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.metron.common.utils.JSONUtils; +import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; + +import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -25,6 +36,7 @@ import java.util.Optional; /** * The configuration object for the Profiler, which may contain many Profile definitions. */ +@JsonSerialize(include=Inclusion.NON_NULL) public class ProfilerConfig implements Serializable { /** @@ -59,10 +71,16 @@ public class ProfilerConfig implements Serializable { return this; } + @JsonGetter("timestampField") + public String getTimestampFieldForJson() { + return timestampField.orElse(null); + } + public Optional<String> getTimestampField() { return timestampField; } + @JsonSetter("timestampField") public void setTimestampField(String timestampField) { this.timestampField = Optional.of(timestampField); } @@ -78,25 +96,66 @@ public class ProfilerConfig implements Serializable { @Override public String toString() { - return "ProfilerConfig{" + - "profiles=" + profiles + - ", timestampField='" + timestampField + '\'' + - '}'; + return new ToStringBuilder(this) + .append("profiles", profiles) + .append("timestampField", timestampField) + .toString(); } @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + ProfilerConfig that = (ProfilerConfig) o; - if (profiles != null ? !profiles.equals(that.profiles) : that.profiles != null) return false; - return timestampField != null ? timestampField.equals(that.timestampField) : that.timestampField == null; + return new EqualsBuilder() + .append(profiles, that.profiles) + .append(timestampField, that.timestampField) + .isEquals(); } @Override public int hashCode() { - int result = profiles != null ? profiles.hashCode() : 0; - result = 31 * result + (timestampField != null ? timestampField.hashCode() : 0); - return result; + return new HashCodeBuilder(17, 37) + .append(profiles) + .append(timestampField) + .toHashCode(); + } + + /** + * Deserialize a {@link ProfilerConfig}. + * + * @param bytes Raw bytes containing a UTF-8 JSON String. + * @return The Profiler configuration. + * @throws IOException + */ + public static ProfilerConfig fromBytes(byte[] bytes) throws IOException { + return JSONUtils.INSTANCE.load(new String(bytes), ProfilerConfig.class); + } + + /** + * Deserialize a {@link ProfilerConfig}. + * + * @param json A String containing JSON. + * @return The Profiler configuration. + * @throws IOException + */ + public static ProfilerConfig fromJSON(String json) throws IOException { + return JSONUtils.INSTANCE.load(json, ProfilerConfig.class); + } + + /** + * Serialize a {@link ProfilerConfig} to a JSON string. + * + * @return The Profiler configuration serialized as a JSON string. + * @throws JsonProcessingException + */ + public String toJSON() throws JsonProcessingException { + return JSONUtils.INSTANCE.toJSON(this, true); } } http://git-wip-us.apache.org/repos/asf/metron/blob/b5bf9a98/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfileConfigTest.java ---------------------------------------------------------------------- diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfileConfigTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfileConfigTest.java index e178ee0..87dbbc4 100644 --- a/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfileConfigTest.java +++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfileConfigTest.java @@ -21,7 +21,6 @@ package org.apache.metron.common.configuration.profiler; import com.fasterxml.jackson.databind.JsonMappingException; import org.adrianwalker.multilinestring.Multiline; -import org.apache.metron.common.utils.JSONUtils; import org.junit.Test; import java.io.IOException; @@ -51,12 +50,29 @@ public class ProfileConfigTest { * The 'onlyif' field should default to 'true' when it is not specified. */ @Test - public void testOnlyIfDefault() throws IOException { - ProfileConfig profile = JSONUtils.INSTANCE.load(onlyIfDefault, ProfileConfig.class); + public void testFromJSONWithOnlyIfDefault() throws IOException { + ProfileConfig profile = ProfileConfig.fromJSON(onlyIfDefault); assertEquals("true", profile.getOnlyif()); } /** + * Tests serializing the Profiler configuration to JSON. + */ + @Test + public void testToJSONWithOnlyIfDefault() throws Exception { + + // setup a profiler config to serialize + ProfileConfig expected = ProfileConfig.fromJSON(onlyIfDefault); + + // execute the test - serialize the config + String asJson = expected.toJSON(); + + // validate - deserialize to validate + ProfileConfig actual = ProfileConfig.fromJSON(asJson); + assertEquals(expected, actual); + } + + /** * { * "foreach": "ip_src_addr", * "update": {}, @@ -70,8 +86,8 @@ public class ProfileConfigTest { * The 'name' of the profile must be defined. */ @Test(expected = JsonMappingException.class) - public void testNameMissing() throws IOException { - JSONUtils.INSTANCE.load(nameMissing, ProfileConfig.class); + public void testFromJSONWithNameMissing() throws IOException { + ProfileConfig.fromJSON(nameMissing); } /** @@ -88,8 +104,8 @@ public class ProfileConfigTest { * The 'foreach' field must be defined. */ @Test(expected = JsonMappingException.class) - public void testForeachMissing() throws IOException { - JSONUtils.INSTANCE.load(foreachMissing, ProfileConfig.class); + public void testFromJSONWithForeachMissing() throws IOException { + ProfileConfig.fromJSON(foreachMissing); } /** @@ -106,8 +122,8 @@ public class ProfileConfigTest { * The 'result' field must be defined. */ @Test(expected = JsonMappingException.class) - public void testResultMissing() throws IOException { - JSONUtils.INSTANCE.load(resultMissing, ProfileConfig.class); + public void testFromJSONWithResultMissing() throws IOException { + ProfileConfig.fromJSON(resultMissing); } /** @@ -125,8 +141,8 @@ public class ProfileConfigTest { * The 'result' field must contain the 'profile' expression used to store the profile measurement. */ @Test(expected = JsonMappingException.class) - public void testResultMissingProfileExpression() throws IOException { - JSONUtils.INSTANCE.load(resultMissingProfileExpression, ProfileConfig.class); + public void testFromJSONWithResultMissingProfileExpression() throws IOException { + ProfileConfig.fromJSON(resultMissingProfileExpression); } /** @@ -145,8 +161,8 @@ public class ProfileConfigTest { * the 'profile' expression used to store the profile measurement. */ @Test - public void testResultWithExpression() throws IOException { - ProfileConfig profile = JSONUtils.INSTANCE.load(resultWithExpression, ProfileConfig.class); + public void testFromJSONWithResultWithExpression() throws IOException { + ProfileConfig profile = ProfileConfig.fromJSON(resultWithExpression); assertEquals("2 + 2", profile.getResult().getProfileExpressions().getExpression()); // no triage expressions expected @@ -154,6 +170,23 @@ public class ProfileConfigTest { } /** + * Tests serializing the Profiler configuration to JSON. + */ + @Test + public void testToJSONWithResultWithExpression() throws Exception { + + // setup a profiler config to serialize + ProfileConfig expected = ProfileConfig.fromJSON(resultWithExpression); + + // execute the test - serialize the config + String asJson = expected.toJSON(); + + // validate - deserialize to validate + ProfileConfig actual = ProfileConfig.fromJSON(asJson); + assertEquals(expected, actual); + } + + /** * { * "profile": "test", * "foreach": "ip_src_addr", @@ -170,8 +203,8 @@ public class ProfileConfigTest { * The result's 'triage' field is optional. */ @Test - public void testResultWithProfileOnly() throws IOException { - ProfileConfig profile = JSONUtils.INSTANCE.load(resultWithProfileOnly, ProfileConfig.class); + public void testFromJSONWithResultWithProfileOnly() throws IOException { + ProfileConfig profile = ProfileConfig.fromJSON(resultWithProfileOnly); assertEquals("2 + 2", profile.getResult().getProfileExpressions().getExpression()); // no triage expressions expected @@ -179,6 +212,23 @@ public class ProfileConfigTest { } /** + * Tests serializing the Profiler configuration to JSON. + */ + @Test + public void testToJSONWithProfileOnly() throws Exception { + + // setup a profiler config to serialize + ProfileConfig expected = ProfileConfig.fromJSON(resultWithProfileOnly); + + // execute the test - serialize the config + String asJson = expected.toJSON(); + + // validate - deserialize to validate + ProfileConfig actual = ProfileConfig.fromJSON(asJson); + assertEquals(expected, actual); + } + + /** * { * "profile": "test", * "foreach": "ip_src_addr", @@ -199,10 +249,28 @@ public class ProfileConfigTest { * The result's 'triage' field can contain many named expressions. */ @Test - public void testResultWithTriage() throws IOException { - ProfileConfig profile = JSONUtils.INSTANCE.load(resultWithTriage, ProfileConfig.class); + public void testFromJSONWithResultWithTriage() throws IOException { + ProfileConfig profile = ProfileConfig.fromJSON(resultWithTriage); assertEquals("4 + 4", profile.getResult().getTriageExpressions().getExpression("eight")); assertEquals("8 + 8", profile.getResult().getTriageExpressions().getExpression("sixteen")); } + + /** + * Tests serializing the Profiler configuration to JSON. + */ + @Test + public void testToJSONWithResultWithTriage() throws Exception { + + // setup a profiler config to serialize + ProfileConfig expected = ProfileConfig.fromJSON(resultWithTriage); + + // execute the test - serialize the config + String asJson = expected.toJSON(); + + // validate - deserialize to validate + ProfileConfig actual = ProfileConfig.fromJSON(asJson); + assertEquals(expected, actual); + } + } http://git-wip-us.apache.org/repos/asf/metron/blob/b5bf9a98/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java ---------------------------------------------------------------------- diff --git a/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java index 2e73cde..1a11811 100644 --- a/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java +++ b/metron-platform/metron-common/src/test/java/org/apache/metron/common/configuration/profiler/ProfilerConfigTest.java @@ -20,7 +20,6 @@ package org.apache.metron.common.configuration.profiler; import org.adrianwalker.multilinestring.Multiline; -import org.apache.metron.common.utils.JSONUtils; import org.junit.Test; import java.io.IOException; @@ -48,14 +47,41 @@ public class ProfilerConfigTest { * } */ @Multiline + private String profile; + + /** + * Tests deserializing the Profiler configuration using the fromJSON(...) method. + */ + @Test + public void testFromJSON() throws IOException { + ProfilerConfig conf = ProfilerConfig.fromJSON(profile); + + assertFalse(conf.getTimestampField().isPresent()); + assertEquals(1, conf.getProfiles().size()); + } + + /** + * { + * "profiles": [ + * { + * "profile": "profile1", + * "foreach": "ip_src_addr", + * "init": { "count": "0" }, + * "update": { "count": "count + 1" }, + * "result": "count" + * } + * ] + * } + */ + @Multiline private String noTimestampField; /** * If no 'timestampField' is defined, it should not be present by default. */ @Test - public void testNoTimestampField() throws IOException { - ProfilerConfig conf = JSONUtils.INSTANCE.load(noTimestampField, ProfilerConfig.class); + public void testFromJSONWithNoTimestampField() throws IOException { + ProfilerConfig conf = ProfilerConfig.fromJSON(noTimestampField); assertFalse(conf.getTimestampField().isPresent()); } @@ -77,11 +103,12 @@ public class ProfilerConfigTest { private String timestampField; /** - * If no 'timestampField' is defined, it should not be present by default. + * Tests deserializing the Profiler configuration when the timestamp field is defined. */ @Test - public void testTimestampField() throws IOException { - ProfilerConfig conf = JSONUtils.INSTANCE.load(timestampField, ProfilerConfig.class); + public void testFromJSONWithTimestampField() throws IOException { + ProfilerConfig conf = ProfilerConfig.fromJSON(timestampField); + assertTrue(conf.getTimestampField().isPresent()); } @@ -108,13 +135,75 @@ public class ProfilerConfigTest { @Multiline private String twoProfiles; + @Test + public void testFromJSONTwoProfiles() throws IOException { + ProfilerConfig conf = ProfilerConfig.fromJSON(twoProfiles); + + assertEquals(2, conf.getProfiles().size()); + assertFalse(conf.getTimestampField().isPresent()); + } + /** - * The 'onlyif' field should default to 'true' when it is not specified. + * Tests serializing the Profiler configuration to JSON. */ @Test - public void testTwoProfiles() throws IOException { - ProfilerConfig conf = JSONUtils.INSTANCE.load(twoProfiles, ProfilerConfig.class); - assertEquals(2, conf.getProfiles().size()); + public void testToJSON() throws Exception { + + // setup a profiler config to serialize + ProfilerConfig expected = ProfilerConfig.fromJSON(profile); + + // execute the test - serialize the config + String asJson = expected.toJSON(); + + // validate - deserialize to validate + ProfilerConfig actual = ProfilerConfig.fromJSON(asJson); + assertEquals(expected, actual); } + /** + * { + * "profiles": [ + * { + * "profile": "profile1", + * "foreach": "ip_src_addr", + * "init": { "count": "0" }, + * "update": { "count": "count + 1" }, + * "result": { + * "profile": "count", + * "triage" : { "count": "count" } + * } + * } + * ] + * } + */ + @Multiline + private String profileWithTriageExpression; + + @Test + public void testToJSONWithTriageExpression() throws Exception { + + // setup a profiler config to serialize + ProfilerConfig expected = ProfilerConfig.fromJSON(profileWithTriageExpression); + + // execute the test - serialize the config + String asJson = expected.toJSON(); + + // validate - deserialize to validate + ProfilerConfig actual = ProfilerConfig.fromJSON(asJson); + assertEquals(expected, actual); + } + + @Test + public void testToJSONWithTwoProfiles() throws Exception { + + // setup a profiler config to serialize + ProfilerConfig expected = ProfilerConfig.fromJSON(twoProfiles); + + // execute the test - serialize the config + String asJson = expected.toJSON(); + + // validate - deserialize to validate + ProfilerConfig actual = ProfilerConfig.fromJSON(asJson); + assertEquals(expected, actual); + } }