From 84e946ba8c771d91a84a6a2e3c608505204083ff Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 20 Nov 2023 17:40:08 +0100 Subject: [PATCH 1/4] fix: KA-389: Remoting Agent: Merging of Arrays does not always work as expected --- .../tractusx/agents/remoting/Invocation.java | 80 ++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java b/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java index 62d1a835..c9d9cf80 100644 --- a/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java +++ b/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java @@ -68,6 +68,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -832,6 +833,70 @@ protected Iterator> produceBatches(BindingHost hos return batches.values().iterator(); } + /** + * merges two given ObjectNodes + * + * @param source the ObjectNodes to be merged + * @param target the ObjectNodes where source needs to be merged into + */ + public static ObjectNode mergeObjectNodes(ObjectNode target, ObjectNode source) { + if (source == null) { + return target; + } + + Iterator> fields = source.fields(); + while (fields.hasNext()) { + Entry entry = fields.next(); + String key = entry.getKey(); + JsonNode sourceValue = entry.getValue(); + JsonNode targetValue = target.get(key); + + if (targetValue != null && targetValue.isObject() && sourceValue.isObject()) { + // Recursively merge nested objects + mergeObjectNodes((ObjectNode) targetValue, (ObjectNode) sourceValue); + } else if (targetValue != null && targetValue.isArray() && sourceValue.isArray()) { + // Merge arrays + mergeArrays((ArrayNode) targetValue, (ArrayNode) sourceValue); + } else { + // Add the field to target + target.set(key, sourceValue); + } + } + + return target; + } + + /** + * merges two given ArrayNodes + * + * @param source the ArrayNode to be merged + * @param target the ArrayNode where source needs to be merged into + */ + private static void mergeArrays(ArrayNode target, ArrayNode source) { + int sourceLength = source.size(); + int targetLength = target.size(); + + for (int i = 0; i < sourceLength; i++) { + // target shorter than source? + if (targetLength < i) { + target.add(source); + return; + } + JsonNode targetElement = target.get(i); + JsonNode sourceElement = source.get(i); + if (targetElement.isObject() && sourceElement.isObject()) { + // Recursively merge JSON objects + mergeObjectNodes((ObjectNode) targetElement, (ObjectNode) sourceElement); + } else if (targetElement.isArray() && sourceElement.isArray()) { + // Recursively merge arrays + mergeArrays((ArrayNode) targetElement, (ArrayNode) sourceElement); + } else { + target.add(source); + } + } + } + + /** * sets a given node under a possible recursive path * @@ -847,17 +912,10 @@ public static void setNode(ObjectMapper objectMapper, ObjectNode finalInput, Str JsonNode traverse = finalInput; int depth = 0; if (argPath.length == depth) { - // https://jira.catena-x.net/browse/TEST-1170 make sure top-level updates are also merged and not overwritten - ObjectReader updater = objectMapper.readerForUpdating(finalInput); - try { - render = updater.readValue(render); - } catch (IOException e) { - throw new RuntimeException(e); - } - finalInput.setAll((ObjectNode) render); + finalInput = (ObjectNode) mergeObjectNodes(finalInput, (ObjectNode) render); return; } - for (String argField : argPath) { + for (String argField : argPath) { if (depth != argPath.length - 1) { if (hasField(traverse, argField)) { JsonNode next = getField(traverse, argField); @@ -876,8 +934,8 @@ public static void setNode(ObjectMapper objectMapper, ObjectNode finalInput, Str setObject(objectMapper, traverse, argField, render); } depth++; - } // set argument in input - } + } // set argument in input + } } /** From 38e0a056bd460bd0079e20cbe12afba45e5bd75f Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 29 Nov 2023 09:08:49 +0100 Subject: [PATCH 2/4] fix/63-ArrayMerge --- .../tractusx/agents/remoting/Invocation.java | 6 +- .../agents/remoting/MergeObjectNodesTest.java | 45 ++++++++++ .../test/resources/MergeObjectNodesIn1.json | 83 ++++++++++++++++++ .../test/resources/MergeObjectNodesIn2.json | 31 +++++++ .../resources/MergeObjectNodesResult.json | 84 +++++++++++++++++++ 5 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 remoting/src/test/java/org/eclipse/tractusx/agents/remoting/MergeObjectNodesTest.java create mode 100644 remoting/src/test/resources/MergeObjectNodesIn1.json create mode 100644 remoting/src/test/resources/MergeObjectNodesIn2.json create mode 100644 remoting/src/test/resources/MergeObjectNodesResult.json diff --git a/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java b/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java index c9d9cf80..1b62e1f1 100644 --- a/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java +++ b/remoting/src/main/java/org/eclipse/tractusx/agents/remoting/Invocation.java @@ -878,8 +878,8 @@ private static void mergeArrays(ArrayNode target, ArrayNode source) { for (int i = 0; i < sourceLength; i++) { // target shorter than source? - if (targetLength < i) { - target.add(source); + if (targetLength < i + 1) { + target.add(source.get(i)); return; } JsonNode targetElement = target.get(i); @@ -891,7 +891,7 @@ private static void mergeArrays(ArrayNode target, ArrayNode source) { // Recursively merge arrays mergeArrays((ArrayNode) targetElement, (ArrayNode) sourceElement); } else { - target.add(source); + target.set(i, source.get(i)); } } } diff --git a/remoting/src/test/java/org/eclipse/tractusx/agents/remoting/MergeObjectNodesTest.java b/remoting/src/test/java/org/eclipse/tractusx/agents/remoting/MergeObjectNodesTest.java new file mode 100644 index 00000000..fade64ef --- /dev/null +++ b/remoting/src/test/java/org/eclipse/tractusx/agents/remoting/MergeObjectNodesTest.java @@ -0,0 +1,45 @@ +package org.eclipse.tractusx.agents.remoting; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.InputStream; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class MergeObjectNodesTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void testMerge() throws Exception { + + // Load input resources + JsonNode input1 = loadJsonResource("MergeObjectNodesIn1.json"); + JsonNode input2 = loadJsonResource("MergeObjectNodesIn2.json"); + + // Load expected result resource + JsonNode expectedResult = loadJsonResource("MergeObjectNodesResult.json"); + + // Invoke merge method with the input resources + input1 = Invocation.mergeObjectNodes((ObjectNode)input1, (ObjectNode)input2); + + // Assert the result against the expected result + assertEquals(expectedResult, input1); + } + + private JsonNode loadJsonResource(String resourceName) throws IOException { + // Use the class loader to load the resource + ClassLoader classLoader = getClass().getClassLoader(); + try (InputStream inputStream = classLoader.getResourceAsStream(resourceName)) { + + // Parse JSON content into ObjectNode + return objectMapper.readTree(inputStream); + } + } + +} diff --git a/remoting/src/test/resources/MergeObjectNodesIn1.json b/remoting/src/test/resources/MergeObjectNodesIn1.json new file mode 100644 index 00000000..9c96c86f --- /dev/null +++ b/remoting/src/test/resources/MergeObjectNodesIn1.json @@ -0,0 +1,83 @@ +{ + "Koepfchen in das Wasser":"Schwänzchen in die Höh", + "content":{ + "endurancePredictorInputs":[ + { + "alle":"meine Entchen", + "classifiedLoadSpectrumGearSet":{ + "schwimmen":"auf dem See", + "metadata":{ + "projectDescription":"pnr_76543", + "routeDescription":"logged", + "status":{ + "mileage":865432, + "operatingHours":32137.9, + "date":"2023-02-28T01:27:20.020Z" + }, + "componentDescription":"GearSet" + }, + "bammId":"urn:bamm:io.openmanufacturing.digitaltwin:1.0.0#ClassifiedLoadSpectrum", + "targetComponentId":"urn:uuid:2", + "header":{ + "countingMethod":"TimeAtLevel", + "countingValue":"Time", + "channels":[ + { + "channelName":"TC_SU", + "unit":"unit:degreeCelsius", + "lowerLimit":0, + "upperLimit":640, + "numberOfBins":128 + } + ], + "countingUnit":"unit:secondUnitOfTime" + }, + "body":{ + "counts":{ + "countsName":"Time", + "countsList":[ + 34968.93, + 739782.51, + 4013185.15, + 4.675505556E7, + 2.526895835E7, + 8649735.95, + 9383635.35, + 1.918926077E7, + 1353867.54 + ] + }, + "classes":[ + { + "className":"TC_SU-class", + "classList":[ + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22 + ] + } + ] + } + }, + "componentId":"urn:uuid:2" + } + ] + }, + "header":{ + "classification":"RemainingUsefulLifePredictor", + "targetDate":"2042-04-06T05:53:40.040Z", + "timeStamp":"2022-12-03T02:09:20.020Z", + "senderBPN":"bpn:legal:BPNLOEM", + "severity":"MINOR", + "recipientBPN":"bpn:legal:BPNLSUPPLIER", + "senderAddress":"edc://sender", + "status":"SENT", + "recipientAddress":"edc://recipient" + } +} \ No newline at end of file diff --git a/remoting/src/test/resources/MergeObjectNodesIn2.json b/remoting/src/test/resources/MergeObjectNodesIn2.json new file mode 100644 index 00000000..b7b8ea2d --- /dev/null +++ b/remoting/src/test/resources/MergeObjectNodesIn2.json @@ -0,0 +1,31 @@ +{ + "Koepfchen in das Wasser":"Schwänzchen in die Höh", + "content":{ + "endurancePredictorInputs":[ + { + "alle":"meine Entchen", + "classifiedLoadSpectrumGearSet":{ + "schwimmen":"auf dem See", + "body":{ + "counts":{ + "countsName":"Time", + "countsList": [ + 4711.0815, + 739782.51, + 4013185.15, + 4.675505556E7, + 2.526895835E7, + 8649735.95, + 9383635.35, + 1.918926077E7, + 1353867.54, + 123.456 + ] + } + } + } + + } + ] + } +} \ No newline at end of file diff --git a/remoting/src/test/resources/MergeObjectNodesResult.json b/remoting/src/test/resources/MergeObjectNodesResult.json new file mode 100644 index 00000000..59cab61b --- /dev/null +++ b/remoting/src/test/resources/MergeObjectNodesResult.json @@ -0,0 +1,84 @@ +{ + "Koepfchen in das Wasser":"Schwänzchen in die Höh", + "content":{ + "endurancePredictorInputs":[ + { + "alle":"meine Entchen", + "classifiedLoadSpectrumGearSet":{ + "schwimmen":"auf dem See", + "metadata":{ + "projectDescription":"pnr_76543", + "routeDescription":"logged", + "status":{ + "mileage":865432, + "operatingHours":32137.9, + "date":"2023-02-28T01:27:20.020Z" + }, + "componentDescription":"GearSet" + }, + "bammId":"urn:bamm:io.openmanufacturing.digitaltwin:1.0.0#ClassifiedLoadSpectrum", + "targetComponentId":"urn:uuid:2", + "header":{ + "countingMethod":"TimeAtLevel", + "countingValue":"Time", + "channels":[ + { + "channelName":"TC_SU", + "unit":"unit:degreeCelsius", + "lowerLimit":0, + "upperLimit":640, + "numberOfBins":128 + } + ], + "countingUnit":"unit:secondUnitOfTime" + }, + "body":{ + "counts":{ + "countsName":"Time", + "countsList":[ + 4711.0815, + 739782.51, + 4013185.15, + 4.675505556E7, + 2.526895835E7, + 8649735.95, + 9383635.35, + 1.918926077E7, + 1353867.54, + 123.456 + ] + }, + "classes":[ + { + "className":"TC_SU-class", + "classList":[ + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22 + ] + } + ] + } + }, + "componentId":"urn:uuid:2" + } + ] + }, + "header":{ + "classification":"RemainingUsefulLifePredictor", + "targetDate":"2042-04-06T05:53:40.040Z", + "timeStamp":"2022-12-03T02:09:20.020Z", + "senderBPN":"bpn:legal:BPNLOEM", + "severity":"MINOR", + "recipientBPN":"bpn:legal:BPNLSUPPLIER", + "senderAddress":"edc://sender", + "status":"SENT", + "recipientAddress":"edc://recipient" + } +} \ No newline at end of file From f8723788ae8dce2bfe2b9b8d6fd6574ee51f1489 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 30 Nov 2023 09:22:22 +0100 Subject: [PATCH 3/4] fix: preventing IllegalArbumentException when starting rdf4j server --- remoting/resources/web/rdf4j.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/remoting/resources/web/rdf4j.xml b/remoting/resources/web/rdf4j.xml index 45a34d8d..ebff020f 100644 --- a/remoting/resources/web/rdf4j.xml +++ b/remoting/resources/web/rdf4j.xml @@ -24,6 +24,9 @@ RDF4J Server + + spring_web + RDF4J Server spring_web