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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 0125a13  CAMEL-17563: camel-attachments - Should use safe copy the 
internal map that holds the current attachments to avoid side-effects when 
exchange is copied such as wiretap etc.
0125a13 is described below

commit 0125a133a11cbdff57d459634bac276c10963387
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Feb 6 19:01:43 2022 +0100

    CAMEL-17563: camel-attachments - Should use safe copy the internal map that 
holds the current attachments to avoid side-effects when exchange is copied 
such as wiretap etc.
---
 .../org/apache/camel/attachment/AttachmentMap.java | 31 ++++++++
 .../camel/attachment/DefaultAttachmentMessage.java | 40 +++++-----
 .../org/apache/camel/attachment/WireTapTest.java   | 92 ++++++++++++++++++++++
 3 files changed, 145 insertions(+), 18 deletions(-)

diff --git 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMap.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMap.java
new file mode 100644
index 0000000..bf4eb24
--- /dev/null
+++ 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/AttachmentMap.java
@@ -0,0 +1,31 @@
+/*
+ * 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.camel.attachment;
+
+import java.util.LinkedHashMap;
+
+import org.apache.camel.SafeCopyProperty;
+
+public final class AttachmentMap extends LinkedHashMap<String, Attachment> 
implements SafeCopyProperty {
+
+    @Override
+    public SafeCopyProperty safeCopy() {
+        AttachmentMap clone = new AttachmentMap();
+        clone.putAll(this);
+        return clone;
+    }
+}
diff --git 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
index 3510fcb..0d3e541 100644
--- 
a/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
+++ 
b/components/camel-attachments/src/main/java/org/apache/camel/attachment/DefaultAttachmentMessage.java
@@ -16,7 +16,6 @@
  */
 package org.apache.camel.attachment;
 
-import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
@@ -25,6 +24,7 @@ import java.util.function.Supplier;
 import javax.activation.DataHandler;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.ExtendedExchange;
 import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Message;
 
@@ -37,9 +37,11 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     private static final String ATTACHMENT_OBJECTS = "CamelAttachmentObjects";
 
     private final Message delegate;
+    private final ExtendedExchange extendedExchange;
 
     public DefaultAttachmentMessage(Message delegate) {
         this.delegate = delegate;
+        this.extendedExchange = 
delegate.getExchange().adapt(ExtendedExchange.class);
     }
 
     @Override
@@ -185,7 +187,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public DataHandler getAttachment(String id) {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        AttachmentMap map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, AttachmentMap.class);
         if (map != null) {
             Attachment att = map.get(id);
             if (att != null) {
@@ -198,7 +200,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public Attachment getAttachmentObject(String id) {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        AttachmentMap map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, AttachmentMap.class);
         if (map != null) {
             return map.get(id);
         }
@@ -208,7 +210,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public Set<String> getAttachmentNames() {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        AttachmentMap map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, AttachmentMap.class);
         if (map != null) {
             return map.keySet();
         }
@@ -218,7 +220,7 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public void removeAttachment(String id) {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        AttachmentMap map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, AttachmentMap.class);
         if (map != null) {
             map.remove(id);
         }
@@ -227,10 +229,10 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public void addAttachment(String id, DataHandler content) {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        AttachmentMap map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, AttachmentMap.class);
         if (map == null) {
-            map = new LinkedHashMap<>();
-            getExchange().setProperty(ATTACHMENT_OBJECTS, map);
+            map = new AttachmentMap();
+            extendedExchange.setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
         }
         map.put(id, new DefaultAttachment(content));
     }
@@ -238,10 +240,10 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public void addAttachmentObject(String id, Attachment content) {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        AttachmentMap map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, AttachmentMap.class);
         if (map == null) {
-            map = new LinkedHashMap<>();
-            getExchange().setProperty(ATTACHMENT_OBJECTS, map);
+            map = new AttachmentMap();
+            extendedExchange.setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
         }
         map.put(id, content);
     }
