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

rmaucher pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 8dc26c11c2718406713e65ad59329f0d18d948f6
Author: opencode <[email protected]>
AuthorDate: Fri Oct 9 16:09:00 2026 +0200

    Detach the unique id when shallow cloning a channel message
    
    The shallow clone shared the unique id byte array with the original
    message. TwoPhaseCommitInterceptor builds its confirmation message
    with this clone when deepclone is disabled and then generates a new
    id into the confirmation's id array in place, which overwrote the id
    of the caller's message. The wire data was already serialized at that
    point, but the in-memory id returned to the application by
    Channel.send afterwards identified the confirmation rather than the
    message that was sent.
    
    The clone now copies the id array, so a derived message can be given
    its own id without affecting the original. Fragment members still
    share the id of their original message by value, which is all the
    interceptors and the receiver side keying ever relied on.
---
 .../org/apache/catalina/tribes/io/ChannelData.java |  7 +++++-
 .../TestTwoPhaseCommitInterceptor.java             | 25 ++++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/catalina/tribes/io/ChannelData.java 
b/java/org/apache/catalina/tribes/io/ChannelData.java
index aad66da3db..0f798c8d85 100644
--- a/java/org/apache/catalina/tribes/io/ChannelData.java
+++ b/java/org/apache/catalina/tribes/io/ChannelData.java
@@ -310,7 +310,9 @@ public class ChannelData implements ChannelMessage {
     }
 
     /**
-     * Create a shallow clone, only the data gets recreated
+     * Create a shallow clone, the message data and the unique id are 
recreated so that the
+     * clone, for example a confirmation message derived from the original, 
can be given a new
+     * id without modifying the original message in place.
      *
      * @return ChannelData clone
      */
@@ -323,6 +325,9 @@ public class ChannelData implements ChannelMessage {
             // Cannot happen
             throw new AssertionError();
         }
+        if (this.uniqueId != null) {
+            clone.uniqueId = this.uniqueId.clone();
+        }
         if (this.message != null) {
             clone.message = new XByteBuffer(this.message.getBytesDirect(), 
false);
             clone.message.setLength(this.message.getLength());
diff --git 
a/test/org/apache/catalina/tribes/group/interceptors/TestTwoPhaseCommitInterceptor.java
 
b/test/org/apache/catalina/tribes/group/interceptors/TestTwoPhaseCommitInterceptor.java
index 7f33f2ce2d..b5ecc22a29 100644
--- 
a/test/org/apache/catalina/tribes/group/interceptors/TestTwoPhaseCommitInterceptor.java
+++ 
b/test/org/apache/catalina/tribes/group/interceptors/TestTwoPhaseCommitInterceptor.java
@@ -121,6 +121,31 @@ public class TestTwoPhaseCommitInterceptor {
         Assert.assertNull(chain.payloads.get(1));
     }
 
+    @Test
+    public void testShallowCloneKeepsOriginalUniqueIdIntact() throws Exception 
{
+        TwoPhaseCommitInterceptor interceptor = new 
TwoPhaseCommitInterceptor();
+        interceptor.setDeepclone(false);
+        RecordingSendChain chain = new RecordingSendChain();
+        GroupChannel channel = new GroupChannel();
+        interceptor.setChannel(channel);
+        interceptor.setNext(chain);
+
+        MemberImpl member = new MemberImpl("localhost", 4000, -1);
+        ChannelData msg = createMessage(member);
+        byte[] originalId = msg.getUniqueId();
+        byte[] savedId = originalId.clone();
+
+        interceptor.sendMessage(new Member[] { member }, msg, null);
+
+        Assert.assertEquals(2, chain.messages.size());
+        Assert.assertArrayEquals("Building the confirmation must not modify 
the original id in place",
+                savedId, msg.getUniqueId());
+        Assert.assertNotSame("The clone must not share the id array with the 
original",
+                originalId, chain.messages.get(1).getUniqueId());
+        Assert.assertFalse("The confirmation must carry a different id",
+                Arrays.equals(savedId, chain.messages.get(1).getUniqueId()));
+    }
+
     @Test
     public void testOriginalSendFailureSkipsConfirmation() throws Exception {
         TwoPhaseCommitInterceptor interceptor = new 
TwoPhaseCommitInterceptor();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to