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&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><GetQuoteResponse
xmlns="http://www.stockquotes.edu/"><GetQuoteResult>100</GetQuoteResult></GetQuoteResponse></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><GetQuoteResponse
xmlns="http://www.stockquotes.edu/"><GetQuoteResult>100</GetQuoteResult></GetQuoteResponse></constant>
+ </setBody>
+ </route>
+ <route>
+ <from
uri="spring-ws:soapaction:http://www.stockquotes.edu/RespondPassThrough?endpointMapping=#endpointMapping&headerFilterStrategy=#passThroughHeaderFilterStrategy"/>
+ <setHeader
name="CamelHttpUri"><constant>http://localhost/backend</constant></setHeader>
+ <setHeader name="TransactionId"><constant>42</constant></setHeader>
+ <setBody>
+ <constant><GetQuoteResponse
xmlns="http://www.stockquotes.edu/"><GetQuoteResult>100</GetQuoteResult></GetQuoteResponse></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.