@@ -249,9 +251,9 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public Map<String, DataHandler> getAttachments() {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        Map<String, Attachment> map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, Map.class);
         if (map != null) {
-            Map<String, DataHandler> answer = new HashMap<>();
+            Map<String, DataHandler> answer = new LinkedHashMap<>();
             map.forEach((id, att) -> answer.put(id, att.getDataHandler()));
             return answer;
         }
@@ -261,25 +263,27 @@ public final class DefaultAttachmentMessage implements 
AttachmentMessage {
     @Override
     @SuppressWarnings("unchecked")
     public Map<String, Attachment> getAttachmentObjects() {
-        return getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        return extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, 
Map.class);
     }
 
     @Override
     public void setAttachments(Map<String, DataHandler> attachments) {
-        Map<String, Attachment> map = new HashMap<>();
+        AttachmentMap map = new AttachmentMap();
         attachments.forEach((id, dh) -> map.put(id, new 
DefaultAttachment(dh)));
-        getExchange().setProperty(ATTACHMENT_OBJECTS, map);
+        extendedExchange.setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
     }
 
     @Override
     public void setAttachmentObjects(Map<String, Attachment> attachments) {
-        getExchange().setProperty(ATTACHMENT_OBJECTS, attachments);
+        AttachmentMap map = new AttachmentMap();
+        map.putAll(attachments);
+        extendedExchange.setSafeCopyProperty(ATTACHMENT_OBJECTS, map);
     }
 
     @Override
     @SuppressWarnings("unchecked")
     public boolean hasAttachments() {
-        Map<String, Attachment> map = 
getExchange().getProperty(ATTACHMENT_OBJECTS, Map.class);
+        AttachmentMap map = 
extendedExchange.getSafeCopyProperty(ATTACHMENT_OBJECTS, AttachmentMap.class);
         return map != null && !map.isEmpty();
     }
 
diff --git 
a/components/camel-attachments/src/test/java/org/apache/camel/attachment/WireTapTest.java
 
b/components/camel-attachments/src/test/java/org/apache/camel/attachment/WireTapTest.java
new file mode 100644
index 0000000..19271ae
--- /dev/null
+++ 
b/components/camel-attachments/src/test/java/org/apache/camel/attachment/WireTapTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.camel.attachment;
+
+import java.io.File;
+import java.util.Iterator;
+
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class WireTapTest extends CamelTestSupport {
+
+    @Test
+    public void testWireTap() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+        getMockEndpoint("mock:tap").expectedMessageCount(1);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        Exchange e1 = 
getMockEndpoint("mock:result").getReceivedExchanges().get(0);
+        Exchange e2 = 
getMockEndpoint("mock:tap").getReceivedExchanges().get(0);
+
+        AttachmentMessage am1 = e1.getMessage(AttachmentMessage.class);
+        AttachmentMessage am2 = e2.getMessage(AttachmentMessage.class);
+
+        // original has 1 attachment
+        Assertions.assertTrue(am1.hasAttachments());
+        Assertions.assertEquals(1, am1.getAttachmentNames().size());
+        Assertions.assertEquals("message1.xml", 
am1.getAttachmentNames().iterator().next());
+
+        // tap has 2 because of 1 original and 1 added afterwards
+        Assertions.assertTrue(am2.hasAttachments());
+        Assertions.assertEquals(2, am2.getAttachmentNames().size());
+        Iterator<String> it = am2.getAttachmentNames().iterator();
+        Assertions.assertEquals("message1.xml", it.next());
+        Assertions.assertEquals("message2.xml", it.next());
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws 
Exception {
+                                AttachmentMessage am = 
exchange.getMessage(AttachmentMessage.class);
+                                am.addAttachment("message1.xml",
+                                        new DataHandler(new FileDataSource(new 
File("src/test/data/message1.xml"))));
+                            }
+                        })
+                        .wireTap("direct:tap")
+                        .to("mock:result");
+
+                from("direct:tap")
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws 
Exception {
+                                AttachmentMessage am = 
exchange.getMessage(AttachmentMessage.class);
+                                am.addAttachmentObject("message2.xml",
+                                        new DefaultAttachment(new 
FileDataSource(new File("src/test/data/message2.xml"))));
+                            }
+                        }).to("mock:tap");
+            }
+        };
+    }
+}

Reply via email to