You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2023/04/20 06:34:04 UTC

[camel] 02/04: [CAMEL-19129] Reorganize sections in Camel CXF

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

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

commit 9ab68aca3e9053b13eba8c672d7ee9dd2e01e036
Author: Lukas Lowinger <ll...@redhat.com>
AuthorDate: Wed Apr 12 09:40:19 2023 +0200

    [CAMEL-19129] Reorganize sections in Camel CXF
---
 .../src/main/docs/cxf-component.adoc               | 1458 ++++++++++----------
 1 file changed, 728 insertions(+), 730 deletions(-)

diff --git a/components/camel-cxf/camel-cxf-soap/src/main/docs/cxf-component.adoc b/components/camel-cxf/camel-cxf-soap/src/main/docs/cxf-component.adoc
index 8aef0a6702e..0ad282436f6 100644
--- a/components/camel-cxf/camel-cxf-soap/src/main/docs/cxf-component.adoc
+++ b/components/camel-cxf/camel-cxf-soap/src/main/docs/cxf-component.adoc
@@ -120,921 +120,919 @@ exchange property, `CamelCXFDataFormat`. The exchange key constant is
 defined in
 `org.apache.camel.component.cxf.common.message.CxfConstants.DATA_FORMAT_PROPERTY`.
 
-[#cxf-loggingout-interceptor-in-message-mode]
-=== How to enable CXF's LoggingOutInterceptor in RAW mode
+== How to consume a message from a camel-cxf endpoint in POJO data format
 
-CXF's `LoggingOutInterceptor` outputs outbound message that goes on the
-wire to logging system (Java Util Logging). Since the
-`LoggingOutInterceptor` is in `PRE_STREAM` phase (but `PRE_STREAM` phase
-is removed in `RAW` mode), you have to configure
-`LoggingOutInterceptor` to be run during the `WRITE` phase. The
-following is an example.
+The `camel-cxf` endpoint consumer POJO data format is based on the
+http://cxf.apache.org/docs/invokers.html[CXF invoker], so the
+message header has a property with the name of
+`CxfConstants.OPERATION_NAME` and the message body is a list of the SEI
+method parameters.
 
-[source,xml]
--------------------------------------------------------------------------------------------------------
-<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor">
-    <!--  it really should have been user-prestream but CXF does have such phase! -->
-    <constructor-arg value="target/write"/>
-</bean>
+Consider the https://github.com/apache/camel/blob/main/components/camel-cxf/camel-cxf-soap/src/test/java/org/apache/camel/wsdl_first/PersonProcessor.java[PersonProcessor] example code:
 
-<cxf:cxfEndpoint id="serviceEndpoint" address="http://localhost:${CXFTestSupport.port2}/LoggingInterceptorInMessageModeTest/helloworld"
-    serviceClass="org.apache.camel.component.cxf.HelloService">
-    <cxf:outInterceptors>
-        <ref bean="loggingOutInterceptor"/>
-    </cxf:outInterceptors>
-    <cxf:properties>
-        <entry key="dataFormat" value="RAW"/>
-    </cxf:properties>
-</cxf:cxfEndpoint>
--------------------------------------------------------------------------------------------------------
+[source,java]
+----
+public class PersonProcessor implements Processor {
 
-=== Description of relayHeaders option
+    private static final Logger LOG = LoggerFactory.getLogger(PersonProcessor.class);
 
-There are _in-band_ and _out-of-band_ on-the-wire headers from the
-perspective of a JAXWS WSDL-first developer.
+    @Override
+    @SuppressWarnings("unchecked")
+    public void process(Exchange exchange) throws Exception {
+        LOG.info("processing exchange in camel");
 
-The _in-band_ headers are headers that are explicitly defined as part of
-the WSDL binding contract for an endpoint such as SOAP headers.
+        BindingOperationInfo boi = (BindingOperationInfo) exchange.getProperty(BindingOperationInfo.class.getName());
+        if (boi != null) {
+            LOG.info("boi.isUnwrapped" + boi.isUnwrapped());
+        }
+        // Get the parameters list which element is the holder.
+        MessageContentsList msgList = (MessageContentsList) exchange.getIn().getBody();
+        Holder<String> personId = (Holder<String>) msgList.get(0);
+        Holder<String> ssn = (Holder<String>) msgList.get(1);
+        Holder<String> name = (Holder<String>) msgList.get(2);
 
-The _out-of-band_ headers are headers that are serialized over the wire,
-but are not explicitly part of the WSDL binding contract.
+        if (personId.value == null || personId.value.length() == 0) {
+            LOG.info("person id 123, so throwing exception");
+            // Try to throw out the soap fault message
+            org.apache.camel.wsdl_first.types.UnknownPersonFault personFault
+                    = new org.apache.camel.wsdl_first.types.UnknownPersonFault();
+            personFault.setPersonId("");
+            org.apache.camel.wsdl_first.UnknownPersonFault fault
+                    = new org.apache.camel.wsdl_first.UnknownPersonFault("Get the null value of person name", personFault);
+            exchange.getMessage().setBody(fault);
+            return;
+        }
 
-Headers relaying/filtering is bi-directional.
+        name.value = "Bonjour";
+        ssn.value = "123";
+        LOG.info("setting Bonjour as the response");
+        // Set the response message, first element is the return value of the operation,
+        // the others are the holders of method parameters
+        exchange.getMessage().setBody(new Object[] { null, personId, ssn, name });
+    }
 
-When a route has a CXF endpoint and the developer needs to have
-on-the-wire headers, such as SOAP headers, be relayed along the route to
-be consumed say by another JAXWS endpoint, then `relayHeaders` should be
-set to `true`, which is the default value.
+}
+----
 
+== How to prepare the message for the camel-cxf endpoint in POJO data format
 
-=== Available only in POJO mode
+The `camel-cxf` endpoint producer is based on the
+https://github.com/apache/cxf/blob/master/core/src/main/java/org/apache/cxf/endpoint/Client.java[CXF
+client API]. First you need to specify the operation name in the message
+header, then add the method parameters to a list, and initialize the
+message with this parameter list. The response message's body is a
+messageContentsList, you can get the result from that list.
 
-The `relayHeaders=true` expresses an intent to relay the headers. The
-actual decision on whether a given header is relayed is delegated to a
-pluggable instance that implements the `MessageHeadersRelay` interface.
-A concrete implementation of `MessageHeadersRelay` will be consulted to
-decide if a header needs to be relayed or not. There is already an
-implementation of `SoapMessageHeadersRelay` which binds itself to
-well-known SOAP name spaces. Currently only out-of-band headers are
-filtered, and in-band headers will always be relayed when
-`relayHeaders=true`. If there is a header on the wire whose name space
-is unknown to the runtime, then a fall back `DefaultMessageHeadersRelay`
-will be used, which simply allows all headers to be relayed.
+If you don't specify the operation name in the message header,
+`CxfProducer` will try to use the `defaultOperationName` from
+`CxfEndpoint`, if there is no `defaultOperationName` set on
+`CxfEndpoint`, it will pick up the first operationName from the Operation
+list.
 
-The `relayHeaders=false` setting specifies that all headers in-band and
-out-of-band should be dropped.
+If you want to get the object array from the message body, you can get
+the body using `message.getBody(Object[].class)`, as shown in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfProducerRouterTest.java#L116[CxfProducerRouterTest.testInvokingSimpleServerWithParams]:
 
-You can plugin your own `MessageHeadersRelay` implementations overriding
-or adding additional ones to the list of relays. In order to override a
-preloaded relay instance just make sure that your `MessageHeadersRelay`
-implementation services the same name spaces as the one you looking to
-override. Also note, that the overriding relay has to service all of the
-name spaces as the one you looking to override, or else a runtime
-exception on route start up will be thrown as this would introduce an
-ambiguity in name spaces to relay instance mappings.
+[source,java]
+----
+Exchange senderExchange = new DefaultExchange(context, ExchangePattern.InOut);
+final List<String> params = new ArrayList<>();
+// Prepare the request message for the camel-cxf procedure
+params.add(TEST_MESSAGE);
+senderExchange.getIn().setBody(params);
+senderExchange.getIn().setHeader(CxfConstants.OPERATION_NAME, ECHO_OPERATION);
 
-[source,xml]
--------------------------------------------------------------------------------------------------------
-<cxf:cxfEndpoint ...>
-   <cxf:properties>
-     <entry key="org.apache.camel.cxf.message.headers.relays">
-       <list>
-         <ref bean="customHeadersRelay"/>
-       </list>
-     </entry>
-   </cxf:properties>
- </cxf:cxfEndpoint>
- <bean id="customHeadersRelay" class="org.apache.camel.component.cxf.soap.headers.CustomHeadersRelay"/>
--------------------------------------------------------------------------------------------------------
+Exchange exchange = template.send("direct:EndpointA", senderExchange);
 
-Take a look at the tests that show how you'd be able to relay/drop
-headers here:
+org.apache.camel.Message out = exchange.getMessage();
+// The response message's body is an MessageContentsList which first element is the return value of the operation,
+// If there are some holder parameters, the holder parameter will be filled in the reset of List.
+// The result will be extract from the MessageContentsList with the String class type
+MessageContentsList result = (MessageContentsList) out.getBody();
+LOG.info("Received output text: " + result.get(0));
+Map<String, Object> responseContext = CastUtils.cast((Map<?, ?>) out.getHeader(Client.RESPONSE_CONTEXT));
+assertNotNull(responseContext);
+assertEquals("UTF-8", responseContext.get(org.apache.cxf.message.Message.ENCODING),
+        "We should get the response context here");
+assertEquals("echo " + TEST_MESSAGE, result.get(0), "Reply body on Camel is wrong");
+----
 
-https://github.com/apache/camel/blob/main/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java[https://github.com/apache/camel/blob/main/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java]
+== How to consume a message from a camel-cxf endpoint in PAYLOAD data format
 
