You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2026/06/19 06:47:32 UTC

(causeway) 01/11: CAUSEWAY-4024: updates the timestamp properly within the DTO

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch CAUSEWAY-4039
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 8908b5a0fd21b41770d01004a451ffc7cee2969b
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Jun 18 17:22:04 2026 +0100

    CAUSEWAY-4024: updates the timestamp properly within the DTO
---
 .../applib/util/schema/CommandDtoUtils.java        | 29 +++++++++++
 .../schema/CommandDtoUtils_copy_Approval_Test.java | 59 ++++++++++++++++++++++
 ...pproval_Test.marshals_command_copy.approved.txt | 14 +++++
 ..._Approval_Test.marshals_command_copy.input.yaml | 32 ++++++++++++
 .../CommandExportManagerMovementSupport.java       |  9 +++-
 5 files changed, 141 insertions(+), 2 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/causeway/applib/util/schema/CommandDtoUtils.java b/api/applib/src/main/java/org/apache/causeway/applib/util/schema/CommandDtoUtils.java
index 7a426769837..6ee9d70f70d 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/util/schema/CommandDtoUtils.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/util/schema/CommandDtoUtils.java
@@ -19,12 +19,18 @@
 package org.apache.causeway.applib.util.schema;
 
 import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
 import javax.xml.datatype.DatatypeConstants;
 import javax.xml.datatype.DatatypeFactory;
 import javax.xml.datatype.XMLGregorianCalendar;
@@ -285,6 +291,29 @@ private JsonUtils.JacksonCustomizer yamlCommandDtoCustomizer() {
         };
     }
 
