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

oscerd 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 b2658f6b407d CAMEL-25340: camel-spring-ws - apply the 
HeaderFilterStrategy to outbound SOAP headers (#27377)
b2658f6b407d is described below

commit b2658f6b407d77229974e4bc1a3c428efc31bd65
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Oct 6 09:25:12 2026 +0200

    CAMEL-25340: camel-spring-ws - apply the HeaderFilterStrategy to outbound 
SOAP headers (#27377)
    
    The producer mapped Exchange headers onto the outbound SOAP message without
    consulting a HeaderFilterStrategy, so Camel-internal headers could travel 
to the
    remote service. The outbound side now applies the configured strategy, 
supplied
    through a Supplier<HeaderFilterStrategy> so the endpoint's own strategy is 
used
    rather than a snapshot taken at construction time.
    
    Also documents which header filter strategy applies on each direction.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 .../apache/camel/catalog/components/spring-ws.json |   2 +-
 .../camel/catalog/docs/spring-ws-component.adoc    |  11 ++
 .../camel/component/spring/ws/spring-ws.json       |   2 +-
 .../src/main/docs/spring-ws-component.adoc         |  11 ++
 .../spring/ws/SpringWebserviceComponent.java       |   4 +-
 .../spring/ws/SpringWebserviceConfiguration.java   |   2 +-
 .../spring/ws/filter/impl/BasicMessageFilter.java  |  43 +++++++-
 .../spring/ws/OutboundSoapHeaderFilterTest.java    | 120 +++++++++++++++++++++
 .../ws/filter/impl/BasicMessageFilterTest.java     |  58 ++++++++++
 .../ws/OutboundSoapHeaderFilterTest-context.xml    |  84 +++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_18.adoc    |  12 +++
 .../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc    |  12 +++
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  12 +++
 .../SpringWebserviceEndpointBuilderFactory.java    |  18 ++--
 14 files changed, 379 insertions(+), 12 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ws.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ws.json
index 1d78d3578166..2e87dfbad95b 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ws.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ws.json
@@ -43,7 +43,7 @@
     "type": { "index": 0, "kind": "path", "displayName": "Type", "group": 
"consumer", "label": "consumer", "required": false, "type": "enum", "javaType": 
"org.apache.camel.component.spring.ws.type.EndpointMappingType", "enum": [ 
"ROOT_QNAME", "ACTION", "TO", "SOAP_ACTION", "XPATHRESULT", "URI", "URI_PATH", 
"BEANNAME" ], "deprecated": false, "autowired": false, "secret": false, 
"configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField" [...]
     "lookupKey": { "index": 1, "kind": "path", "displayName": "Lookup Key", 
"group": "consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Endpoint mapping key if 
endpoint mapping is used" },
     "webServiceEndpointUri": { "index": 2, "kind": "path", "displayName": "Web 
Service Endpoint Uri", "group": "producer", "label": "producer", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "The default Web Service 
endpoint uri to use for the producer." },
-    "headerFilterStrategy": { "index": 3, "kind": "parameter", "displayName": 
"Header Filter Strategy", "group": "common", "label": "common", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "To use a custom 
HeaderFilterStrategy to filter head [...]
+    "headerFilterStrategy": { "index": 3, "kind": "parameter", "displayName": 
"Header Filter Strategy", "group": "common", "label": "common", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "To use a custom 
HeaderFilterStrategy to filter head [...]
     "messageFilter": { "index": 4, "kind": "parameter", "displayName": 
"Message Filter", "group": "common", "label": "", "required": false, "type": 
"object", "javaType": 
"org.apache.camel.component.spring.ws.filter.MessageFilter", "deprecated": 
false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Option to provide a 
custom MessageFilter. For example wh [...]
     "messageIdStrategy": { "index": 5, "kind": "parameter", "displayName": 
"Message Id Strategy", "group": "common", "label": "common", "required": false, 
"type": "object", "javaType": 
"org.springframework.ws.soap.addressing.messageid.MessageIdStrategy", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Option to provide a 
custom Messa [...]
     "endpointDispatcher": { "index": 6, "kind": "parameter", "displayName": 
"Endpoint Dispatcher", "group": "consumer", "label": "consumer", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.component.spring.ws.bean.CamelEndpointDispatcher", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Spring 
org.springframework.w [...]
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-ws-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-ws-component.adoc
index e53b08f6353d..8a54d2036e31 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-ws-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-ws-component.adoc
@@ -335,6 +335,17 @@ protected void doProcessSoapAttachements(Message inOrOut, 
SoapMessage response)
 }
 ----
 
+The `BasicMessageFilter` that the component creates for an endpoint
+applies the endpoint's `headerFilterStrategy` to the message headers it
+writes into the SOAP header, so by default headers whose names start
+with `Camel` or `camel` are not written. A message filter you provide
+yourself, global or local, does not know which endpoint it runs for: a
+`BasicMessageFilter`, or a subclass of it, created with the no-argument
+constructor applies a `SpringWebserviceHeaderFilterStrategy`, whatever
+`headerFilterStrategy` the endpoint is configured with. To apply another
+strategy, create it with the 
`BasicMessageFilter(Supplier<HeaderFilterStrategy>)`
+constructor, or override `getHeaderFilterStrategy()`.
+
 === Using a custom MessageSender and MessageFactory
 
 A custom message sender or factory in the registry can be referenced
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/generated/resources/META-INF/org/apache/camel/component/spring/ws/spring-ws.json
 
b/components/camel-spring-parent/camel-spring-ws/src/generated/resources/META-INF/org/apache/camel/component/spring/ws/spring-ws.json
index 1d78d3578166..2e87dfbad95b 100644
--- 
a/components/camel-spring-parent/camel-spring-ws/src/generated/resources/META-INF/org/apache/camel/component/spring/ws/spring-ws.json
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/generated/resources/META-INF/org/apache/camel/component/spring/ws/spring-ws.json
@@ -43,7 +43,7 @@
     "type": { "index": 0, "kind": "path", "displayName": "Type", "group": 
"consumer", "label": "consumer", "required": false, "type": "enum", "javaType": 
"org.apache.camel.component.spring.ws.type.EndpointMappingType", "enum": [ 
"ROOT_QNAME", "ACTION", "TO", "SOAP_ACTION", "XPATHRESULT", "URI", "URI_PATH", 
"BEANNAME" ], "deprecated": false, "autowired": false, "secret": false, 
"configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField" [...]
     "lookupKey": { "index": 1, "kind": "path", "displayName": "Lookup Key", 
"group": "consumer", "label": "consumer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Endpoint mapping key if 
endpoint mapping is used" },
     "webServiceEndpointUri": { "index": 2, "kind": "path", "displayName": "Web 
Service Endpoint Uri", "group": "producer", "label": "producer", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "The default Web Service 
endpoint uri to use for the producer." },
-    "headerFilterStrategy": { "index": 3, "kind": "parameter", "displayName": 
"Header Filter Strategy", "group": "common", "label": "common", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "To use a custom 
HeaderFilterStrategy to filter head [...]
+    "headerFilterStrategy": { "index": 3, "kind": "parameter", "displayName": 
"Header Filter Strategy", "group": "common", "label": "common", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "To use a custom 
HeaderFilterStrategy to filter head [...]
     "messageFilter": { "index": 4, "kind": "parameter", "displayName": 
"Message Filter", "group": "common", "label": "", "required": false, "type": 
"object", "javaType": 
"org.apache.camel.component.spring.ws.filter.MessageFilter", "deprecated": 
false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Option to provide a 
custom MessageFilter. For example wh [...]
     "messageIdStrategy": { "index": 5, "kind": "parameter", "displayName": 
"Message Id Strategy", "group": "common", "label": "common", "required": false, 
"type": "object", "javaType": 
"org.springframework.ws.soap.addressing.messageid.MessageIdStrategy", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Option to provide a 
custom Messa [...]
     "endpointDispatcher": { "index": 6, "kind": "parameter", "displayName": 
"Endpoint Dispatcher", "group": "consumer", "label": "consumer", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.component.spring.ws.bean.CamelEndpointDispatcher", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.spring.ws.SpringWebserviceConfiguration", 
"configurationField": "configuration", "description": "Spring 
org.springframework.w [...]
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/main/docs/spring-ws-component.adoc
 
b/components/camel-spring-parent/camel-spring-ws/src/main/docs/spring-ws-component.adoc
index e53b08f6353d..8a54d2036e31 100644
--- 
a/components/camel-spring-parent/camel-spring-ws/src/main/docs/spring-ws-component.adoc
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/main/docs/spring-ws-component.adoc
@@ -335,6 +335,17 @@ protected void doProcessSoapAttachements(Message inOrOut, 
SoapMessage response)
 }
 ----
 
+The `BasicMessageFilter` that the component creates for an endpoint
+applies the endpoint's `headerFilterStrategy` to the message headers it
+writes into the SOAP header, so by default headers whose names start
+with `Camel` or `camel` are not written. A message filter you provide
+yourself, global or local, does not know which endpoint it runs for: a
+`BasicMessageFilter`, or a subclass of it, created with the no-argument
+constructor applies a `SpringWebserviceHeaderFilterStrategy`, whatever
+`headerFilterStrategy` the endpoint is configured with. To apply another
+strategy, create it with the 
`BasicMessageFilter(Supplier<HeaderFilterStrategy>)`
+constructor, or override `getHeaderFilterStrategy()`.
+
 === Using a custom MessageSender and MessageFactory
 
 A custom message sender or factory in the registry can be referenced
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceComponent.java
 
b/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceComponent.java
index 60a8b7dc55a5..f1e35ccd2f0c 100644
--- 
a/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceComponent.java
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceComponent.java
@@ -168,8 +168,8 @@ public class SpringWebserviceComponent extends 
DefaultComponent implements SSLCo
             if (globalMessageFilter != null) {
                 configuration.setMessageFilter(globalMessageFilter);
             } else {
-                // use basic as fallback
-                configuration.setMessageFilter(new BasicMessageFilter());
+                // use basic as fallback, filtering the headers it writes with 
the endpoint's header filter strategy
+                configuration.setMessageFilter(new 
BasicMessageFilter(configuration::getHeaderFilterStrategy));
             }
         }
     }
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceConfiguration.java
 
b/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceConfiguration.java
index 123cf22b74a4..0891c04b7d8a 100644
--- 
a/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceConfiguration.java
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/SpringWebserviceConfiguration.java
@@ -431,7 +431,7 @@ public class SpringWebserviceConfiguration {
     /**
      * To use a custom HeaderFilterStrategy to filter headers mapped to and 
from the Camel message. By default the
      * internal {@code Camel} and {@code camel} header namespace 
(case-insensitive) is filtered out from inbound SOAP
-     * headers.
+     * headers, and the default message filter does not write it into outbound 
SOAP headers.
      */
     public void setHeaderFilterStrategy(HeaderFilterStrategy 
headerFilterStrategy) {
         this.headerFilterStrategy = headerFilterStrategy;
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilter.java
 
b/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilter.java
index 9539a26987d7..77a4d327c58a 100644
--- 
a/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilter.java
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/main/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilter.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.spring.ws.filter.impl;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Supplier;
 
 import jakarta.activation.DataHandler;
 
@@ -27,16 +28,50 @@ import javax.xml.namespace.QName;
 import org.apache.camel.Exchange;
 import org.apache.camel.attachment.AttachmentMessage;
 import org.apache.camel.component.spring.ws.SpringWebserviceConstants;
+import 
org.apache.camel.component.spring.ws.SpringWebserviceHeaderFilterStrategy;
 import org.apache.camel.component.spring.ws.filter.MessageFilter;
+import org.apache.camel.spi.HeaderFilterStrategy;
 import org.springframework.ws.WebServiceMessage;
 import org.springframework.ws.soap.SoapHeader;
 import org.springframework.ws.soap.SoapMessage;
 
 /**
  * This class populates a SOAP header and attachments in the WebServiceMessage 
instance.
+ * <p>
+ * A message header is written into the SOAP header only when the {@link 
HeaderFilterStrategy} does not filter it, so by
+ * default the internal {@code Camel} and {@code camel} header namespace is 
not written.
  */
 public class BasicMessageFilter implements MessageFilter {
 
+    private final Supplier<HeaderFilterStrategy> headerFilterStrategy;
+
+    /**
+     * Creates a filter that applies a {@link 
SpringWebserviceHeaderFilterStrategy} to the message headers it writes
+     * into the SOAP header.
+     */
+    public BasicMessageFilter() {
+        HeaderFilterStrategy strategy = new 
SpringWebserviceHeaderFilterStrategy();
+        this.headerFilterStrategy = () -> strategy;
+    }
+
+    /**
+     * Creates a filter that applies the {@link HeaderFilterStrategy} returned 
by the given supplier to the message
+     * headers it writes into the SOAP header. A {@code null} strategy 
disables the filtering.
+     *
+     * @param headerFilterStrategy supplies the strategy to apply, such as the 
one configured on the endpoint
+     */
+    public BasicMessageFilter(Supplier<HeaderFilterStrategy> 
headerFilterStrategy) {
+        this.headerFilterStrategy = headerFilterStrategy;
+    }
+
+    /**
+     * The {@link HeaderFilterStrategy} applied to the message headers written 
into the SOAP header, or {@code null}
+     * when they are not filtered.
+     */
+    protected HeaderFilterStrategy getHeaderFilterStrategy() {
+        return headerFilterStrategy != null ? headerFilterStrategy.get() : 
null;
+    }
+
     /**
      * Whether a header is valid
      */
@@ -85,10 +120,13 @@ public class BasicMessageFilter implements MessageFilter {
      * The SOAP header is populated from exchange.getOut().getHeaders() if 
this class is used by the consumer or
      * exchange.getIn().getHeaders() if this class is used by the producer. If 
.getHeaders() contains under a certain
      * key a value with the QName object, it is directly added as a new header 
element. If it contains only a String
-     * value, it is transformed into a header attribute. Following headers are 
excluded:
+     * value, it is transformed into a header attribute. The spring-ws 
headers, the breadcrumb id and Content-Type are
+     * excluded, and so is any header that the {@link HeaderFilterStrategy} 
filters.
      */
     protected void doProcessSoapHeader(AttachmentMessage inOrOut, SoapMessage 
soapMessage) {
         SoapHeader soapHeader = soapMessage.getSoapHeader();
+        HeaderFilterStrategy strategy = getHeaderFilterStrategy();
+        Exchange exchange = inOrOut.getExchange();
 
         Map<String, Object> headers = inOrOut.getHeaders();
 
@@ -116,6 +154,9 @@ public class BasicMessageFilter implements MessageFilter {
             }
 
             Object value = headers.get(name);
+            if (strategy != null && strategy.applyFilterToCamelHeaders(name, 
value, exchange)) {
+                continue;
+            }
             if (value instanceof QName qname) {
                 soapHeader.addHeaderElement(qname);
             } else {
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/test/java/org/apache/camel/component/spring/ws/OutboundSoapHeaderFilterTest.java
 
b/components/camel-spring-parent/camel-spring-ws/src/test/java/org/apache/camel/component/spring/ws/OutboundSoapHeaderFilterTest.java
new file mode 100644
index 000000000000..083880d5bada
--- /dev/null
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/test/java/org/apache/camel/component/spring/ws/OutboundSoapHeaderFilterTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.component.spring.ws;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.spring.junit6.CamelSpringTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.ws.client.core.WebServiceTemplate;
+import org.springframework.ws.soap.SoapHeader;
+import org.springframework.ws.soap.SoapMessage;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class OutboundSoapHeaderFilterTest extends CamelSpringTestSupport {
+
+    private final String xmlRequest = "<GetQuote 
xmlns=\"http://www.stockquotes.edu/\";><symbol>GOOG</symbol></GetQuote>";
+
+    @EndpointInject("mock:received")
+    private MockEndpoint received;
+
+    @Test
+    void internalCamelHeadersAreNotWrittenIntoTheConsumerResponse() {
+        List<String> attributes = 
responseSoapHeaderAttributes("http://www.stockquotes.edu/Respond";);
+
+        assertTrue(attributes.contains("TransactionId"),
+                "Application-level message header should be written into the 
SOAP response header");
+        assertFalse(attributes.stream().anyMatch(name -> 
name.toLowerCase(Locale.ROOT).startsWith("camel")),
+                "Internal Camel* headers must not be written into the SOAP 
response header: " + attributes);
+    }
+
+    @Test
+    void internalCamelHeadersAreNotWrittenIntoTheProducerRequest() throws 
Exception {
+        received.expectedMessageCount(1);
+
+        template.send("direct:send", exchange -> {
+            exchange.getIn().setBody(xmlRequest);
+            exchange.getIn().setHeader("CamelHttpUri", 
"http://localhost/backend";);
+            exchange.getIn().setHeader("cAmElFileName", "request.xml");
+            exchange.getIn().setHeader("TransactionId", "42");
+        });
+
+        received.assertIsSatisfied();
+        Exchange exchange = received.getExchanges().get(0);
+        String soapHeader = 
exchange.getIn().getHeader(SpringWebserviceConstants.SPRING_WS_SOAP_HEADER, 
String.class);
+        assertTrue(soapHeader.contains("TransactionId"),
+                "Application-level message header should be written into the 
SOAP request header");
+        
assertFalse(soapHeader.toLowerCase(Locale.ROOT).contains("camelhttpuri"),
+                "Internal Camel* headers must not be written into the SOAP 
request header: " + soapHeader);
+        
assertFalse(soapHeader.toLowerCase(Locale.ROOT).contains("camelfilename"),
+                "Internal Camel* headers must not be written into the SOAP 
request header: " + soapHeader);
+    }
+
+    @Test
+    void endpointHeaderFilterStrategyIsApplied() {
+        List<String> attributes = 
responseSoapHeaderAttributes("http://www.stockquotes.edu/RespondPassThrough";);
+
+        // the endpoint's own strategy decides, so a strategy that filters 
nothing writes every header
+        assertTrue(attributes.contains("CamelHttpUri"),
+                "The headerFilterStrategy configured on the endpoint should be 
applied: " + attributes);
+    }
+
+    private List<String> responseSoapHeaderAttributes(String soapAction) {
+        WebServiceTemplate client = 
applicationContext.getBean("webServiceTemplate", WebServiceTemplate.class);
+        return client.sendAndReceive(
+                request -> {
+                    try {
+                        TransformerFactory.newInstance().newTransformer()
+                                .transform(new StreamSource(new 
StringReader(xmlRequest)), request.getPayloadResult());
+                    } catch (TransformerException e) {
+                        throw new IllegalStateException(e);
+                    }
+                    ((SoapMessage) request).setSoapAction(soapAction);
+                },
+                response -> {
+                    List<String> names = new ArrayList<>();
+                    SoapHeader soapHeader = ((SoapMessage) 
response).getSoapHeader();
+                    Iterator<QName> attributes = soapHeader.getAllAttributes();
+                    while (attributes.hasNext()) {
+                        names.add(attributes.next().getLocalPart());
+                    }
+                    return names;
+                });
+    }
+
+    @Override
+    protected AbstractXmlApplicationContext createApplicationContext() {
+        return new ClassPathXmlApplicationContext(
+                
"org/apache/camel/component/spring/ws/OutboundSoapHeaderFilterTest-context.xml");
+    }
+}
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/test/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilterTest.java
 
b/components/camel-spring-parent/camel-spring-ws/src/test/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilterTest.java
index 323ecc935044..ca0809fadc7b 100644
--- 
a/components/camel-spring-parent/camel-spring-ws/src/test/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilterTest.java
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/test/java/org/apache/camel/component/spring/ws/filter/impl/BasicMessageFilterTest.java
@@ -16,12 +16,16 @@
  */
 package org.apache.camel.component.spring.ws.filter.impl;
 
+import java.util.List;
+import java.util.Locale;
+
 import jakarta.activation.DataHandler;
 
 import javax.xml.namespace.QName;
 
 import org.apache.camel.attachment.AttachmentMessage;
 import org.apache.camel.component.spring.ws.SpringWebserviceConstants;
+import org.apache.camel.support.DefaultHeaderFilterStrategy;
 import org.apache.camel.test.junit6.ExchangeTestSupport;
 import org.assertj.core.util.Streams;
 import org.junit.jupiter.api.BeforeEach;
@@ -184,4 +188,58 @@ class BasicMessageFilterTest extends ExchangeTestSupport {
         
assertThat(Streams.stream(message.getAttachments()).toList()).isNotEmpty();
         assertThat(message.getAttachment("testAttachment")).isNotNull();
     }
+
+    @Test
+    void producerDoesNotWriteInternalCamelHeaders() {
+        exchange.getIn().setHeader("CamelHttpUri", "http://localhost/backend";);
+        exchange.getIn().setHeader("cAmElFileName", "request.xml");
+        exchange.getIn().setHeader("TransactionId", "42");
+
+        filter.filterProducer(exchange, message);
+
+        assertThat(attributeNames()).contains("foo", "TransactionId")
+                .noneMatch(name -> 
name.toLowerCase(Locale.ROOT).startsWith("camel"));
+    }
+
+    @Test
+    void consumerDoesNotWriteInternalCamelHeaders() {
+        exchange.getMessage().setHeader("CamelHttpUri", 
"http://localhost/backend";);
+        exchange.getMessage().setHeader("cAmElFileName", "response.xml");
+        exchange.getMessage().setHeader("TransactionId", "42");
+        exchange.getMessage().setHeader("CamelElement", new 
QName("http://example.com/test";, "camelElement"));
+        exchange.getMessage().setHeader("AppElement", new 
QName("http://example.com/test";, "appElement"));
+
+        filter.filterConsumer(exchange, message);
+
+        assertThat(attributeNames()).contains("foo", "TransactionId")
+                .noneMatch(name -> 
name.toLowerCase(Locale.ROOT).startsWith("camel"));
+        
assertThat(Streams.stream(message.getSoapHeader().examineAllHeaderElements())
+                .map(element -> 
element.getName().getLocalPart()).toList()).containsExactly("appElement");
+    }
+
+    @Test
+    void suppliedHeaderFilterStrategyIsApplied() {
+        DefaultHeaderFilterStrategy passThrough = new 
DefaultHeaderFilterStrategy();
+        passThrough.setOutFilterStartsWith((String[]) null);
+        filter = new BasicMessageFilter(() -> passThrough);
+        exchange.getIn().setHeader("CamelHttpUri", "http://localhost/backend";);
+
+        filter.filterProducer(exchange, message);
+
+        assertThat(attributeNames()).contains("foo", "CamelHttpUri");
+    }
+
+    @Test
+    void noHeaderFilterStrategyWritesEveryHeader() {
+        filter = new BasicMessageFilter(() -> null);
+        exchange.getIn().setHeader("CamelHttpUri", "http://localhost/backend";);
+
+        filter.filterProducer(exchange, message);
+
+        assertThat(attributeNames()).contains("foo", "CamelHttpUri");
+    }
+
+    private List<String> attributeNames() {
+        return 
Streams.stream(message.getSoapHeader().getAllAttributes()).map(QName::getLocalPart).toList();
+    }
 }
diff --git 
a/components/camel-spring-parent/camel-spring-ws/src/test/resources/org/apache/camel/component/spring/ws/OutboundSoapHeaderFilterTest-context.xml
 
b/components/camel-spring-parent/camel-spring-ws/src/test/resources/org/apache/camel/component/spring/ws/OutboundSoapHeaderFilterTest-context.xml
new file mode 100644
index 000000000000..80ea8a8db100
--- /dev/null
+++ 
b/components/camel-spring-parent/camel-spring-ws/src/test/resources/org/apache/camel/component/spring/ws/OutboundSoapHeaderFilterTest-context.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+         http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
+         http://camel.apache.org/schema/spring 
http://camel.apache.org/schema/spring/camel-spring.xsd";>
+
+    <camelContext xmlns="http://camel.apache.org/schema/spring";>
+        <!-- producer route (web service client) writing the message headers 
into the SOAP request header -->
+        <route>
+            <from uri="direct:send"/>
+            <to 
uri="spring-ws:http://localhost?webServiceTemplate=#webServiceTemplate&amp;soapAction=http://www.stockquotes.edu/Capture"/>
+        </route>
+
+        <!-- consumer route (the web service invoked by the producer) 
capturing the SOAP request it received -->
+        <route>
+            <from 
uri="spring-ws:soapaction:http://www.stockquotes.edu/Capture?endpointMapping=#endpointMapping"/>
+            <to uri="mock:received"/>
+            <setBody>
+                <constant>&lt;GetQuoteResponse 
xmlns="http://www.stockquotes.edu/"&gt;&lt;GetQuoteResult&gt;100&lt;/GetQuoteResult&gt;&lt;/GetQuoteResponse&gt;</constant>
+            </setBody>
+        </route>
+
+        <!-- consumer routes whose response message carries internal Camel 
headers and an application header -->
+        <route>
+            <from 
uri="spring-ws:soapaction:http://www.stockquotes.edu/Respond?endpointMapping=#endpointMapping"/>
+            <setHeader 
name="CamelHttpUri"><constant>http://localhost/backend</constant></setHeader>
+            <setHeader 
name="cAmElFileName"><constant>response.xml</constant></setHeader>
+            <setHeader name="TransactionId"><constant>42</constant></setHeader>
+            <setBody>
+                <constant>&lt;GetQuoteResponse 
xmlns="http://www.stockquotes.edu/"&gt;&lt;GetQuoteResult&gt;100&lt;/GetQuoteResult&gt;&lt;/GetQuoteResponse&gt;</constant>
+            </setBody>
+        </route>
+        <route>
+            <from 
uri="spring-ws:soapaction:http://www.stockquotes.edu/RespondPassThrough?endpointMapping=#endpointMapping&amp;headerFilterStrategy=#passThroughHeaderFilterStrategy"/>
+            <setHeader 
name="CamelHttpUri"><constant>http://localhost/backend</constant></setHeader>
+            <setHeader name="TransactionId"><constant>42</constant></setHeader>
+            <setBody>
+                <constant>&lt;GetQuoteResponse 
xmlns="http://www.stockquotes.edu/"&gt;&lt;GetQuoteResult&gt;100&lt;/GetQuoteResult&gt;&lt;/GetQuoteResponse&gt;</constant>
+            </setBody>
+        </route>
+    </camelContext>
+
+    <bean id="messageFactory" 
class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
+
+    <bean id="endpointMapping"
+          
class="org.apache.camel.component.spring.ws.bean.CamelEndpointMapping"/>
+
+    <!-- a header filter strategy that does not filter any header -->
+    <bean id="passThroughHeaderFilterStrategy" 
class="org.apache.camel.support.DefaultHeaderFilterStrategy">
+        <property name="inFilterStartsWith">
+            <null/>
+        </property>
+        <property name="outFilterStartsWith">
+            <null/>
+        </property>
+    </bean>
+
+    <bean id="webServiceTemplate" 
class="org.springframework.ws.client.core.WebServiceTemplate">
+        <property name="defaultUri" value="http://localhost"/>
+        <property name="messageSender">
+            <bean 
class="net.javacrumbs.springws.test.helper.InMemoryWebServiceMessageSender2"/>
+        </property>
+    </bean>
+
+</beans>
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
index 0411732c7d45..1d2427cb6eb5 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
@@ -66,6 +66,18 @@ Other SOAP response header attributes and elements are 
mapped as before, and the
 available in the `CamelSpringWebserviceSoapHeader` header. A route that relies 
on the previous behaviour can supply a
 custom `headerFilterStrategy` on the `spring-ws` endpoint.
 
+=== camel-spring-ws - internal Camel headers are no longer written into SOAP 
headers
+
+The default `messageFilter` of the `spring-ws` component, 
`BasicMessageFilter`, now applies the endpoint's
+`headerFilterStrategy` before it writes a message header into the SOAP header, 
both on the request sent by the
+producer and on the response returned by the consumer. With the default 
`SpringWebserviceHeaderFilterStrategy`,
+message headers whose names start with `Camel` or `camel` (case-insensitively) 
are no longer written into the SOAP
+header. Other message headers are written as before.
+
+A `BasicMessageFilter` created in application code applies a 
`SpringWebserviceHeaderFilterStrategy` unless it is
+created with another strategy. A route that relies on the previous behaviour 
can supply a custom
+`headerFilterStrategy` on the `spring-ws` endpoint.
+
 === camel-http-common, camel-platform-http-vertx - fileNameExtWhitelist 
entries are matched exactly
 
 `fileNameExtWhitelist` is now checked the same way everywhere, by one shared 
check in `camel-http-base`. Two
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 833c5ead441c..12fa8331d909 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -81,6 +81,18 @@ Other SOAP response header attributes and elements are 
mapped as before, and the
 available in the `CamelSpringWebserviceSoapHeader` header. A route that relies 
on the previous behaviour can supply a
 custom `headerFilterStrategy` on the `spring-ws` endpoint.
 
+=== camel-spring-ws - internal Camel headers are no longer written into SOAP 
headers
+
+The default `messageFilter` of the `spring-ws` component, 
`BasicMessageFilter`, now applies the endpoint's
+`headerFilterStrategy` before it writes a message header into the SOAP header, 
both on the request sent by the
+producer and on the response returned by the consumer. With the default 
`SpringWebserviceHeaderFilterStrategy`,
+message headers whose names start with `Camel` or `camel` (case-insensitively) 
are no longer written into the SOAP
+header. Other message headers are written as before.
+
+A `BasicMessageFilter` created in application code applies a 
`SpringWebserviceHeaderFilterStrategy` unless it is
+created with another strategy. A route that relies on the previous behaviour 
can supply a custom
+`headerFilterStrategy` on the `spring-ws` endpoint.
+
 === camel-http-common, camel-platform-http-vertx - fileNameExtWhitelist 
entries are matched exactly
 
 `fileNameExtWhitelist` is now checked the same way everywhere, by one shared 
check in `camel-http-base`. Two
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 0eaa9c32cd09..9c91357c9d45 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -3983,6 +3983,18 @@ Other SOAP response header attributes and elements are 
mapped as before, and the
 available in the `CamelSpringWebserviceSoapHeader` header. A route that relies 
on the previous behaviour can supply a
 custom `headerFilterStrategy` on the `spring-ws` endpoint.
 
+=== camel-spring-ws - internal Camel headers are no longer written into SOAP 
headers
+
+The default `messageFilter` of the `spring-ws` component, 
`BasicMessageFilter`, now applies the endpoint's
+`headerFilterStrategy` before it writes a message header into the SOAP header, 
both on the request sent by the
+producer and on the response returned by the consumer. With the default 
`SpringWebserviceHeaderFilterStrategy`,
+message headers whose names start with `Camel` or `camel` (case-insensitively) 
are no longer written into the SOAP
+header. Other message headers are written as before.
+
+A `BasicMessageFilter` created in application code applies a 
`SpringWebserviceHeaderFilterStrategy` unless it is
+created with another strategy. A route that relies on the previous behaviour 
can supply a custom
+`headerFilterStrategy` on the `spring-ws` endpoint.
+
 === camel-mustache, camel-chunk - potential breaking change
 
 The Exchange header constants in `MustacheConstants` and `ChunkConstants` have 
been renamed to
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringWebserviceEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringWebserviceEndpointBuilderFactory.java
index 929526364422..98d635cfb38f 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringWebserviceEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringWebserviceEndpointBuilderFactory.java
@@ -47,7 +47,8 @@ public interface SpringWebserviceEndpointBuilderFactory {
          * To use a custom HeaderFilterStrategy to filter headers mapped to and
          * from the Camel message. By default the internal Camel and camel
          * header namespace (case-insensitive) is filtered out from inbound 
SOAP
-         * headers.
+         * headers, and the default message filter does not write it into
+         * outbound SOAP headers.
          * 
          * The option is a:
          * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
@@ -65,7 +66,8 @@ public interface SpringWebserviceEndpointBuilderFactory {
          * To use a custom HeaderFilterStrategy to filter headers mapped to and
          * from the Camel message. By default the internal Camel and camel
          * header namespace (case-insensitive) is filtered out from inbound 
SOAP
-         * headers.
+         * headers, and the default message filter does not write it into
+         * outbound SOAP headers.
          * 
          * The option will be converted to a
          * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
@@ -410,7 +412,8 @@ public interface SpringWebserviceEndpointBuilderFactory {
          * To use a custom HeaderFilterStrategy to filter headers mapped to and
          * from the Camel message. By default the internal Camel and camel
          * header namespace (case-insensitive) is filtered out from inbound 
SOAP
-         * headers.
+         * headers, and the default message filter does not write it into
+         * outbound SOAP headers.
          * 
          * The option is a:
          * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
@@ -428,7 +431,8 @@ public interface SpringWebserviceEndpointBuilderFactory {
          * To use a custom HeaderFilterStrategy to filter headers mapped to and
          * from the Camel message. By default the internal Camel and camel
          * header namespace (case-insensitive) is filtered out from inbound 
SOAP
-         * headers.
+         * headers, and the default message filter does not write it into
+         * outbound SOAP headers.
          * 
          * The option will be converted to a
          * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
@@ -1026,7 +1030,8 @@ public interface SpringWebserviceEndpointBuilderFactory {
          * To use a custom HeaderFilterStrategy to filter headers mapped to and
          * from the Camel message. By default the internal Camel and camel
          * header namespace (case-insensitive) is filtered out from inbound 
SOAP
-         * headers.
+         * headers, and the default message filter does not write it into
+         * outbound SOAP headers.
          * 
          * The option is a:
          * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
@@ -1044,7 +1049,8 @@ public interface SpringWebserviceEndpointBuilderFactory {
          * To use a custom HeaderFilterStrategy to filter headers mapped to and
          * from the Camel message. By default the internal Camel and camel
          * header namespace (case-insensitive) is filtered out from inbound 
SOAP
-         * headers.
+         * headers, and the default message filter does not write it into
+         * outbound SOAP headers.
          * 
          * The option will be converted to a
          * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.

Reply via email to