-* `POJO` and `PAYLOAD` modes are supported. In `POJO` mode, only
-out-of-band message headers are available for filtering as the in-band
-headers have been processed and removed from header list by CXF. The
-in-band headers are incorporated into the `MessageContentList` in POJO
-mode. The `camel-cxf` component does make any attempt to remove the
-in-band headers from the `MessageContentList`. If filtering of in-band
-headers is required, please use `PAYLOAD` mode or plug in a (pretty
-straightforward) CXF interceptor/JAXWS Handler to the CXF endpoint.
-* The Message Header Relay mechanism has been merged into
-`CxfHeaderFilterStrategy`. The `relayHeaders` option, its semantics, and
-default value remain the same, but it is a property of
-`CxfHeaderFilterStrategy`.
- Here is an example of configuring it.
+`PAYLOAD` means that you process the payload from the SOAP
+envelope as a native CxfPayload. `Message.getBody()` will return a
+`org.apache.camel.component.cxf.CxfPayload` object, with getters
+for SOAP message headers and the SOAP body.
 
-[source,xml]
--------------------------------------------------------------------------------------------------------
-<bean id="dropAllMessageHeadersStrategy" class="org.apache.camel.component.cxf.transport.header.CxfHeaderFilterStrategy">
+See https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadTest.java#L66[CxfConsumerPayloadTest]:
 
-    <!--  Set relayHeaders to false to drop all SOAP headers -->
-    <property name="relayHeaders" value="false"/>
+[source,java]
+----
+protected RouteBuilder createRouteBuilder() {
+    return new RouteBuilder() {
+        public void configure() {
+            from(simpleEndpointURI + "&dataFormat=PAYLOAD").to("log:info").process(new Processor() {
+                @SuppressWarnings("unchecked")
+                public void process(final Exchange exchange) throws Exception {
+                    CxfPayload<SoapHeader> requestPayload = exchange.getIn().getBody(CxfPayload.class);
+                    List<Source> inElements = requestPayload.getBodySources();
+                    List<Source> outElements = new ArrayList<>();
+                    // You can use a customer toStringConverter to turn a CxfPayLoad message into String as you want
+                    String request = exchange.getIn().getBody(String.class);
+                    XmlConverter converter = new XmlConverter();
+                    String documentString = ECHO_RESPONSE;
 
-</bean>
--------------------------------------------------------------------------------------------------------
+                    Element in = new XmlConverter().toDOMElement(inElements.get(0));
+                    // Just check the element namespace
+                    if (!in.getNamespaceURI().equals(ELEMENT_NAMESPACE)) {
+                        throw new IllegalArgumentException("Wrong element namespace");
+                    }
+                    if (in.getLocalName().equals("echoBoolean")) {
+                        documentString = ECHO_BOOLEAN_RESPONSE;
+                        checkRequest("ECHO_BOOLEAN_REQUEST", request);
+                    } else {
+                        documentString = ECHO_RESPONSE;
+                        checkRequest("ECHO_REQUEST", request);
+                    }
+                    Document outDocument = converter.toDOMDocument(documentString, exchange);
+                    outElements.add(new DOMSource(outDocument.getDocumentElement()));
+                    // set the payload header with null
+                    CxfPayload<SoapHeader> responsePayload = new CxfPayload<>(null, outElements, null);
+                    exchange.getMessage().setBody(responsePayload);
+                }
+            });
+        }
+    };
+}
+----
 
-Then, your endpoint can reference the `CxfHeaderFilterStrategy`.
+== How to get and set SOAP headers in POJO mode
 
-[source,xml]
--------------------------------------------------------------------------------------------------------
-<route>
-    <from uri="cxf:bean:routerNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
-    <to uri="cxf:bean:serviceNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
-</route>
--------------------------------------------------------------------------------------------------------
+`POJO` means that the data format is a "list of Java objects" when the
+camel-cxf endpoint produces or consumes Camel exchanges. Even though
+Camel exposes the message body as POJOs in this mode, camel-cxf still
+provides access to read and write SOAP headers. However, since CXF
+interceptors remove in-band SOAP headers from the header list after they
+have been processed, only out-of-band SOAP headers are available to
+camel-cxf in POJO mode.
 
-* The `MessageHeadersRelay` interface has changed slightly and has been
-renamed to `MessageHeaderFilter`. It is a property of
-`CxfHeaderFilterStrategy`. Here is an example of configuring user
-defined Message Header Filters:
+The following example illustrates how to get/set SOAP headers. Suppose we
+have a route that forwards from one Camel-cxf endpoint to another. That
+is, SOAP Client -> Camel -> CXF service. We can attach two processors to
+obtain/insert SOAP headers at (1) before a request goes out to the CXF
+service and (2) before the response comes back to the SOAP Client. Processor
+(1) and (2) in this example are InsertRequestOutHeaderProcessor and
+InsertResponseOutHeaderProcessor. Our route looks like this:
 
 [source,xml]
--------------------------------------------------------------------------------------------------------
-<bean id="customMessageFilterStrategy" class="org.apache.camel.component.cxf.transport.header.CxfHeaderFilterStrategy">
-    <property name="messageHeaderFilters">
-        <list>
-            <!--  SoapMessageHeaderFilter is the built in filter.  It can be removed by omitting it. -->
-            <bean class="org.apache.camel.component.cxf.common.header.SoapMessageHeaderFilter"/>
-
-            <!--  Add custom filter here -->
-            <bean class="org.apache.camel.component.cxf.soap.headers.CustomHeaderFilter"/>
-        </list>
-    </property>
-</bean>
--------------------------------------------------------------------------------------------------------
-
-* In addition to `relayHeaders`, the following properties can be
-configured in `CxfHeaderFilterStrategy`.
+----
+<route>
+    <from uri="cxf:bean:routerRelayEndpointWithInsertion"/>
+    <process ref="InsertRequestOutHeaderProcessor" />
+    <to uri="cxf:bean:serviceRelayEndpointWithInsertion"/>
+    <process ref="InsertResponseOutHeaderProcessor" />
+</route>
+----
 
-[width="100%",cols="10%,10%,80%",options="header",]
-|=======================================================================
-|Name |Required |Description
-|`relayHeaders` |No |All message headers will be processed by Message Header Filters
- _Type_: `boolean`
- _Default_: `true`
+SOAP headers are propagated to and from Camel Message headers. The Camel
+message header name is "org.apache.cxf.headers.Header.list" which is a
+constant defined in CXF (org.apache.cxf.headers.Header.HEADER_LIST). The
+header value is a List of CXF SoapHeader objects
+(org.apache.cxf.binding.soap.SoapHeader). The following snippet is the
+InsertResponseOutHeaderProcessor (that insert a new SOAP header in the
+response message). The way to access SOAP headers in both
+InsertResponseOutHeaderProcessor and InsertRequestOutHeaderProcessor are
+actually the same. The only difference between the two processors is
+setting the direction of the inserted SOAP header.
 
-|`relayAllMessageHeaders` | No |All message headers will be propagated (without processing by Message
-Header Filters)
- _Type_: `boolean`
- _Default_: `false`
+You can find the `InsertResponseOutHeaderProcessor` example in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java#L730[CxfMessageHeadersRelayTest]:
 
-|`allowFilterNamespaceClash` |No |If two filters overlap in activation namespace, the property control how
-it should be handled. If the value is `true`, last one wins. If the
-value is `false`, it will throw an exception
- _Type_: `boolean`
- _Default_: `false`
-|=======================================================================
+[source,java]
+----
+public static class InsertResponseOutHeaderProcessor implements Processor {
 
-== Configure the CXF endpoints with Spring
+    public void process(Exchange exchange) throws Exception {
+        List<SoapHeader> soapHeaders = CastUtils.cast((List<?>)exchange.getIn().getHeader(Header.HEADER_LIST));
 
-You can configure the CXF endpoint with the Spring configuration file
-shown below, and you can also embed the endpoint into the `camelContext`
-tags. When you are invoking the service endpoint, you can set the
-`operationName` and `operationNamespace` headers to explicitly state
-which operation you are calling.
+        // Insert a new header
+        String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><outofbandHeader "
+            + "xmlns=\"http://cxf.apache.org/outofband/Header\" hdrAttribute=\"testHdrAttribute\" "
+            + "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" soap:mustUnderstand=\"1\">"
+            + "<name>New_testOobHeader</name><value>New_testOobHeaderValue</value></outofbandHeader>";
+        SoapHeader newHeader = new SoapHeader(soapHeaders.get(0).getName(),
+                       DOMUtils.readXml(new StringReader(xml)).getDocumentElement());
+        // make sure direction is OUT since it is a response message.
+        newHeader.setDirection(Direction.DIRECTION_OUT);
+        //newHeader.setMustUnderstand(false);
+        soapHeaders.add(newHeader);
 
-[source,xml]
-----------------------------------------------------------------------------------------------------------------
-<beans xmlns="http://www.springframework.org/schema/beans"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-        xmlns:cxf="http://camel.apache.org/schema/cxf"
-        xsi:schemaLocation="
-        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
-        http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
-        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
-     <cxf:cxfEndpoint id="routerEndpoint" address="http://localhost:9003/CamelContext/RouterPort"
-            serviceClass="org.apache.hello_world_soap_http.GreeterImpl"/>
-     <cxf:cxfEndpoint id="serviceEndpoint" address="http://localhost:9000/SoapContext/SoapPort"
-            wsdlURL="testutils/hello_world.wsdl"
-            serviceClass="org.apache.hello_world_soap_http.Greeter"
-            endpointName="s:SoapPort"
-            serviceName="s:SOAPService"
-        xmlns:s="http://apache.org/hello_world_soap_http" />
-     <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
-       <route>
-         <from uri="cxf:bean:routerEndpoint" />
-         <to uri="cxf:bean:serviceEndpoint" />
-       </route>
-    </camelContext>
-  </beans>
-----------------------------------------------------------------------------------------------------------------
+    }
 
-Be sure to include the JAX-WS `schemaLocation` attribute specified on
-the root beans element. This allows CXF to validate the file and is
-required. Also note the namespace declarations at the end of the
-`<cxf:cxfEndpoint/>` tag. These declarations are required because the combined `\{namespace}localName` syntax is presently not supported for this tag's
-attribute values.
+}
+----
 
-The `cxf:cxfEndpoint` element supports many additional attributes:
+== How to get and set SOAP headers in PAYLOAD mode
 
-[width="100%",cols="50%,50%",options="header",]
-|=======================================================================
-|Name |Value
+We've already shown how to access the SOAP message as CxfPayload object in
+PAYLOAD mode in the section <<How to consume a message from a camel-cxf endpoint in PAYLOAD data format>>.
 