+    public static CommandDto copy(final CommandDto commandDto) {
+        if (commandDto == null) {
+            return null;
+        }
+        try {
+            final JAXBContext jaxbContext = JaxbUtils.jaxbContextFor(CommandDto.class);
+
+            // create marshaller and unmarshaller per-call (they are NOT thread-safe)
+            final Marshaller marshaller = jaxbContext.createMarshaller();
+            final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+
+            final StringWriter sw = new StringWriter();
+            marshaller.marshal(commandDto, sw);
+
+            final String xml = sw.toString();
+            try (final StringReader sr = new StringReader(xml)) {
+                return (CommandDto) unmarshaller.unmarshal(sr);
+            }
+        } catch (JAXBException e) {
+            throw new RuntimeException("Failed to deep-copy CommandDto", e);
+        }
+    }
+
     public static class ImportedCommandDto {
 
         private CommandDto command;
diff --git a/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.java b/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.java
new file mode 100644
index 00000000000..28a08c34c64
--- /dev/null
+++ b/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.java
@@ -0,0 +1,59 @@
+/*
+ *  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.causeway.applib.util.schema;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.approvaltests.Approvals;
+import org.approvaltests.core.Options;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.util.StreamUtils;
+
+import org.apache.causeway.commons.io.DataSource;
+import org.apache.causeway.schema.cmd.v2.CommandDto;
+
+class CommandDtoUtils_copy_Approval_Test {
+
+    @Test
+    void marshals_command_copy() throws IOException {
+        // read YAML sample from classpath (existing test resource)
+        final String path = "CommandDtoUtils_copy_Approval_Test.marshals_command_copy.input.yaml";
+        try (InputStream is = getClass().getResourceAsStream(path)) {
+            final String yaml = StreamUtils.copyToString(is, StandardCharsets.UTF_8);
+
+            final List<CommandDto> commands = CommandDtoUtils.fromYaml(DataSource.ofStringUtf8(yaml));
+            Assertions.assertThat(commands).isNotEmpty();
+
+            final CommandDto original = commands.get(0);
+            final CommandDto copy = CommandDtoUtils.copy(original);
+
+            // ensure we actually created a different instance
+            Assertions.assertThat(copy).isNotSameAs(original);
+
+            // marshal the copy to its XML representation and approve
+            final String xml = CommandDtoUtils.dtoMapper().toString(copy);
+            Approvals.verify(xml);
+        }
+    }
+}
+
diff --git a/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.marshals_command_copy.approved.txt b/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.marshals_command_copy.approved.txt
new file mode 100644
index 00000000000..2d6411d2eb2
--- /dev/null
+++ b/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.marshals_command_copy.approved.txt
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<cmd:commandDto xmlns:com="https://causeway.apache.org/schema/common" xmlns:cmd="https://causeway.apache.org/schema/cmd">
+    <cmd:majorVersion>2</cmd:majorVersion>
+    <cmd:minorVersion>0</cmd:minorVersion>
+    <cmd:interactionId>08c210e1-55c0-4c45-83db-491f6581e621</cmd:interactionId>
+    <cmd:timestamp>2026-04-21T09:43:58.169Z</cmd:timestamp>
+    <cmd:username>estatio-admin</cmd:username>
+    <cmd:targets>
+        <com:oid type="outgoing.invoiceforlease.InvoiceForLease" id="419264"/>
+    </cmd:targets>
+    <cmd:member xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="cmd:actionDto" interactionType="action_invocation">
+        <cmd:logicalMemberIdentifier>outgoing.invoiceforlease.InvoiceForLease#approve</cmd:logicalMemberIdentifier>
+    </cmd:member>
+</cmd:commandDto>
diff --git a/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.marshals_command_copy.input.yaml b/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.marshals_command_copy.input.yaml
new file mode 100644
index 00000000000..81222d72ad7
--- /dev/null
+++ b/api/applib/src/test/java/org/apache/causeway/applib/util/schema/CommandDtoUtils_copy_Approval_Test.marshals_command_copy.input.yaml
@@ -0,0 +1,32 @@
+- majorVersion: "2"
+  minorVersion: "0"
+  interactionId: "08c210e1-55c0-4c45-83db-491f6581e621"
+  timestamp: "2026-04-21T09:43:58.169+00:00"
+  username: "estatio-admin"
+  targets:
+    oid:
+      - type: "outgoing.invoiceforlease.InvoiceForLease"
+        id: "419264"
+  member: !<ACT>
+    logicalMemberIdentifier: "outgoing.invoiceforlease.InvoiceForLease#approve"
+    interactionType: "action_invocation"
+- majorVersion: "2"
+  minorVersion: "0"
+  interactionId: "0e2ad15e-c1d8-48c5-8b63-b8cdde673715"
+  timestamp: "2026-04-21T09:45:07.409+00:00"
+  username: "estatio-admin"
+  targets:
+    oid:
+      - type: "outgoing.invoiceforlease.InvoiceForLease"
+        id: "419264"
+  member: !<ACT>
+    parameters:
+      parameter:
+        - localDate: "2026-04-20"
+          type: "localDate"
+          name: "Invoice Date"
+        - boolean: false
+          type: "boolean"
+          name: "Allow Invoice Date In Future"
+    logicalMemberIdentifier: "outgoing.invoiceforlease.InvoiceForLease#invoice"
+    interactionType: "action_invocation"
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/replay/CommandExportManagerMovementSupport.java b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/replay/CommandExportManagerMovementSupport.java
index 5b46a5c656d..cecafcc5296 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/replay/CommandExportManagerMovementSupport.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/replay/CommandExportManagerMovementSupport.java
@@ -28,8 +28,10 @@
 import java.util.stream.Collectors;
 
 import org.apache.causeway.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
+import org.apache.causeway.applib.util.schema.CommandDtoUtils;
 import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.extensions.commandlog.applib.dom.CommandLogEntry;
+import org.apache.causeway.schema.cmd.v2.CommandDto;
 
 class CommandExportManagerMovementSupport {
 
@@ -184,8 +186,11 @@ private static void setTimestamp(
     private static void updateCommandDtoTimestamp(
             final CommandLogEntry entry,
             final Timestamp timestamp) {
-        if (entry.getCommandDto() != null) {
-            entry.getCommandDto().setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
+        final var commandDto = entry.getCommandDto();
+        if (commandDto != null) {
+            var commandDtoCopy = CommandDtoUtils.copy(commandDto);
+            commandDtoCopy.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
+            entry.setCommandDto(commandDtoCopy);
         }
     }