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).
-
-
+-------------------------------------------------------------------------
+====