-|`PortName` |The endpoint name this service is implementing, it maps to the
-`wsdl:port@name`. In the format of `ns:PORT_NAME` where `ns` is a
-namespace prefix valid at this scope.
+Once you obtain a CxfPayload object, you can invoke the
+CxfPayload.getHeaders() method that returns a List of DOM Elements (SOAP
+headers).
 
-|`serviceName` |The service name this service is implementing, it maps to the
-`wsdl:service@name`. In the format of `ns:SERVICE_NAME` where `ns` is a
-namespace prefix valid at this scope.
+For an example see https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfPayLoadSoapHeaderTest.java#L51[CxfPayLoadSoapHeaderTest]:
 
-|`wsdlURL` |The location of the WSDL. Can be on the classpath, file system, or be
-hosted remotely.
+[source,java]
+----
+from(getRouterEndpointURI()).process(new Processor() {
+    @SuppressWarnings("unchecked")
+    public void process(Exchange exchange) throws Exception {
+        CxfPayload<SoapHeader> payload = exchange.getIn().getBody(CxfPayload.class);
+        List<Source> elements = payload.getBodySources();
+        assertNotNull(elements, "We should get the elements here");
+        assertEquals(1, elements.size(), "Get the wrong elements size");
 
-|`bindingId` |The `bindingId` for the service model to use.
+        Element el = new XmlConverter().toDOMElement(elements.get(0));
+        elements.set(0, new DOMSource(el));
+        assertEquals("http://camel.apache.org/pizza/types",
+                el.getNamespaceURI(), "Get the wrong namespace URI");
 
-|`address` |The service publish address.
+        List<SoapHeader> headers = payload.getHeaders();
+        assertNotNull(headers, "We should get the headers here");
+        assertEquals(1, headers.size(), "Get the wrong headers size");
+        assertEquals("http://camel.apache.org/pizza/types",
+                ((Element) (headers.get(0).getObject())).getNamespaceURI(), "Get the wrong namespace URI");
+        // alternatively you can also get the SOAP header via the camel header:
+        headers = exchange.getIn().getHeader(Header.HEADER_LIST, List.class);
+        assertNotNull(headers, "We should get the headers here");
+        assertEquals(1, headers.size(), "Get the wrong headers size");
+        assertEquals("http://camel.apache.org/pizza/types",
+                ((Element) (headers.get(0).getObject())).getNamespaceURI(), "Get the wrong namespace URI");
 
-|`bus` |The bus name that will be used in the JAX-WS endpoint.
+    }
 
-|`serviceClass` |The class name of the SEI (Service Endpoint Interface) class which could
-have JSR181 annotation or not.
-|=======================================================================
+})
+.to(getServiceEndpointURI());
+----
 
-It also supports many child elements:
+You can also use the same way as described in
+sub-chapter "How to get and set SOAP headers in POJO mode" to set or get
+the SOAP headers. So, you can use the
+header "org.apache.cxf.headers.Header.list" to get and set a list of
+SOAP headers.This does also mean that if you have a route that forwards
+from one Camel-cxf endpoint to another (SOAP Client -> Camel -> CXF
+service), now also the SOAP headers sent by the SOAP client are
+forwarded to the CXF service. If you do not want that these headers are
+forwarded you have to remove them in the Camel header
+"org.apache.cxf.headers.Header.list".
 
-[width="100%",cols="50%,50%",options="header",]
-|=======================================================================
-|Name |Value
+== SOAP headers are not available in RAW mode
 
-|`cxf:inInterceptors` |The incoming interceptors for this endpoint. A list of `<bean>` or
-`<ref>`.
+SOAP headers are not available in RAW mode as SOAP processing is
+skipped.
 
-|`cxf:inFaultInterceptors` |The incoming fault interceptors for this endpoint. A list of `<bean>` or
-`<ref>`.
+== How to throw a SOAP Fault from Camel
 
-|`cxf:outInterceptors` |The outgoing interceptors for this endpoint. A list of `<bean>` or
-`<ref>`.
+If you are using a `camel-cxf` endpoint to consume the SOAP request, you
+may need to throw the SOAP Fault from the camel context. +
+ Basically, you can use the `throwFault` DSL to do that; it works for
+`POJO`, `PAYLOAD` and `RAW` data format. +
+ You can define the soap fault as shown in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfCustomizedExceptionTest.java#L64[CxfCustomizedExceptionTest]:
 
-|`cxf:outFaultInterceptors` |The outgoing fault interceptors for this endpoint. A list of `<bean>` or
-`<ref>`.
+[source,java]
+----
+SOAP_FAULT = new SoapFault(EXCEPTION_MESSAGE, SoapFault.FAULT_CODE_CLIENT);
+Element detail = SOAP_FAULT.getOrCreateDetail();
+Document doc = detail.getOwnerDocument();
+Text tn = doc.createTextNode(DETAIL_TEXT);
+detail.appendChild(tn);
+----
 
-|`cxf:properties` | A properties map which should be supplied to the JAX-WS endpoint. See
-below.
+Then throw it as you like
 
-|`cxf:handlers` |A JAX-WS handler list which should be supplied to the JAX-WS endpoint.
-See below.
+[source,java]
+----
+from(routerEndpointURI).setFaultBody(constant(SOAP_FAULT));
+----
 
-|`cxf:dataBinding` |You can specify the which `DataBinding` will be use in the endpoint.
-This can be supplied using the Spring `<bean class="MyDataBinding"/>`
-syntax.
 
-|`cxf:binding` |You can specify the `BindingFactory` for this endpoint to use. This can
-be supplied using the Spring `<bean class="MyBindingFactory"/>` syntax.
+If your CXF endpoint is working in the `RAW` data format, you could
+set the SOAP Fault message in the message body and set the response
+code in the message header as demonstrated by https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfMessageStreamExceptionTest.java#L43[CxfMessageStreamExceptionTest]
 
-|`cxf:features` |The features that hold the interceptors for this endpoint. A list of
-beans or refs
+[source,java]
+----
+from(routerEndpointURI).process(new Processor() {
 
-|`cxf:schemaLocations` |The schema locations for endpoint to use. A list of schemaLocations
+    public void process(Exchange exchange) throws Exception {
+        Message out = exchange.getOut();
+        // Set the message body with the
+        out.setBody(this.getClass().getResourceAsStream("SoapFaultMessage.xml"));
+        // Set the response code here
+        out.setHeader(org.apache.cxf.message.Message.RESPONSE_CODE, new Integer(500));
+    }
 
-|`cxf:serviceFactory` |The service factory for this endpoint to use. This can be supplied using
-the Spring `<bean class="MyServiceFactory"/>` syntax
-|=======================================================================
+});
+----
 
-You can find more advanced examples that show how to provide
-interceptors, properties and handlers on the CXF
-http://cxf.apache.org/docs/jax-ws-configuration.html[JAX-WS
-Configuration page].
+Same for using POJO data format. You can set the SOAPFault on the out
+body.
 
-[NOTE]
-====
-You can use cxf:properties to set the camel-cxf endpoint's dataFormat
-and setDefaultBus properties from spring configuration file.
+[#propagate-request-response-context]
+== How to propagate a camel-cxf endpoint's request and response context
 
-[source,xml]
--------------------------------------------------------------------------
-<cxf:cxfEndpoint id="testEndpoint" address="http://localhost:9000/router"
-     serviceClass="org.apache.camel.component.cxf.HelloService"
-     endpointName="s:PortName"
-     serviceName="s:ServiceName"
-     xmlns:s="http://www.example.com/test">
-     <cxf:properties>
-       <entry key="dataFormat" value="RAW"/>
-       <entry key="setDefaultBus" value="true"/>
-     </cxf:properties>
-   </cxf:cxfEndpoint>
--------------------------------------------------------------------------
-====
+https://github.com/apache/cxf/blob/master/core/src/main/java/org/apache/cxf/endpoint/Client.java[CXF
+client API] provides a way to invoke the operation with request and
+response context. If you are using a `camel-cxf` endpoint producer to
+invoke the outside web service, you can set the request context and get
+response context with the following code:
 
-== How to make the camel-cxf component use log4j instead of java.util.logging
+[source,java]
+-------------------------------------------------------------------------------------------------------------
+        CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
+             public void process(final Exchange exchange) {
+                 final List<String> params = new ArrayList<String>();
+                 params.add(TEST_MESSAGE);
+                 // Set the request context to the inMessage
+                 Map<String, Object> requestContext = new HashMap<String, Object>();
+                 requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
+                 exchange.getIn().setBody(params);
+                 exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
+                 exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
+             }
+         });
+         org.apache.camel.Message out = exchange.getOut();
+         // The output is an object array, the first element of the array is the return value
+         Object\[\] output = out.getBody(Object\[\].class);
+         LOG.info("Received output text: " + output\[0\]);
+         // Get the response context form outMessage
+         Map<String, Object> responseContext = CastUtils.cast((Map)out.getHeader(Client.RESPONSE_CONTEXT));
+         assertNotNull(responseContext);
+         assertEquals("Get the wrong wsdl operation name", "{http://apache.org/hello_world_soap_http}greetMe",
+                      responseContext.get("javax.xml.ws.wsdl.operation").toString());
+-------------------------------------------------------------------------------------------------------------
 
-CXF's default logger is `java.util.logging`. If you want to change it to
-log4j, proceed as follows. Create a file, in the classpath, named
-`META-INF/cxf/org.apache.cxf.logger`. This file should contain the
-fully-qualified name of the class,
-`org.apache.cxf.common.logging.Log4jLogger`, with no comments, on a
-single line.
+== Attachment Support
 
-== How to let camel-cxf response start with xml processing instruction
+*POJO Mode:* MTOM are supported if is enabled(see
+example in Payload Mode for enabling MTOM).  Since attachments are 
+marshalled and unmarshalled into POJOs, the attachments should be 
+retrieved from Camel Message Body(As parameter list), and it isn't
+possible to retrieve attachments by Camel Message API
 
-If you are using some SOAP client such as PHP, you will get this kind of
-error, because CXF doesn't add the XML processing instruction
-`<?xml version="1.0" encoding="utf-8"?>`:
+[source,java]
+--------------------------------------------
+DataHandler Exchange.getIn(AttachmentMessage.class).getAttachment(String id)
+--------------------------------------------
 
