diff options
author | Michael Arrastia <MArrasti@amdocs.com> | 2018-06-25 15:55:54 +0100 |
---|---|---|
committer | Michael Arrastia <MArrasti@amdocs.com> | 2018-06-25 15:55:54 +0100 |
commit | 4aec102f14f7cb837733c4d2b217e7fdc7088f86 (patch) | |
tree | 6167d6b45a135f51db3dceb49deadf0d5bdc05e3 /champ-lib/champ-core | |
parent | 0edaac90429d84075ee01404166f29c666b3d107 (diff) |
Fix serialization of the "key" property
The introduction of a payload envelope to the update notification event
issued by champ-core caused the incorrect serialization of the "key"
property as a JSON object instead of a String.
This caused Spike to fail when parsing the notification event from
Champ as it expects "key" to be a String.
A previous fix attempt incorrectly addressed this problem. As the issue
originated from the inconsistent use of both Jackson and Gson JSON
libraries with the envelope using Gson but the contained objects
using Jackson annotations. This fix primarily involves updates to
champ-core to ensure that the envelope generation uses Jackson
instead of Gson and respects the original Jackson serialization of
the "key" property as a String.
Change-Id: I2a12732c9ff3970c3db9de5f0039304e68cb3556
Issue-ID: AAI-1243
Signed-off-by: Michael Arrastia <MArrasti@amdocs.com>
Diffstat (limited to 'champ-lib/champ-core')
10 files changed, 129 insertions, 209 deletions
diff --git a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/ChampEvent.java b/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/ChampEvent.java index a8a8117..847f255 100644 --- a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/ChampEvent.java +++ b/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/ChampEvent.java @@ -33,7 +33,6 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.annotations.SerializedName; public class ChampEvent { @@ -46,14 +45,12 @@ public class ChampEvent { private ChampOperation operation; private long timestamp; - @SerializedName("transaction-id") private String transactionId = null; private ChampObject vertex = null; private ChampRelationship relationship = null; private ChampPartition partition = null; private ChampObjectIndex objectIndex = null; private ChampRelationshipIndex relationshipIndex = null; - @SerializedName("database-transaction-id") private String dbTransactionId = null; @@ -131,15 +128,11 @@ public class ChampEvent { return dbTransactionId; } - public void setDbTransactionId(String id) { this.dbTransactionId = id; } - - public String toJson() { - ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(Include.NON_NULL); @@ -151,14 +144,12 @@ public class ChampEvent { } public static ChampEvent fromJson(String json) throws JsonParseException, JsonMappingException, IOException { - mapper.setSerializationInclusion(Include.NON_NULL); return mapper.readValue(json, ChampEvent.class); } @Override public String toString() { - return toJson(); } @@ -166,7 +157,6 @@ public class ChampEvent { ChampEvent event = null; - public Builder() { event = new ChampEvent(); } @@ -201,9 +191,7 @@ public class ChampEvent { return this; } - public ChampEvent build() { - event.setTimestamp(System.currentTimeMillis()); // Set a unique transaction id on this event that can be used by downstream entities diff --git a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelope.java b/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelope.java index f4a20a6..0dbe1d9 100644 --- a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelope.java +++ b/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelope.java @@ -22,20 +22,15 @@ package org.onap.aai.champcore.event.envelope; import org.onap.aai.champcore.event.ChampEvent; -import org.onap.aai.champcore.event.envelope.util.GsonUtil; -import org.onap.aai.champcore.exceptions.ChampUnmarshallingException; -import com.google.gson.Gson; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; public class ChampEventEnvelope { private ChampEventHeader header; private ChampEvent body; - /** - * Serializer/deserializer for converting to/from JSON. - */ - private static final Gson gson = GsonUtil.createGson(); - public ChampEventEnvelope(ChampEvent event) { this.header = new ChampEventHeader.Builder(ChampEventHeader.EventType.UPDATE_NOTIFICATION) .requestId(event.getTransactionId()).build(); @@ -69,24 +64,13 @@ public class ChampEventEnvelope { * @return - A JSON format string representation of this Vertex. */ public String toJson() { - return gson.toJson(this); - } + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(Include.NON_NULL); - /** - * Deserializes the provided JSON string into a Event Envelope object. - * - * @param json the JSON string to produce the Event Envelope from. - * @return an Event Envelope object. - * @throws ChampUnmarshallingException - */ - public static ChampEventEnvelope fromJson(String json) throws ChampUnmarshallingException { try { - if (json == null || json.isEmpty()) { - throw new ChampUnmarshallingException("Empty or null JSON string."); - } - return gson.fromJson(json, ChampEventEnvelope.class); - } catch (Exception ex) { - throw new ChampUnmarshallingException("Unable to parse JSON string: "); + return mapper.writeValueAsString(this); + } catch (JsonProcessingException e) { + return "Unmarshallable: " + e.getMessage(); } } diff --git a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventHeader.java b/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventHeader.java index 9ff6deb..3e19aa5 100644 --- a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventHeader.java +++ b/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/ChampEventHeader.java @@ -27,9 +27,10 @@ import java.time.format.DateTimeFormatter; import java.util.Objects; import java.util.UUID; import org.apache.commons.lang3.builder.EqualsBuilder; -import org.onap.aai.champcore.event.envelope.util.GsonUtil; -import com.google.gson.Gson; -import com.google.gson.annotations.SerializedName; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; public class ChampEventHeader { @@ -50,28 +51,26 @@ public class ChampEventHeader { } } - @SerializedName("request-id") + @JsonProperty("request-id") private String requestId; private String timestamp; - @SerializedName("source-name") + @JsonProperty("source-name") private String sourceName; - @SerializedName("event-type") + @JsonProperty("event-type") private String eventType; - @SerializedName("validation-entity-type") + @JsonProperty("validation-entity-type") private String validationEntityType; - @SerializedName("validation-top-entity-type") + @JsonProperty("validation-top-entity-type") private String validationTopEntityType; - @SerializedName("entity-link") + @JsonProperty("entity-link") private String entityLink; - private static final Gson gson = GsonUtil.createGson(); - public static class Builder { private final EventType eventType; @@ -127,7 +126,14 @@ public class ChampEventHeader { * @return a JSON format string representation of this object. */ public String toJson() { - return gson.toJson(this); + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(Include.NON_NULL); + + try { + return mapper.writeValueAsString(this); + } catch (JsonProcessingException e) { + return "Unmarshallable: " + e.getMessage(); + } } /////////////////////////////////////////////////////////////////////////// diff --git a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/util/GsonUtil.java b/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/util/GsonUtil.java deleted file mode 100644 index df64040..0000000 --- a/champ-lib/champ-core/src/main/java/org/onap/aai/champcore/event/envelope/util/GsonUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * ============LICENSE_START========================================== - * org.onap.aai - * =================================================================== - * Copyright © 2017 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017 Amdocs - * =================================================================== - * Licensed 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. - * ============LICENSE_END============================================ - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - */ -package org.onap.aai.champcore.event.envelope.util; - -import java.io.IOException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Optional; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -public class GsonUtil { - - /** - * All methods are static. - */ - private GsonUtil() { - // Do not instantiate - } - - /** - * Tell Gson how to handle Optional fields. This factory builds a Type Adapter for a class wrapped - * with Optional - * - */ - public static class OptionalTypeAdapterFactory implements TypeAdapterFactory { - @SuppressWarnings({"unchecked", "rawtypes"}) - @Override - public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { - Class<T> rawType = (Class<T>) type.getRawType(); - if (rawType != Optional.class) { - return null; - } - final ParameterizedType parameterizedType = (ParameterizedType) type.getType(); - final Type actualType = parameterizedType.getActualTypeArguments()[0]; - return new OptionalTypeAdapter(gson.getAdapter(TypeToken.get(actualType))); - } - } - - /** - * Implementation of the Optional Type Adapter - * - * @param <E> - */ - public static class OptionalTypeAdapter<E> extends TypeAdapter<Optional<E>> { - - private final TypeAdapter<E> adapter; - - public static final TypeAdapterFactory FACTORY = new OptionalTypeAdapterFactory(); - - /** - * @param adapter - */ - public OptionalTypeAdapter(TypeAdapter<E> adapter) { - this.adapter = adapter; - } - - @Override - public void write(JsonWriter out, Optional<E> value) throws IOException { - if (value != null && value.isPresent()) { // NOSONAR - adapter.write(out, value.get()); - } else { - boolean nullsAllowed = out.getSerializeNulls(); - if (nullsAllowed) { - out.setSerializeNulls(false); - } - out.nullValue(); - if (nullsAllowed) { - out.setSerializeNulls(true); - } - } - } - - @Override - public Optional<E> read(JsonReader in) throws IOException { - final JsonToken peek = in.peek(); - if (peek != JsonToken.NULL) { - return Optional.ofNullable(adapter.read(in)); - } - return Optional.empty(); - } - - } - - /** - * @return a new GsonBuilder with standard settings - */ - public static GsonBuilder createGsonBuilder() { - return new GsonBuilder().disableHtmlEscaping().setPrettyPrinting() - .registerTypeAdapterFactory(OptionalTypeAdapter.FACTORY); - } - - /** - * @return a new Gson instance - */ - public static Gson createGson() { - return createGsonBuilder().create(); - } -} diff --git a/champ-lib/champ-core/src/test/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelopeTest.java b/champ-lib/champ-core/src/test/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelopeTest.java index 2067ab4..56cf8ea 100644 --- a/champ-lib/champ-core/src/test/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelopeTest.java +++ b/champ-lib/champ-core/src/test/java/org/onap/aai/champcore/event/envelope/ChampEventEnvelopeTest.java @@ -26,17 +26,20 @@ import static org.junit.Assert.assertThat; import org.junit.Test; import org.onap.aai.champcore.event.ChampEvent; import org.onap.aai.champcore.model.ChampObject; +import org.onap.aai.champcore.model.ChampRelationship; import org.onap.aai.champcore.util.TestUtil; import org.skyscreamer.jsonassert.Customization; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.comparator.CustomComparator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; public class ChampEventEnvelopeTest { @Test - public void testEventEnvelopeBodyNoKey() throws Exception { - String expectedEnvelope = TestUtil.getFileAsString("event/event-envelope-no-key.json"); + public void testVertexEventEnvelopeBodyNoKey() throws Exception { + String expectedEnvelope = TestUtil.getFileAsString("event/vertex-event-envelope-no-key.json"); ChampEvent body = ChampEvent.builder().entity(new ChampObject.Builder("pserver").build()).build(); @@ -50,8 +53,8 @@ public class ChampEventEnvelopeTest { } @Test - public void testEventEnvelopeBodyWithKey() throws Exception { - String expectedEnvelope = TestUtil.getFileAsString("event/event-envelope-with-key.json"); + public void testVertexEventEnvelopeBodyWithKey() throws Exception { + String expectedEnvelope = TestUtil.getFileAsString("event/vertex-event-envelope-with-key.json"); ChampEvent body = ChampEvent.builder().entity(new ChampObject.Builder("pserver").key("1234").build()).build(); @@ -73,4 +76,29 @@ public class ChampEventEnvelopeTest { assertThat(envelope.getHeader().getRequestId(), is(envelope.getBody().getTransactionId())); } + + @Test + public void testEdgeEventEnvelope() throws Exception { + String expectedEnvelope = TestUtil.getFileAsString("event/edge-event-envelope.json"); + + ObjectMapper mapper = new ObjectMapper(); + ObjectNode objectNode = mapper.createObjectNode(); + objectNode.put("inVertexId", 5678); + objectNode.put("typeId", 1000); + objectNode.put("relationId", 2000); + objectNode.put("outVertexId", 1234); + + ChampRelationship relationship = + new ChampRelationship.Builder(new ChampObject.Builder("vserver").key("1234").build(), + new ChampObject.Builder("pserver").key("5678").build(), "test").key(objectNode).build(); + ChampEvent body = ChampEvent.builder().entity(relationship).build(); + + String envelope = new ChampEventEnvelope(body).toJson(); + + JSONAssert.assertEquals(expectedEnvelope, envelope, + new CustomComparator(JSONCompareMode.STRICT, new Customization("header.request-id", (o1, o2) -> true), + new Customization("header.timestamp", (o1, o2) -> true), + new Customization("body.timestamp", (o1, o2) -> true), + new Customization("body.transaction-id", (o1, o2) -> true))); + } } diff --git a/champ-lib/champ-core/src/test/resources/event/edge-event-envelope.json b/champ-lib/champ-core/src/test/resources/event/edge-event-envelope.json new file mode 100644 index 0000000..617bb9b --- /dev/null +++ b/champ-lib/champ-core/src/test/resources/event/edge-event-envelope.json @@ -0,0 +1,33 @@ +{ + "header": { + "timestamp": "20180625T131609Z", + "request-id": "ad65331f-932e-4cce-acc5-97ad88ce0483", + "source-name": "CHAMP", + "event-type": "update-notification-raw" + }, + "body": { + "timestamp": 1529932568889, + "relationship": { + "type": "test", + "properties": {}, + "source": { + "type": "vserver", + "properties": {}, + "object": true, + "relationship": false, + "key": "1234" + }, + "target": { + "type": "pserver", + "properties": {}, + "object": true, + "relationship": false, + "key": "5678" + }, + "object": false, + "relationship": true, + "key": "{\"inVertexId\":5678,\"typeId\":1000,\"relationId\":2000,\"outVertexId\":1234}" + }, + "transaction-id": "ad65331f-932e-4cce-acc5-97ad88ce0483" + } +}
\ No newline at end of file diff --git a/champ-lib/champ-core/src/test/resources/event/event-envelope-no-key.json b/champ-lib/champ-core/src/test/resources/event/event-envelope-no-key.json deleted file mode 100644 index f6f072b..0000000 --- a/champ-lib/champ-core/src/test/resources/event/event-envelope-no-key.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "header": { - "request-id": "bffbbf77-e6fc-4c5a-af02-2dbe1bef003f", - "timestamp": "20180320T164558Z", - "source-name": "CHAMP", - "event-type": "update-notification-raw" - }, - "body": { - "timestamp": 1521564357772, - "transaction-id": "bffbbf77-e6fc-4c5a-af02-2dbe1bef003f", - "vertex": { - "type": "pserver", - "properties": {} - } - } -}
\ No newline at end of file diff --git a/champ-lib/champ-core/src/test/resources/event/event-envelope-with-key.json b/champ-lib/champ-core/src/test/resources/event/event-envelope-with-key.json deleted file mode 100644 index 4e1c50a..0000000 --- a/champ-lib/champ-core/src/test/resources/event/event-envelope-with-key.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "header": { - "request-id": "bffbbf77-e6fc-4c5a-af02-2dbe1bef003f", - "timestamp": "20180320T164558Z", - "source-name": "CHAMP", - "event-type": "update-notification-raw" - }, - "body": { - "timestamp": 1521564357772, - "transaction-id": "bffbbf77-e6fc-4c5a-af02-2dbe1bef003f", - "vertex": { - "type": "pserver", - "key": "1234", - "properties": {} - } - } -}
\ No newline at end of file diff --git a/champ-lib/champ-core/src/test/resources/event/vertex-event-envelope-no-key.json b/champ-lib/champ-core/src/test/resources/event/vertex-event-envelope-no-key.json new file mode 100644 index 0000000..1935b07 --- /dev/null +++ b/champ-lib/champ-core/src/test/resources/event/vertex-event-envelope-no-key.json @@ -0,0 +1,19 @@ +{ + "header": { + "request-id": "d9b95d97-ac49-4425-8700-a704fa9fc88b", + "timestamp": "20180625T100903Z", + "source-name": "CHAMP", + "event-type": "update-notification-raw" + }, + "body": { + "timestamp": 1529921297261, + "vertex": { + "type": "pserver", + "properties": {}, + "object": true, + "relationship": false, + "key": "" + }, + "transaction-id": "d9b95d97-ac49-4425-8700-a704fa9fc88b" + } +}
\ No newline at end of file diff --git a/champ-lib/champ-core/src/test/resources/event/vertex-event-envelope-with-key.json b/champ-lib/champ-core/src/test/resources/event/vertex-event-envelope-with-key.json new file mode 100644 index 0000000..87e4ae1 --- /dev/null +++ b/champ-lib/champ-core/src/test/resources/event/vertex-event-envelope-with-key.json @@ -0,0 +1,19 @@ +{ + "header": { + "timestamp": "20180625T105153Z", + "request-id": "013c787f-86e1-42d2-a4f7-e736ed7e6c60", + "source-name": "CHAMP", + "event-type": "update-notification-raw" + }, + "body": { + "timestamp": 1529923913318, + "vertex": { + "type": "pserver", + "properties": {}, + "object": true, + "relationship": false, + "key": "1234" + }, + "transaction-id": "013c787f-86e1-42d2-a4f7-e736ed7e6c60" + } +}
\ No newline at end of file |