You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/08/06 13:18:39 UTC
(camel) branch worktree-fix+CAMEL-24365 created (now 45a0915d563b)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a change to branch worktree-fix+CAMEL-24365
in repository https://gitbox.apache.org/repos/asf/camel.git
at 45a0915d563b CAMEL-24365: Copy attachment trait from IN when creating a fresh OUT message
This branch includes the following new commits:
new 45a0915d563b CAMEL-24365: Copy attachment trait from IN when creating a fresh OUT message
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel) 01/01: CAMEL-24365: Copy attachment trait from IN when creating a fresh OUT message
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch worktree-fix+CAMEL-24365
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 45a0915d563bc101f1f8ea08e9f4c7c182d2c0b3
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Aug 6 15:18:24 2026 +0200
CAMEL-24365: Copy attachment trait from IN when creating a fresh OUT message
Since CAMEL-21755 moved attachments from Exchange-level properties to
Message-level traits, any producer that creates a new OUT message via
exchange.getOut() silently drops attachments. Fix by copying the
ATTACHMENTS trait in AbstractExchange.newOutMessage(), and remove the
per-component workaround added in CAMEL-23193 for camel-jms.
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
.../attachment/AttachmentOnOutMessageTest.java | 75 ++++++++++++++++++++++
.../org/apache/camel/component/jms/JmsMessage.java | 7 --
.../org/apache/camel/support/AbstractExchange.java | 10 +++
3 files changed, 85 insertions(+), 7 deletions(-)
diff --git a/components/camel-attachments/src/test/java/org/apache/camel/attachment/AttachmentOnOutMessageTest.java b/components/camel-attachments/src/test/java/org/apache/camel/attachment/AttachmentOnOutMessageTest.java
new file mode 100644
index 000000000000..520eaa06172a
--- /dev/null
+++ b/components/camel-attachments/src/test/java/org/apache/camel/attachment/AttachmentOnOutMessageTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 jakarta.activation.DataHandler;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test that attachments on the IN message survive when a producer creates a fresh OUT message via exchange.getOut().
+ * This is a regression test for CAMEL-24365.
+ */
+class AttachmentOnOutMessageTest extends CamelTestSupport {
+
+ @Test
+ void testAttachmentSurvivesOutMessage() throws Exception {
+ getMockEndpoint("mock:result").expectedMessageCount(1);
+
+ template.sendBody("direct:start", "Hello");
+
+ MockEndpoint.assertIsSatisfied(context);
+
+ Exchange received = getMockEndpoint("mock:result").getReceivedExchanges().get(0);
+ AttachmentMessage am = received.getMessage(AttachmentMessage.class);
+ assertTrue(am.hasAttachments());
+ assertEquals(1, am.getAttachmentNames().size());
+ assertTrue(am.getAttachmentNames().contains("test.txt"));
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .process(exchange -> {
+ AttachmentMessage msg = exchange.getMessage(AttachmentMessage.class);
+ msg.addAttachment("test.txt", new DataHandler("content", "text/plain"));
+ })
+ .process(exchange -> {
+ // simulate what HttpProducer does: create a fresh OUT message
+ exchange.getOut().setBody("response");
+ })
+ .process(exchange -> {
+ // after pipeline promotes OUT to IN, attachments must still be present
+ AttachmentMessage msg = exchange.getMessage(AttachmentMessage.class);
+ exchange.getMessage().setHeader("attachmentCount", msg.getAttachmentNames().size());
+ })
+ .to("mock:result");
+ }
+ };
+ }
+}
diff --git a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java
index e0d67a1f3596..38bf1c9cf1a3 100644
--- a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java
+++ b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsMessage.java
@@ -17,7 +17,6 @@
package org.apache.camel.component.jms;
import java.io.File;
-import java.util.LinkedHashMap;
import java.util.Map;
import jakarta.jms.Destination;
@@ -121,12 +120,6 @@ public class JmsMessage extends DefaultMessage {
if (that.hasHeaders()) {
getHeaders().putAll(that.getHeaders());
}
-
- // copy attachments
- Map<String, Object> attachments = (Map<String, Object>) that.getPayloadForTrait(MessageTrait.ATTACHMENTS);
- if (attachments != null) {
- setPayloadForTrait(MessageTrait.ATTACHMENTS, new LinkedHashMap<>(attachments));
- }
}
public JmsBinding getBinding() {
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
index 10cc6ee63742..ce4bf980344b 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/AbstractExchange.java
@@ -19,6 +19,7 @@ package org.apache.camel.support;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -514,10 +515,19 @@ abstract class AbstractExchange implements Exchange, ExchangeExtension {
return out;
}
+ @SuppressWarnings("unchecked")
private Message newOutMessage() {
if (in != null) {
Message answer = in.newInstance();
CamelContextAware.trySetCamelContext(answer, getContext());
+ // copy attachments from IN so they survive when a producer swaps in a fresh OUT
+ if (in.hasTrait(MessageTrait.ATTACHMENTS)) {
+ Map<String, Object> attachments
+ = (Map<String, Object>) in.getPayloadForTrait(MessageTrait.ATTACHMENTS);
+ if (attachments != null) {
+ answer.setPayloadForTrait(MessageTrait.ATTACHMENTS, new LinkedHashMap<>(attachments));
+ }
+ }
return answer;
} else {
return new DefaultMessage(getContext());