----------------------------------------------------------------------------------------
-Error:sendSms: SoapFault exception: [Client] looks like we got no XML document in [...]
----------------------------------------------------------------------------------------
+*Payload Mode:* MTOM is supported by this Mode. Attachments can be
+retrieved by Camel Message APIs mentioned above. SOAP with Attachment
+(SwA) is supported and attachments can be retrieved. SwA is
+the default (same as setting the CXF endpoint property "mtom-enabled" to
+false). 
 
-To resolve this issue, you just need to tell StaxOutInterceptor to
-write the XML start document for you, as in the https://github.com/apache/camel/blob/main/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/WriteXmlDeclarationInterceptor.java[WriteXmlDeclarationInterceptor] below:
+To enable MTOM, set the CXF endpoint property "mtom-enabled" to _true_.
 
-[source,java]
+[source,xml]
 ----
-public class WriteXmlDeclarationInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
-    public WriteXmlDeclarationInterceptor() {
-        super(Phase.PRE_STREAM);
-        addBefore(StaxOutInterceptor.class.getName());
-    }
+<cxf:cxfEndpoint id="routerEndpoint" address="http://localhost:${CXFTestSupport.port1}/CxfMtomRouterPayloadModeTest/jaxws-mtom/hello"
+         wsdlURL="mtom.wsdl"
+         serviceName="ns:HelloService"
+         endpointName="ns:HelloPort"
+         xmlns:ns="http://apache.org/camel/cxf/mtom_feature">
 
-    public void handleMessage(SoapMessage message) throws Fault {
-        message.put("org.apache.cxf.stax.force-start-document", Boolean.TRUE);
-    }
+     <cxf:properties>
+         <!--  enable mtom by setting this property to true -->
+         <entry key="mtom-enabled" value="true"/>
 
-}
+         <!--  set the camel-cxf endpoint data fromat to PAYLOAD mode -->
+         <entry key="dataFormat" value="PAYLOAD"/>
+     </cxf:properties>
+</cxf:cxfEndpoint>
 ----
 
-As an alternative you can add a message header for it as demonstrated in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerTest.java#L59[CxfConsumerTest]:
+You can produce a Camel message with attachment to send to a CXF
+endpoint in Payload mode.
 
 [source,java]
--------------------------------------------------------------------
- // set up the response context which force start document
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("org.apache.cxf.stax.force-start-document", Boolean.TRUE);
- exchange.getOut().setHeader(Client.RESPONSE_CONTEXT, map);
--------------------------------------------------------------------
-
-== How to consume a message from a camel-cxf endpoint in POJO data format
+----
+Exchange exchange = context.createProducerTemplate().send("direct:testEndpoint", new Processor() {
 
-The `camel-cxf` endpoint consumer POJO data format is based on the
-http://cxf.apache.org/docs/invokers.html[CXF invoker], so the
-message header has a property with the name of
-`CxfConstants.OPERATION_NAME` and the message body is a list of the SEI
-method parameters.
+    public void process(Exchange exchange) throws Exception {
+        exchange.setPattern(ExchangePattern.InOut);
+        List<Source> elements = new ArrayList<Source>();
+        elements.add(new DOMSource(DOMUtils.readXml(new StringReader(MtomTestHelper.REQ_MESSAGE)).getDocumentElement()));
+        CxfPayload<SoapHeader> body = new CxfPayload<SoapHeader>(new ArrayList<SoapHeader>(),
+            elements, null);
+        exchange.getIn().setBody(body);
+        exchange.getIn(AttachmentMessage.class).addAttachment(MtomTestHelper.REQ_PHOTO_CID,
+            new DataHandler(new ByteArrayDataSource(MtomTestHelper.REQ_PHOTO_DATA, "application/octet-stream")));
 
-Consider the https://github.com/apache/camel/blob/main/components/camel-cxf/camel-cxf-soap/src/test/java/org/apache/camel/wsdl_first/PersonProcessor.java[PersonProcessor] example code:
+        exchange.getIn(AttachmentMessage.class).addAttachment(MtomTestHelper.REQ_IMAGE_CID,
+            new DataHandler(new ByteArrayDataSource(MtomTestHelper.requestJpeg, "image/jpeg")));
 
-[source,java]
-----
-public class PersonProcessor implements Processor {
+    }
 
-    private static final Logger LOG = LoggerFactory.getLogger(PersonProcessor.class);
+});
 
-    @Override
-    @SuppressWarnings("unchecked")
-    public void process(Exchange exchange) throws Exception {
-        LOG.info("processing exchange in camel");
+// process response 
 
-        BindingOperationInfo boi = (BindingOperationInfo) exchange.getProperty(BindingOperationInfo.class.getName());
-        if (boi != null) {
-            LOG.info("boi.isUnwrapped" + boi.isUnwrapped());
-        }
-        // Get the parameters list which element is the holder.
-        MessageContentsList msgList = (MessageContentsList) exchange.getIn().getBody();
-        Holder<String> personId = (Holder<String>) msgList.get(0);
-        Holder<String> ssn = (Holder<String>) msgList.get(1);
-        Holder<String> name = (Holder<String>) msgList.get(2);
+CxfPayload<SoapHeader> out = exchange.getMessage().getBody(CxfPayload.class);
+assertEquals(1, out.getBody().size());
 
-        if (personId.value == null || personId.value.length() == 0) {
-            LOG.info("person id 123, so throwing exception");
-            // Try to throw out the soap fault message
-            org.apache.camel.wsdl_first.types.UnknownPersonFault personFault
-                    = new org.apache.camel.wsdl_first.types.UnknownPersonFault();
-            personFault.setPersonId("");
-            org.apache.camel.wsdl_first.UnknownPersonFault fault
-                    = new org.apache.camel.wsdl_first.UnknownPersonFault("Get the null value of person name", personFault);
-            exchange.getMessage().setBody(fault);
-            return;
-        }
+Map<String, String> ns = new HashMap<>();
+ns.put("ns", MtomTestHelper.SERVICE_TYPES_NS);
+ns.put("xop", MtomTestHelper.XOP_NS);
 
-        name.value = "Bonjour";
-        ssn.value = "123";
-        LOG.info("setting Bonjour as the response");
-        // Set the response message, first element is the return value of the operation,
-        // the others are the holders of method parameters
-        exchange.getMessage().setBody(new Object[] { null, personId, ssn, name });
-    }
+XPathUtils xu = new XPathUtils(ns);
+Element oute = new XmlConverter().toDOMElement(out.getBody().get(0));
+Element ele = (Element) xu.getValue("//ns:DetailResponse/ns:photo/xop:Include", oute,
+                XPathConstants.NODE);
+String photoId = ele.getAttribute("href").substring(4); // skip "cid:"
 
-}
-----
+ele = (Element) xu.getValue("//ns:DetailResponse/ns:image/xop:Include", oute,
+                XPathConstants.NODE);
+String imageId = ele.getAttribute("href").substring(4); // skip "cid:"
 
-== How to prepare the message for the camel-cxf endpoint in POJO data format
+DataHandler dr = exchange.getMessage(AttachmentMessage.class).getAttachment(decodingReference(photoId));
+assertEquals("application/octet-stream", dr.getContentType());
+assertArrayEquals(MtomTestHelper.RESP_PHOTO_DATA, IOUtils.readBytesFromStream(dr.getInputStream()));
 
-The `camel-cxf` endpoint producer is based on the
-https://github.com/apache/cxf/blob/master/core/src/main/java/org/apache/cxf/endpoint/Client.java[CXF
-client API]. First you need to specify the operation name in the message
-header, then add the method parameters to a list, and initialize the
-message with this parameter list. The response message's body is a
-messageContentsList, you can get the result from that list.
+dr = exchange.getMessage(AttachmentMessage.class).getAttachment(decodingReference(imageId));
+assertEquals("image/jpeg", dr.getContentType());
 
-If you don't specify the operation name in the message header,
-`CxfProducer` will try to use the `defaultOperationName` from
-`CxfEndpoint`, if there is no `defaultOperationName` set on
-`CxfEndpoint`, it will pick up the first operationName from the Operation
-list.
+BufferedImage image = ImageIO.read(dr.getInputStream());
+assertEquals(560, image.getWidth());
+assertEquals(300, image.getHeight());
+----
 
-If you want to get the object array from the message body, you can get
-the body using `message.getBody(Object[].class)`, as shown in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfProducerRouterTest.java#L116[CxfProducerRouterTest.testInvokingSimpleServerWithParams]:
+You can also consume a Camel message received from a CXF endpoint in
+Payload mode.
+The https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/mtom/CxfMtomConsumerPayloadModeTest.java#L98[CxfMtomConsumerPayloadModeTest] illustrates how this works:
 
 [source,java]
 ----
