I have been recently working with Apache Camel 2.12.0 and Spring WS 2.1.3. Every external dependency in our application is hidden behind Apache Camel. It is really good because it hides the fact that I want to send a JMS message or I want to hit a SOAP service. What is more, the retry policy and monitoring are consistent and they are exposed out of the box through Camel's MBeans.
However, one day I needed to place two headers within SOAP envelope:
<soap-env:Header>
<MyFirstHeader>...</MyFirstHeader>
<MySecondHeader>...</MySecondHeader>
</soap-env:Header>
The creators of Camel's Spring WS endpoint provided a way for SOAP header addition by means of
CamelSpringWebserviceSoapHeader
property:
... @EndpointInject(uri = MY_SOAP_ENDPOINT) private ProducerTemplate mySoapEndpoint; public sendSoapWithMultipleHeaders() { mySoapEndpoint.requestBodyAndHeaders(whateverSoapPayload, createHeader()); } private Map<String, Object> createHeader() { Map<String, Object> headers = new HashMap<>(); headers.put("CamelSpringWebserviceSoapHeader", " <MyFirstHeader>...</MyFirstHeader>
It is easy, isn't it? Yes, it would be easy if the above code did not produce a nasty exception. The exception comes from<MySecondHeader>...</MySecondHeader> "); return headers; } ...
SpringWebserviceProducer
- XML of the headers is not well formed - it has two root elements. OK, let's try again: headers.put("CamelSpringWebserviceSoapHeader", "<soap-env:Header>
<MyFirstHeader>...</MyFirstHeader>
<MySecondHeader>...</MySecondHeader></soap-env:Header>
");
No more exceptions but produced header is:<soap-env:Header>
<soap-env:Header>
<MyFirstHeader>...</MyFirstHeader>
<MySecondHeader>...</MySecondHeader>
</soap-env:Header>
</soap-env:Header>
What a nightmare. Camel's Spring WS producer blindly copies the provided header into: <soap-env:Header>
.
It performs default identity XSL transformation provided by javax.xml.transform.Transformer. So I could not set my header with Camel's Spring WS endpoint. Spring WS was my last resort. After some analysis it appeared that there is ClientInterceptor (allows to modify SOAP message right before request sending and right after response receiving) within WebServiceTemplate which is used by Camel's Spring WS endpoint. I defined:
@Bean public WebServiceTemplate customWebServiceTemplate() { WebServiceTemplate template = new WebServiceTemplate(); template.setInterceptors(new ClientInterceptor[]{new MultipleSoapHeaderSetterInterceptor()}); return template; }and Camel's route:
spring-ws:https://mysoapservice.com?webServiceTemplate=#customWebServiceTemplateMultipleSoapHeaderSetterInterceptor retrieves the header and stores it correctly. With proposed solution the header is wrong for some time but right before message sending it is tailored to be in the correct form.
The conclusion is that the abstraction is good when it works without any difficulties. You can see that I needed to analyze low level details of the libraries. When there are any problems the abstraction becomes leaky and you need to dig deeper. Pssst... The magic disappears.
MultipleSoapHeaderSetterInterceptor cannot be found. can u share an example
ReplyDelete