-Exchange senderExchange = new DefaultExchange(context, ExchangePattern.InOut);
-final List<String> params = new ArrayList<>();
-// Prepare the request message for the camel-cxf procedure
-params.add(TEST_MESSAGE);
-senderExchange.getIn().setBody(params);
-senderExchange.getIn().setHeader(CxfConstants.OPERATION_NAME, ECHO_OPERATION);
+public static class MyProcessor implements Processor {
 
-Exchange exchange = template.send("direct:EndpointA", senderExchange);
+        @Override
+        @SuppressWarnings("unchecked")
+        public void process(Exchange exchange) throws Exception {
+            CxfPayload<SoapHeader> in = exchange.getIn().getBody(CxfPayload.class);
 
-org.apache.camel.Message out = exchange.getMessage();
-// The response message's body is an MessageContentsList which first element is the return value of the operation,
-// If there are some holder parameters, the holder parameter will be filled in the reset of List.
-// The result will be extract from the MessageContentsList with the String class type
-MessageContentsList result = (MessageContentsList) out.getBody();
-LOG.info("Received output text: " + result.get(0));
-Map<String, Object> responseContext = CastUtils.cast((Map<?, ?>) out.getHeader(Client.RESPONSE_CONTEXT));
-assertNotNull(responseContext);
-assertEquals("UTF-8", responseContext.get(org.apache.cxf.message.Message.ENCODING),
-        "We should get the response context here");
-assertEquals("echo " + TEST_MESSAGE, result.get(0), "Reply body on Camel is wrong");
-----
+            // verify request
+            assertEquals(1, in.getBody().size());
 
-== How to consume a message from a camel-cxf endpoint in PAYLOAD data format
+            Map<String, String> ns = new HashMap<>();
+            ns.put("ns", MtomTestHelper.SERVICE_TYPES_NS);
+            ns.put("xop", MtomTestHelper.XOP_NS);
 
-`PAYLOAD` means that you process the payload from the SOAP
-envelope as a native CxfPayload. `Message.getBody()` will return a
-`org.apache.camel.component.cxf.CxfPayload` object, with getters
-for SOAP message headers and the SOAP body.
+            XPathUtils xu = new XPathUtils(ns);
+            Element body = new XmlConverter().toDOMElement(in.getBody().get(0));
+            Element ele = (Element) xu.getValue("//ns:Detail/ns:photo/xop:Include", body,
+                    XPathConstants.NODE);
+            String photoId = ele.getAttribute("href").substring(4); // skip "cid:"
+            assertEquals(MtomTestHelper.REQ_PHOTO_CID, photoId);
 
-See https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerPayloadTest.java#L66[CxfConsumerPayloadTest]:
+            ele = (Element) xu.getValue("//ns:Detail/ns:image/xop:Include", body,
+                    XPathConstants.NODE);
+            String imageId = ele.getAttribute("href").substring(4); // skip "cid:"
+            assertEquals(MtomTestHelper.REQ_IMAGE_CID, imageId);
 
-[source,java]
-----
-protected RouteBuilder createRouteBuilder() {
-    return new RouteBuilder() {
-        public void configure() {
-            from(simpleEndpointURI + "&dataFormat=PAYLOAD").to("log:info").process(new Processor() {
-                @SuppressWarnings("unchecked")
-                public void process(final Exchange exchange) throws Exception {
-                    CxfPayload<SoapHeader> requestPayload = exchange.getIn().getBody(CxfPayload.class);
-                    List<Source> inElements = requestPayload.getBodySources();
-                    List<Source> outElements = new ArrayList<>();
-                    // You can use a customer toStringConverter to turn a CxfPayLoad message into String as you want
-                    String request = exchange.getIn().getBody(String.class);
-                    XmlConverter converter = new XmlConverter();
-                    String documentString = ECHO_RESPONSE;
+            DataHandler dr = exchange.getIn(AttachmentMessage.class).getAttachment(photoId);
+            assertEquals("application/octet-stream", dr.getContentType());
+            assertArrayEquals(MtomTestHelper.REQ_PHOTO_DATA, IOUtils.readBytesFromStream(dr.getInputStream()));
+
+            dr = exchange.getIn(AttachmentMessage.class).getAttachment(imageId);
+            assertEquals("image/jpeg", dr.getContentType());
+            assertArrayEquals(MtomTestHelper.requestJpeg, IOUtils.readBytesFromStream(dr.getInputStream()));
+
+            // create response
+            List<Source> elements = new ArrayList<>();
+            elements.add(new DOMSource(StaxUtils.read(new StringReader(MtomTestHelper.RESP_MESSAGE)).getDocumentElement()));
+            CxfPayload<SoapHeader> sbody = new CxfPayload<>(
+                    new ArrayList<SoapHeader>(),
+                    elements, null);
+            exchange.getMessage().setBody(sbody);
+            exchange.getMessage(AttachmentMessage.class).addAttachment(MtomTestHelper.RESP_PHOTO_CID,
+                    new DataHandler(new ByteArrayDataSource(MtomTestHelper.RESP_PHOTO_DATA, "application/octet-stream")));
+
+            exchange.getMessage(AttachmentMessage.class).addAttachment(MtomTestHelper.RESP_IMAGE_CID,
+                    new DataHandler(new ByteArrayDataSource(MtomTestHelper.responseJpeg, "image/jpeg")));
 
-                    Element in = new XmlConverter().toDOMElement(inElements.get(0));
-                    // Just check the element namespace
-                    if (!in.getNamespaceURI().equals(ELEMENT_NAMESPACE)) {
-                        throw new IllegalArgumentException("Wrong element namespace");
-                    }
-                    if (in.getLocalName().equals("echoBoolean")) {
-                        documentString = ECHO_BOOLEAN_RESPONSE;
-                        checkRequest("ECHO_BOOLEAN_REQUEST", request);
-                    } else {
-                        documentString = ECHO_RESPONSE;
-                        checkRequest("ECHO_REQUEST", request);
-                    }
-                    Document outDocument = converter.toDOMDocument(documentString, exchange);
-                    outElements.add(new DOMSource(outDocument.getDocumentElement()));
-                    // set the payload header with null
-                    CxfPayload<SoapHeader> responsePayload = new CxfPayload<>(null, outElements, null);
-                    exchange.getMessage().setBody(responsePayload);
-                }
-            });
         }
-    };
+    }
 }
 ----
 
-== How to get and set SOAP headers in POJO mode
+*Message Mode:* Attachments are not supported as it does not process the
+message at all.
 
-`POJO` means that the data format is a "list of Java objects" when the
-camel-cxf endpoint produces or consumes Camel exchanges. Even though
-Camel exposes the message body as POJOs in this mode, camel-cxf still
-provides access to read and write SOAP headers. However, since CXF
-interceptors remove in-band SOAP headers from the header list after they
-have been processed, only out-of-band SOAP headers are available to
-camel-cxf in POJO mode.
+*CXF_MESSAGE Mode*: MTOM is supported, and Attachments can be retrieved
+by Camel Message APIs mentioned above. Note that when receiving a
+multipart (i.e. MTOM) message the default SOAPMessage to String
+converter will provide the complete multipart payload on the body. If
+you require just the SOAP XML as a String, you can set the message body
+with message.getSOAPPart(), and Camel convert can do the rest of work
+for you.
 
-The following example illustrates how to get/set SOAP headers. Suppose we
-have a route that forwards from one Camel-cxf endpoint to another. That
-is, SOAP Client -> Camel -> CXF service. We can attach two processors to
-obtain/insert SOAP headers at (1) before a request goes out to the CXF
-service and (2) before the response comes back to the SOAP Client. Processor
-(1) and (2) in this example are InsertRequestOutHeaderProcessor and
-InsertResponseOutHeaderProcessor. Our route looks like this:
+== Streaming Support in PAYLOAD mode
 
-[source,xml]
-----
-<route>
-    <from uri="cxf:bean:routerRelayEndpointWithInsertion"/>
-    <process ref="InsertRequestOutHeaderProcessor" />
-    <to uri="cxf:bean:serviceRelayEndpointWithInsertion"/>
-    <process ref="InsertResponseOutHeaderProcessor" />
-</route>
-----
+The camel-cxf component now supports streaming of incoming
+messages when using PAYLOAD mode. Previously, the incoming messages
+would have been completely DOM parsed. For large messages, this is time
+consuming and uses a significant amount of memory. The incoming messages can remain as a javax.xml.transform.Source while
+being routed and, if nothing modifies the payload, can then be directly
+streamed out to the target destination. For common "simple proxy" use
+cases (example: from("cxf:...").to("cxf:...")), this can provide very
+significant performance increases as well as significantly lowered
+memory requirements.
 
-SOAP headers are propagated to and from Camel Message headers. The Camel
-message header name is "org.apache.cxf.headers.Header.list" which is a
-constant defined in CXF (org.apache.cxf.headers.Header.HEADER_LIST). The
-header value is a List of CXF SoapHeader objects
-(org.apache.cxf.binding.soap.SoapHeader). The following snippet is the
-InsertResponseOutHeaderProcessor (that insert a new SOAP header in the
-response message). The way to access SOAP headers in both
-InsertResponseOutHeaderProcessor and InsertRequestOutHeaderProcessor are
-actually the same. The only difference between the two processors is
-setting the direction of the inserted SOAP header.
+However, there are cases where streaming may not be appropriate or
+desired. Due to the streaming nature, invalid incoming XML may not be
+caught until later in the processing chain. Also, certain actions may
+require the message to be DOM parsed anyway (like WS-Security or message
+tracing and such) in which case the advantages of the streaming is
+limited. At this point, there are two ways to control the streaming:
 
-You can find the `InsertResponseOutHeaderProcessor` example in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java#L730[CxfMessageHeadersRelayTest]:
+* Endpoint property: you can add "allowStreaming=false" as an endpoint
+property to turn the streaming on/off.
 
-[source,java]
-----
-public static class InsertResponseOutHeaderProcessor implements Processor {
+* Component property: the CxfComponent object also has an allowStreaming
+property that can set the default for endpoints created from that
+component.
 
-    public void process(Exchange exchange) throws Exception {
-        List<SoapHeader> soapHeaders = CastUtils.cast((List<?>)exchange.getIn().getHeader(Header.HEADER_LIST));
+Global system property: you can add a system property of
+"org.apache.camel.component.cxf.streaming" to "false" to turn it off.
+That sets the global default, but setting the endpoint property above
+will override this value for that endpoint.
 
-        // Insert a new header
-        String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><outofbandHeader "
-            + "xmlns=\"http://cxf.apache.org/outofband/Header\" hdrAttribute=\"testHdrAttribute\" "
-            + "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" soap:mustUnderstand=\"1\">"
-            + "<name>New_testOobHeader</name><value>New_testOobHeaderValue</value></outofbandHeader>";
-        SoapHeader newHeader = new SoapHeader(soapHeaders.get(0).getName(),
-                       DOMUtils.readXml(new StringReader(xml)).getDocumentElement());
-        // make sure direction is OUT since it is a response message.
-        newHeader.setDirection(Direction.DIRECTION_OUT);
-        //newHeader.setMustUnderstand(false);
-        soapHeaders.add(newHeader);
+== Using the generic CXF Dispatch mode
 
-    }
+The camel-cxf component supports the generic
+https://cxf.apache.org/docs/jax-ws-dispatch-api.html[CXF dispatch
+mode] that can transport messages of arbitrary structures (i.e., not
+bound to a specific XML schema). To use this mode, you simply omit
+specifying the wsdlURL and serviceClass attributes of the CXF endpoint.
 
-}
-----
+[source,xml]
+-------------------------------------------------------------------------------------------
+<cxf:cxfEndpoint id="testEndpoint" address="http://localhost:9000/SoapContext/SoapAnyPort">
+     <cxf:properties>
+       <entry key="dataFormat" value="PAYLOAD"/>
+     </cxf:properties>
+   </cxf:cxfEndpoint>
+-------------------------------------------------------------------------------------------
 
-== How to get and set SOAP headers in PAYLOAD mode
+It is noted that the default CXF dispatch client does not send a
+specific SOAPAction header. Therefore, when the target service requires
+a specific SOAPAction value, it is supplied in the Camel header using
+the key SOAPAction (case-insensitive).
 
-We've already shown how to access the SOAP message as CxfPayload object in
-PAYLOAD mode in the section <<How to consume a message from a camel-cxf endpoint in PAYLOAD data format>>.
+[#cxf-loggingout-interceptor-in-message-mode]
+=== How to enable CXF's LoggingOutInterceptor in RAW mode
 
-Once you obtain a CxfPayload object, you can invoke the
-CxfPayload.getHeaders() method that returns a List of DOM Elements (SOAP
-headers).
+CXF's `LoggingOutInterceptor` outputs outbound message that goes on the
+wire to logging system (Java Util Logging). Since the
+`LoggingOutInterceptor` is in `PRE_STREAM` phase (but `PRE_STREAM` phase
+is removed in `RAW` mode), you have to configure
+`LoggingOutInterceptor` to be run during the `WRITE` phase. The
+following is an example.
 
-For an example see https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfPayLoadSoapHeaderTest.java#L51[CxfPayLoadSoapHeaderTest]:
+[source,xml]
+-------------------------------------------------------------------------------------------------------
+<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor">
+    <!--  it really should have been user-prestream but CXF does have such phase! -->
+    <constructor-arg value="target/write"/>
+</bean>
 
-[source,java]
-----
-from(getRouterEndpointURI()).process(new Processor() {
-    @SuppressWarnings("unchecked")
-    public void process(Exchange exchange) throws Exception {
-        CxfPayload<SoapHeader> payload = exchange.getIn().getBody(CxfPayload.class);
-        List<Source> elements = payload.getBodySources();
-        assertNotNull(elements, "We should get the elements here");
-        assertEquals(1, elements.size(), "Get the wrong elements size");
+<cxf:cxfEndpoint id="serviceEndpoint" address="http://localhost:${CXFTestSupport.port2}/LoggingInterceptorInMessageModeTest/helloworld"
+    serviceClass="org.apache.camel.component.cxf.HelloService">
+    <cxf:outInterceptors>
+        <ref bean="loggingOutInterceptor"/>
+    </cxf:outInterceptors>
+    <cxf:properties>
+        <entry key="dataFormat" value="RAW"/>
+    </cxf:properties>
+</cxf:cxfEndpoint>
+-------------------------------------------------------------------------------------------------------
+
+=== Description of relayHeaders option
 
-        Element el = new XmlConverter().toDOMElement(elements.get(0));
-        elements.set(0, new DOMSource(el));
-        assertEquals("http://camel.apache.org/pizza/types",
-                el.getNamespaceURI(), "Get the wrong namespace URI");
+There are _in-band_ and _out-of-band_ on-the-wire headers from the
+perspective of a JAXWS WSDL-first developer.
 
-        List<SoapHeader> headers = payload.getHeaders();
-        assertNotNull(headers, "We should get the headers here");
-        assertEquals(1, headers.size(), "Get the wrong headers size");
-        assertEquals("http://camel.apache.org/pizza/types",
-                ((Element) (headers.get(0).getObject())).getNamespaceURI(), "Get the wrong namespace URI");
-        // alternatively you can also get the SOAP header via the camel header:
-        headers = exchange.getIn().getHeader(Header.HEADER_LIST, List.class);
-        assertNotNull(headers, "We should get the headers here");
-        assertEquals(1, headers.size(), "Get the wrong headers size");
-        assertEquals("http://camel.apache.org/pizza/types",
-                ((Element) (headers.get(0).getObject())).getNamespaceURI(), "Get the wrong namespace URI");
+The _in-band_ headers are headers that are explicitly defined as part of
+the WSDL binding contract for an endpoint such as SOAP headers.
 
-    }
+The _out-of-band_ headers are headers that are serialized over the wire,
+but are not explicitly part of the WSDL binding contract.
 
-})
-.to(getServiceEndpointURI());
-----
+Headers relaying/filtering is bi-directional.
 
-You can also use the same way as described in
-sub-chapter "How to get and set SOAP headers in POJO mode" to set or get
-the SOAP headers. So, you can use the
-header "org.apache.cxf.headers.Header.list" to get and set a list of
-SOAP headers.This does also mean that if you have a route that forwards
-from one Camel-cxf endpoint to another (SOAP Client -> Camel -> CXF
-service), now also the SOAP headers sent by the SOAP client are
-forwarded to the CXF service. If you do not want that these headers are
-forwarded you have to remove them in the Camel header
-"org.apache.cxf.headers.Header.list".
+When a route has a CXF endpoint and the developer needs to have
+on-the-wire headers, such as SOAP headers, be relayed along the route to
+be consumed say by another JAXWS endpoint, then `relayHeaders` should be
+set to `true`, which is the default value.
 
-== SOAP headers are not available in RAW mode
 
-SOAP headers are not available in RAW mode as SOAP processing is
-skipped.
+=== Available only in POJO mode
 
-== How to throw a SOAP Fault from Camel
+The `relayHeaders=true` expresses an intent to relay the headers. The
+actual decision on whether a given header is relayed is delegated to a
+pluggable instance that implements the `MessageHeadersRelay` interface.
+A concrete implementation of `MessageHeadersRelay` will be consulted to
+decide if a header needs to be relayed or not. There is already an
+implementation of `SoapMessageHeadersRelay` which binds itself to
+well-known SOAP name spaces. Currently only out-of-band headers are
+filtered, and in-band headers will always be relayed when
+`relayHeaders=true`. If there is a header on the wire whose name space
+is unknown to the runtime, then a fall back `DefaultMessageHeadersRelay`
+will be used, which simply allows all headers to be relayed.
 
-If you are using a `camel-cxf` endpoint to consume the SOAP request, you
-may need to throw the SOAP Fault from the camel context. +
- Basically, you can use the `throwFault` DSL to do that; it works for
-`POJO`, `PAYLOAD` and `RAW` data format. +
- You can define the soap fault as shown in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfCustomizedExceptionTest.java#L64[CxfCustomizedExceptionTest]:
+The `relayHeaders=false` setting specifies that all headers in-band and
+out-of-band should be dropped.
 
-[source,java]
-----
-SOAP_FAULT = new SoapFault(EXCEPTION_MESSAGE, SoapFault.FAULT_CODE_CLIENT);
-Element detail = SOAP_FAULT.getOrCreateDetail();
-Document doc = detail.getOwnerDocument();
-Text tn = doc.createTextNode(DETAIL_TEXT);
-detail.appendChild(tn);
-----
+You can plugin your own `MessageHeadersRelay` implementations overriding
+or adding additional ones to the list of relays. In order to override a
+preloaded relay instance just make sure that your `MessageHeadersRelay`
+implementation services the same name spaces as the one you looking to
+override. Also note, that the overriding relay has to service all of the
+name spaces as the one you looking to override, or else a runtime
+exception on route start up will be thrown as this would introduce an
+ambiguity in name spaces to relay instance mappings.
 
-Then throw it as you like
+[source,xml]
+-------------------------------------------------------------------------------------------------------
+<cxf:cxfEndpoint ...>
+   <cxf:properties>
+     <entry key="org.apache.camel.cxf.message.headers.relays">
+       <list>
+         <ref bean="customHeadersRelay"/>
+       </list>
+     </entry>
+   </cxf:properties>
+ </cxf:cxfEndpoint>
+ <bean id="customHeadersRelay" class="org.apache.camel.component.cxf.soap.headers.CustomHeadersRelay"/>
+-------------------------------------------------------------------------------------------------------
 
-[source,java]
-----
-from(routerEndpointURI).setFaultBody(constant(SOAP_FAULT));
-----
+Take a look at the tests that show how you'd be able to relay/drop
+headers here:
 
+https://github.com/apache/camel/blob/main/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java[https://github.com/apache/camel/blob/main/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java]
 
-If your CXF endpoint is working in the `RAW` data format, you could
-set the SOAP Fault message in the message body and set the response
-code in the message header as demonstrated by https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfMessageStreamExceptionTest.java#L43[CxfMessageStreamExceptionTest]
+* `POJO` and `PAYLOAD` modes are supported. In `POJO` mode, only
+out-of-band message headers are available for filtering as the in-band
+headers have been processed and removed from header list by CXF. The
+in-band headers are incorporated into the `MessageContentList` in POJO
+mode. The `camel-cxf` component does make any attempt to remove the
+in-band headers from the `MessageContentList`. If filtering of in-band
+headers is required, please use `PAYLOAD` mode or plug in a (pretty
+straightforward) CXF interceptor/JAXWS Handler to the CXF endpoint.
+* The Message Header Relay mechanism has been merged into
+`CxfHeaderFilterStrategy`. The `relayHeaders` option, its semantics, and
+default value remain the same, but it is a property of
+`CxfHeaderFilterStrategy`.
+ Here is an example of configuring it.
 
-[source,java]
-----
-from(routerEndpointURI).process(new Processor() {
+[source,xml]
+-------------------------------------------------------------------------------------------------------
+<bean id="dropAllMessageHeadersStrategy" class="org.apache.camel.component.cxf.transport.header.CxfHeaderFilterStrategy">
 
-    public void process(Exchange exchange) throws Exception {
-        Message out = exchange.getOut();
-        // Set the message body with the
-        out.setBody(this.getClass().getResourceAsStream("SoapFaultMessage.xml"));
-        // Set the response code here
-        out.setHeader(org.apache.cxf.message.Message.RESPONSE_CODE, new Integer(500));
-    }
+    <!--  Set relayHeaders to false to drop all SOAP headers -->
+    <property name="relayHeaders" value="false"/>
 
-});
-----
+</bean>
+-------------------------------------------------------------------------------------------------------
 
-Same for using POJO data format. You can set the SOAPFault on the out
-body.
+Then, your endpoint can reference the `CxfHeaderFilterStrategy`.
 
-[#propagate-request-response-context]
-== How to propagate a camel-cxf endpoint's request and response context
+[source,xml]
+-------------------------------------------------------------------------------------------------------
+<route>
+    <from uri="cxf:bean:routerNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
+    <to uri="cxf:bean:serviceNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
+</route>
+-------------------------------------------------------------------------------------------------------
 
-https://github.com/apache/cxf/blob/master/core/src/main/java/org/apache/cxf/endpoint/Client.java[CXF
-client API] provides a way to invoke the operation with request and
-response context. If you are using a `camel-cxf` endpoint producer to
-invoke the outside web service, you can set the request context and get
-response context with the following code:
+* The `MessageHeadersRelay` interface has changed slightly and has been
+renamed to `MessageHeaderFilter`. It is a property of
+`CxfHeaderFilterStrategy`. Here is an example of configuring user
+defined Message Header Filters:
 
-[source,java]
--------------------------------------------------------------------------------------------------------------
-        CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
-             public void process(final Exchange exchange) {
-                 final List<String> params = new ArrayList<String>();
-                 params.add(TEST_MESSAGE);
-                 // Set the request context to the inMessage
-                 Map<String, Object> requestContext = new HashMap<String, Object>();
-                 requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
-                 exchange.getIn().setBody(params);
-                 exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
-                 exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
-             }
-         });
-         org.apache.camel.Message out = exchange.getOut();
-         // The output is an object array, the first element of the array is the return value
-         Object\[\] output = out.getBody(Object\[\].class);
-         LOG.info("Received output text: " + output\[0\]);
-         // Get the response context form outMessage
-         Map<String, Object> responseContext = CastUtils.cast((Map)out.getHeader(Client.RESPONSE_CONTEXT));
-         assertNotNull(responseContext);
-         assertEquals("Get the wrong wsdl operation name", "{http://apache.org/hello_world_soap_http}greetMe",
-                      responseContext.get("javax.xml.ws.wsdl.operation").toString());
--------------------------------------------------------------------------------------------------------------
+[source,xml]
+-------------------------------------------------------------------------------------------------------
+<bean id="customMessageFilterStrategy" class="org.apache.camel.component.cxf.transport.header.CxfHeaderFilterStrategy">
+    <property name="messageHeaderFilters">
+        <list>
+            <!--  SoapMessageHeaderFilter is the built in filter.  It can be removed by omitting it. -->
+            <bean class="org.apache.camel.component.cxf.common.header.SoapMessageHeaderFilter"/>
 
-== Attachment Support
+            <!--  Add custom filter here -->
+            <bean class="org.apache.camel.component.cxf.soap.headers.CustomHeaderFilter"/>
+        </list>
+    </property>
+</bean>
+-------------------------------------------------------------------------------------------------------
 
-*POJO Mode:* MTOM are supported if is enabled(see
-example in Payload Mode for enabling MTOM).  Since attachments are 
-marshalled and unmarshalled into POJOs, the attachments should be 
-retrieved from Camel Message Body(As parameter list), and it isn't
-possible to retrieve attachments by Camel Message API
+* In addition to `relayHeaders`, the following properties can be
+configured in `CxfHeaderFilterStrategy`.
 
-[source,java]
---------------------------------------------
-DataHandler Exchange.getIn(AttachmentMessage.class).getAttachment(String id)
---------------------------------------------
+[width="100%",cols="10%,10%,80%",options="header",]
+|=======================================================================
+|Name |Required |Description
+|`relayHeaders` |No |All message headers will be processed by Message Header Filters
+ _Type_: `boolean`
+ _Default_: `true`
 
-*Payload Mode:* MTOM is supported by this Mode. Attachments can be
-retrieved by Camel Message APIs mentioned above. SOAP with Attachment
-(SwA) is supported and attachments can be retrieved. SwA is
-the default (same as setting the CXF endpoint property "mtom-enabled" to
-false). 
+|`relayAllMessageHeaders` | No |All message headers will be propagated (without processing by Message
+Header Filters)
+ _Type_: `boolean`
+ _Default_: `false`
 
-To enable MTOM, set the CXF endpoint property "mtom-enabled" to _true_.
+|`allowFilterNamespaceClash` |No |If two filters overlap in activation namespace, the property control how
+it should be handled. If the value is `true`, last one wins. If the
+value is `false`, it will throw an exception
+ _Type_: `boolean`
+ _Default_: `false`
+|=======================================================================
 
-[source,xml]
-----
-<cxf:cxfEndpoint id="routerEndpoint" address="http://localhost:${CXFTestSupport.port1}/CxfMtomRouterPayloadModeTest/jaxws-mtom/hello"
-         wsdlURL="mtom.wsdl"
-         serviceName="ns:HelloService"
-         endpointName="ns:HelloPort"
-         xmlns:ns="http://apache.org/camel/cxf/mtom_feature">
+== How to make the camel-cxf component use log4j instead of java.util.logging
 
-     <cxf:properties>
-         <!--  enable mtom by setting this property to true -->
-         <entry key="mtom-enabled" value="true"/>
+CXF's default logger is `java.util.logging`. If you want to change it to
+log4j, proceed as follows. Create a file, in the classpath, named
+`META-INF/cxf/org.apache.cxf.logger`. This file should contain the
+fully-qualified name of the class,
+`org.apache.cxf.common.logging.Log4jLogger`, with no comments, on a
+single line.
 
-         <!--  set the camel-cxf endpoint data fromat to PAYLOAD mode -->
-         <entry key="dataFormat" value="PAYLOAD"/>
-     </cxf:properties>
-</cxf:cxfEndpoint>
-----
+== How to let camel-cxf response start with xml processing instruction
+
+If you are using some SOAP client such as PHP, you will get this kind of
+error, because CXF doesn't add the XML processing instruction
+`<?xml version="1.0" encoding="utf-8"?>`:
+
+---------------------------------------------------------------------------------------
+Error:sendSms: SoapFault exception: [Client] looks like we got no XML document in [...]
+---------------------------------------------------------------------------------------
 
-You can produce a Camel message with attachment to send to a CXF
-endpoint in Payload mode.
+To resolve this issue, you just need to tell StaxOutInterceptor to
+write the XML start document for you, as in the https://github.com/apache/camel/blob/main/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/WriteXmlDeclarationInterceptor.java[WriteXmlDeclarationInterceptor] below:
 
 [source,java]
 ----
-Exchange exchange = context.createProducerTemplate().send("direct:testEndpoint", new Processor() {
-
-    public void process(Exchange exchange) throws Exception {
-        exchange.setPattern(ExchangePattern.InOut);
-        List<Source> elements = new ArrayList<Source>();
-        elements.add(new DOMSource(DOMUtils.readXml(new StringReader(MtomTestHelper.REQ_MESSAGE)).getDocumentElement()));
-        CxfPayload<SoapHeader> body = new CxfPayload<SoapHeader>(new ArrayList<SoapHeader>(),
-            elements, null);
-        exchange.getIn().setBody(body);
-        exchange.getIn(AttachmentMessage.class).addAttachment(MtomTestHelper.REQ_PHOTO_CID,
-            new DataHandler(new ByteArrayDataSource(MtomTestHelper.REQ_PHOTO_DATA, "application/octet-stream")));
-
-        exchange.getIn(AttachmentMessage.class).addAttachment(MtomTestHelper.REQ_IMAGE_CID,
-            new DataHandler(new ByteArrayDataSource(MtomTestHelper.requestJpeg, "image/jpeg")));
+public class WriteXmlDeclarationInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
+    public WriteXmlDeclarationInterceptor() {
+        super(Phase.PRE_STREAM);
+        addBefore(StaxOutInterceptor.class.getName());
+    }
 
+    public void handleMessage(SoapMessage message) throws Fault {
+        message.put("org.apache.cxf.stax.force-start-document", Boolean.TRUE);
     }
 
-});
+}
+----
 
-// process response 
+As an alternative you can add a message header for it as demonstrated in https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfConsumerTest.java#L59[CxfConsumerTest]:
 
-CxfPayload<SoapHeader> out = exchange.getMessage().getBody(CxfPayload.class);
-assertEquals(1, out.getBody().size());
+[source,java]
+-------------------------------------------------------------------
+ // set up the response context which force start document
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("org.apache.cxf.stax.force-start-document", Boolean.TRUE);
+ exchange.getOut().setHeader(Client.RESPONSE_CONTEXT, map);
+-------------------------------------------------------------------
 
-Map<String, String> ns = new HashMap<>();
-ns.put("ns", MtomTestHelper.SERVICE_TYPES_NS);
-ns.put("xop", MtomTestHelper.XOP_NS);
+== Configure the CXF endpoints with Spring
 
-XPathUtils xu = new XPathUtils(ns);
-Element oute = new XmlConverter().toDOMElement(out.getBody().get(0));
-Element ele = (Element) xu.getValue("//ns:DetailResponse/ns:photo/xop:Include", oute,
-                XPathConstants.NODE);
-String photoId = ele.getAttribute("href").substring(4); // skip "cid:"
+You can configure the CXF endpoint with the Spring configuration file
+shown below, and you can also embed the endpoint into the `camelContext`
+tags. When you are invoking the service endpoint, you can set the
+`operationName` and `operationNamespace` headers to explicitly state
+which operation you are calling.
 
-ele = (Element) xu.getValue("//ns:DetailResponse/ns:image/xop:Include", oute,
-                XPathConstants.NODE);
-String imageId = ele.getAttribute("href").substring(4); // skip "cid:"
+[source,xml]
+----------------------------------------------------------------------------------------------------------------
+<beans xmlns="http://www.springframework.org/schema/beans"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns:cxf="http://camel.apache.org/schema/cxf"
+        xsi:schemaLocation="
+        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
+        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
+     <cxf:cxfEndpoint id="routerEndpoint" address="http://localhost:9003/CamelContext/RouterPort"
+            serviceClass="org.apache.hello_world_soap_http.GreeterImpl"/>
+     <cxf:cxfEndpoint id="serviceEndpoint" address="http://localhost:9000/SoapContext/SoapPort"
+            wsdlURL="testutils/hello_world.wsdl"
+            serviceClass="org.apache.hello_world_soap_http.Greeter"
+            endpointName="s:SoapPort"
+            serviceName="s:SOAPService"
+        xmlns:s="http://apache.org/hello_world_soap_http" />
+     <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
+       <route>
+         <from uri="cxf:bean:routerEndpoint" />
+         <to uri="cxf:bean:serviceEndpoint" />
+       </route>
+    </camelContext>
+  </beans>
+----------------------------------------------------------------------------------------------------------------
 
-DataHandler dr = exchange.getMessage(AttachmentMessage.class).getAttachment(decodingReference(photoId));
-assertEquals("application/octet-stream", dr.getContentType());
-assertArrayEquals(MtomTestHelper.RESP_PHOTO_DATA, IOUtils.readBytesFromStream(dr.getInputStream()));
+Be sure to include the JAX-WS `schemaLocation` attribute specified on
+the root beans element. This allows CXF to validate the file and is
+required. Also note the namespace declarations at the end of the
+`<cxf:cxfEndpoint/>` tag. These declarations are required because the combined `\{namespace}localName` syntax is presently not supported for this tag's
+attribute values.
 
-dr = exchange.getMessage(AttachmentMessage.class).getAttachment(decodingReference(imageId));
-assertEquals("image/jpeg", dr.getContentType());
+The `cxf:cxfEndpoint` element supports many additional attributes:
 
-BufferedImage image = ImageIO.read(dr.getInputStream());
-assertEquals(560, image.getWidth());
-assertEquals(300, image.getHeight());
-----
+[width="100%",cols="50%,50%",options="header",]
+|=======================================================================
+|Name |Value
 
-You can also consume a Camel message received from a CXF endpoint in
-Payload mode.
-The https://github.com/apache/camel/blob/e818e0103490a106fa1538219f91a732ddebc562/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/mtom/CxfMtomConsumerPayloadModeTest.java#L98[CxfMtomConsumerPayloadModeTest] illustrates how this works:
+|`PortName` |The endpoint name this service is implementing, it maps to the
+`wsdl:port@name`. In the format of `ns:PORT_NAME` where `ns` is a
+namespace prefix valid at this scope.
 
-[source,java]
-----
-public static class MyProcessor implements Processor {
+|`serviceName` |The service name this service is implementing, it maps to the
+`wsdl:service@name`. In the format of `ns:SERVICE_NAME` where `ns` is a
+namespace prefix valid at this scope.
 
-        @Override
-        @SuppressWarnings("unchecked")
-        public void process(Exchange exchange) throws Exception {
-            CxfPayload<SoapHeader> in = exchange.getIn().getBody(CxfPayload.class);
+|`wsdlURL` |The location of the WSDL. Can be on the classpath, file system, or be
+hosted remotely.
 
-            // verify request
-            assertEquals(1, in.getBody().size());
+|`bindingId` |The `bindingId` for the service model to use.
 
-            Map<String, String> ns = new HashMap<>();
-            ns.put("ns", MtomTestHelper.SERVICE_TYPES_NS);
-            ns.put("xop", MtomTestHelper.XOP_NS);
+|`address` |The service publish address.
 
-            XPathUtils xu = new XPathUtils(ns);
-            Element body = new XmlConverter().toDOMElement(in.getBody().get(0));
-            Element ele = (Element) xu.getValue("//ns:Detail/ns:photo/xop:Include", body,
-                    XPathConstants.NODE);
-            String photoId = ele.getAttribute("href").substring(4); // skip "cid:"
-            assertEquals(MtomTestHelper.REQ_PHOTO_CID, photoId);
+|`bus` |The bus name that will be used in the JAX-WS endpoint.
 
-            ele = (Element) xu.getValue("//ns:Detail/ns:image/xop:Include", body,
-                    XPathConstants.NODE);
-            String imageId = ele.getAttribute("href").substring(4); // skip "cid:"
-            assertEquals(MtomTestHelper.REQ_IMAGE_CID, imageId);
+|`serviceClass` |The class name of the SEI (Service Endpoint Interface) class which could
+have JSR181 annotation or not.
+|=======================================================================
 
-            DataHandler dr = exchange.getIn(AttachmentMessage.class).getAttachment(photoId);
-            assertEquals("application/octet-stream", dr.getContentType());
-            assertArrayEquals(MtomTestHelper.REQ_PHOTO_DATA, IOUtils.readBytesFromStream(dr.getInputStream()));
+It also supports many child elements:
 
-            dr = exchange.getIn(AttachmentMessage.class).getAttachment(imageId);
-            assertEquals("image/jpeg", dr.getContentType());
-            assertArrayEquals(MtomTestHelper.requestJpeg, IOUtils.readBytesFromStream(dr.getInputStream()));
+[width="100%",cols="50%,50%",options="header",]
+|=======================================================================
+|Name |Value
 
-            // create response
-            List<Source> elements = new ArrayList<>();
-            elements.add(new DOMSource(StaxUtils.read(new StringReader(MtomTestHelper.RESP_MESSAGE)).getDocumentElement()));
-            CxfPayload<SoapHeader> sbody = new CxfPayload<>(
-                    new ArrayList<SoapHeader>(),
-                    elements, null);
-            exchange.getMessage().setBody(sbody);
-            exchange.getMessage(AttachmentMessage.class).addAttachment(MtomTestHelper.RESP_PHOTO_CID,
-                    new DataHandler(new ByteArrayDataSource(MtomTestHelper.RESP_PHOTO_DATA, "application/octet-stream")));
+|`cxf:inInterceptors` |The incoming interceptors for this endpoint. A list of `<bean>` or
+`<ref>`.
 
-            exchange.getMessage(AttachmentMessage.class).addAttachment(MtomTestHelper.RESP_IMAGE_CID,
-                    new DataHandler(new ByteArrayDataSource(MtomTestHelper.responseJpeg, "image/jpeg")));
+|`cxf:inFaultInterceptors` |The incoming fault interceptors for this endpoint. A list of `<bean>` or
+`<ref>`.
 
-        }
-    }
-}
-----
+|`cxf:outInterceptors` |The outgoing interceptors for this endpoint. A list of `<bean>` or
+`<ref>`.
 
-*Message Mode:* Attachments are not supported as it does not process the
-message at all.
+|`cxf:outFaultInterceptors` |The outgoing fault interceptors for this endpoint. A list of `<bean>` or
+`<ref>`.
 
-*CXF_MESSAGE Mode*: MTOM is supported, and Attachments can be retrieved
-by Camel Message APIs mentioned above. Note that when receiving a
-multipart (i.e. MTOM) message the default SOAPMessage to String
-converter will provide the complete multipart payload on the body. If
-you require just the SOAP XML as a String, you can set the message body
-with message.getSOAPPart(), and Camel convert can do the rest of work
-for you.
+|`cxf:properties` | A properties map which should be supplied to the JAX-WS endpoint. See
+below.
 
-== Streaming Support in PAYLOAD mode
+|`cxf:handlers` |A JAX-WS handler list which should be supplied to the JAX-WS endpoint.
+See below.
 
-The camel-cxf component now supports streaming of incoming
-messages when using PAYLOAD mode. Previously, the incoming messages
-would have been completely DOM parsed. For large messages, this is time
-consuming and uses a significant amount of memory. The incoming messages can remain as a javax.xml.transform.Source while
-being routed and, if nothing modifies the payload, can then be directly
-streamed out to the target destination. For common "simple proxy" use
-cases (example: from("cxf:...").to("cxf:...")), this can provide very
-significant performance increases as well as significantly lowered
-memory requirements.
+|`cxf:dataBinding` |You can specify the which `DataBinding` will be use in the endpoint.
+This can be supplied using the Spring `<bean class="MyDataBinding"/>`
+syntax.
 
-However, there are cases where streaming may not be appropriate or
-desired. Due to the streaming nature, invalid incoming XML may not be
-caught until later in the processing chain. Also, certain actions may
-require the message to be DOM parsed anyway (like WS-Security or message
-tracing and such) in which case the advantages of the streaming is
-limited. At this point, there are two ways to control the streaming:
+|`cxf:binding` |You can specify the `BindingFactory` for this endpoint to use. This can
+be supplied using the Spring `<bean class="MyBindingFactory"/>` syntax.
 
-* Endpoint property: you can add "allowStreaming=false" as an endpoint
-property to turn the streaming on/off.
+|`cxf:features` |The features that hold the interceptors for this endpoint. A list of
+beans or refs
 
-* Component property: the CxfComponent object also has an allowStreaming
-property that can set the default for endpoints created from that
-component.
+|`cxf:schemaLocations` |The schema locations for endpoint to use. A list of schemaLocations
 
-Global system property: you can add a system property of
-"org.apache.camel.component.cxf.streaming" to "false" to turn it off.
-That sets the global default, but setting the endpoint property above
-will override this value for that endpoint.
+|`cxf:serviceFactory` |The service factory for this endpoint to use. This can be supplied using
+the Spring `<bean class="MyServiceFactory"/>` syntax
+|=======================================================================
 
-== Using the generic CXF Dispatch mode
+You can find more advanced examples that show how to provide
+interceptors, properties and handlers on the CXF
+http://cxf.apache.org/docs/jax-ws-configuration.html[JAX-WS
+Configuration page].
 
-The camel-cxf component supports the generic
-https://cxf.apache.org/docs/jax-ws-dispatch-api.html[CXF dispatch
-mode] that can transport messages of arbitrary structures (i.e., not
-bound to a specific XML schema). To use this mode, you simply omit
-specifying the wsdlURL and serviceClass attributes of the CXF endpoint.
+[NOTE]
+====
+You can use cxf:properties to set the camel-cxf endpoint's dataFormat
+and setDefaultBus properties from spring configuration file.
 
 [source,xml]
--------------------------------------------------------------------------------------------
-<cxf:cxfEndpoint id="testEndpoint" address="http://localhost:9000/SoapContext/SoapAnyPort">
+-------------------------------------------------------------------------
+<cxf:cxfEndpoint id="testEndpoint" address="http://localhost:9000/router"
+     serviceClass="org.apache.camel.component.cxf.HelloService"
+     endpointName="s:PortName"
+     serviceName="s:ServiceName"
+     xmlns:s="http://www.example.com/test">
      <cxf:properties>
-       <entry key="dataFormat" value="PAYLOAD"/>
+       <entry key="dataFormat" value="RAW"/>
+       <entry key="setDefaultBus" value="true"/>
      </cxf:properties>
    </cxf:cxfEndpoint>
--------------------------------------------------------------------------------------------
-
-It is noted that the default CXF dispatch client does not send a
-specific SOAPAction header. Therefore, when the target service requires
-a specific SOAPAction value, it is supplied in the Camel header using
-the key SOAPAction (case-insensitive).
-
- 
+-------------------------------------------------------------------------
+====