Camel and CXF: How to get the outbound message? - soap

I have this camel route:
from("cxf:bean:endpointDocs01?loggingFeatureEnabled=true")
.to("direct:CBR")
.transform().method(WebServiceUtils.class,"response()")
.log("Outbound message: ${body}");
endpointDocs01 is defined in the blueprint like this:
<cxf:cxfEndpoint address="/documentos/" id="endpointDocs01"
serviceClass="com.adelco.webservice.ServiceDocs" wsdlURL="wsdl/wsdl03.wsdl">
<cxf:properties>
<entry key="schema-validation-enabled" value="true"/>
</cxf:properties>
</cxf:cxfEndpoint>
This route works with no problems including the schema-validation.
When I send a correct request, I can do "things" (in this case logging) using the last line of the exchange ".log("Outbound message: ${body}". In this case, the log shows this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<outputDocumento xmlns="http://webservice.adelco.com">
<respuesta>0</respuesta>
<mensaje>El mensaje [113282] fue recibido. Fecha recepciĆ³n Wed Apr 12 17:01:11 CLT 2017</mensaje>
</outputDocumento>
</soap:Body>
</soap:Envelope>
But, when I send a incorrect request, the line ".log("Log outbound message: ${body}" does nothing. However I get a response in the client (a Soap:Fault response)
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Unmarshalling Error: cvc-complex-type.2.4.a: Invalid content was found starting with element 'Sociedad'. One of '{"http://webservice.adelco.com":TipoMovimiento}' is expected.</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Why this soap:Fault response in not logged?

Your route is invoked after unmarshalling. So if unmarshalling fails because of invalid input, the route does not trigger and does not log.
Read this article about CXF architecture.
Phase Interceptors
CXF provides an InterceptorChain implementation called the PhaseInterceptorChain. [...]
Let us take a hypothetical simplified example (NOTE: these phases and interceptors don't necessarily exist in CXF). Let us say we are parsing a SOAP message. We may want to have two phases. First, a dispatch phase which parses the soap headers and determines which service to route the Message to. Second, an unmarshal phase which binds the SOAP body to JAXB objects.
Fault Handling
At any point during processing, an interceptor may throw a Fault, or a derivative of a Fault like the SoapFault. This will cause the chain to stop invoking and unwind it. Unwinding consists of calling handleFault on each interceptor that was invoked in reverse order.
When a Fault occurs the processing is stopped and the interceptor chain unwinded. CXF uses different chains for messages (in & out) and Faults (in & out).
To use custom beans (which must implement PhaseInterceptor interface) as interceptors:
<cxf:cxfEndpoint address="/documentos/" id="endpointDocs01"
serviceClass="com.adelco.webservice.ServiceDocs" wsdlURL="wsdl/wsdl03.wsdl">
<cxf:properties>
<entry key="schema-validation-enabled" value="true"/>
</cxf:properties>
<cxf:inInterceptors>
<ref component-id="inInterceptorBean" />
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref component-id="outInterceptorBean" />
</cxf:outInterceptors>
<cxf:outFaultInterceptors>
<ref component-id="outFaultInterceptorBean" />
</cxf:outFaultInterceptors>
</cxf:cxfEndpoint>

As #Allesandro has said, the chain would unwind on failure.
You could've added an onException clause:
onException(ValidationException.class)
.log("Outbound message: ${body}");
from("cxf:bean:endpointDocs01?loggingFeatureEnabled=true")
.to("direct:CBR")
.transform().method(WebServiceUtils.class,"response()")
.log("Outbound message: ${body}");

Related

Return SOAP response using WSO2 when invoking an API

I have created an API using the WSO2 Dataservice which return a JSON Object, My client Application waiting for a SOAP response not a JSON response, so I tried to use datamapper to change the response format from JSON to SOAP but it return only and XML format without the SOAP Body and the SOAP envelope. How to hundle this problem?
This is the SOAP response format I would like to get:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetFacturesClientResponse xmlns="http://tempuri.org/">
<GetFacturesClientResult>
<code>0</code>
<codeClient>P-2008-043681</codeClient>
<nom>na</nom>
<prenom>ouss</prenom>
</GetFacturesClientResult>
</GetFacturesClientResponse>
</soap:Body>
</soap:Envelope>
But I got this response:
<GetFacturesClientResponse xmlns="http://tempuri.org/">
<GetFacturesClientResult>
<code>0</code>
<codeClient>P-2008-043681</codeClient>
<nom>na</nom>
<prenom>ouss</prenom>
</GetFacturesClientResult>
</GetFacturesClientResponse>
And this is my code:
<datamapper config="gov:datamapper/oussama.dmc" inputSchema="gov:datamapper/oussama_inputSchema.json" inputType="JSON" outputSchema="gov:datamapper/oussama_outputSchema.json" outputType="JSON" xsltStyleSheet="gov:datamapper/oussama_xsltStyleSheet.xml"/>
Before the Respond Mediator set the following property.
<property name="messageType" value="application/soap+xml" scope="axis2"/>
Also in your Datamapper section the outputType="XML" not JSON.
Update
As ophychius mentioned. The above content type will create a SOAP 1.2 message and if you need a SOAP 1.1 message set the content type to text/xml. Typically, clients supports both versions.

When Aegis data binding is added the soap request body is empty

<jaxws:endpoint id="FacService"
address="/FacService" implementor="#facServiceProxyBean"
implementorClass="xxx.yyy.zzz.FacServiceSEI"
wsdlLocation="wsdl/FacService.wsdl" serviceName="fac:FacService"
endpointName="fac:FacServiceSEIPort">
<jaxws:features>
<ref bean="xsltFeature" />
</jaxws:features>
<jaxws:dataBinding>
<bean class="org.apache.cxf.aegis.databinding.AegisDatabinding">
</bean>
</jaxws:dataBinding>
</jaxws:endpoint>
Everything worked fine before adding Aegis data binding in my applicationContentCXF.xml file, But after adding the data binding the soap request body is empty for the request calls its making.
I am using <cxf.version>3.1.11</cxf.version>,<cxf.xjc.version>3.1.0</cxf.xjc.version>,<cxf.aegis.version>3.1.8</cxf.aegis.version>. Hope this version difference is not an issue.
I appreciate any help on this.

Apache Camel Route doesn't set body

I have two Apache Camel Routers in a Docker Network.
The one acts as client and sends some data in the body to the outer with a route.
I now want to get the modified body from the server router.
But apparently the body modification is never applied.
(For context the initial request is a post request where the final body should be the response)
Here is how my "client" route looks like:
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<restConfiguration component="restlet" bindingMode="json" port="8989" enableCORS="true"/>
<rest path="/finData">
<description>User rest service</description>
<post>
<to uri="direct:update"/>
</post>
</rest>
<route id="sendFinData">
<from uri="direct:update"/>
<log message="Got some data: ${body}"/>
<to uri="aclient://otherClient:9191"/>
</route>
And here is how the "server" looks like:
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="receiveFinData">
<from
uri="aserver://0.0.0.0:9191"/>
<log message="Received via data: ${body}"/>
<setBody>
<simple>{"result": true }</simple>
</setBody>
</route>
Update: If I add a second route to the "client" and call this one instead the external of the "server" and modify the body there it is working
Update due to comments: When the setBody is not the problem, then I have to ask about the flow in the code.
Are you calling /finData to send a request from the client to the server?
But who is listening on aclient://otherClient:9191? This route is not in your question.
And the caller of aserver://0.0.0.0:9191 is also not in your question.
Can you post the whole flow?
Original answer
I think #tadayoshi-sato already commented the solution to your problem.
You use the Camel Simple language (an Expression Language) to set a constant string into your message body.
Use the Camel Constant language instead.
<setBody>
<constant>{"result": true }</constant>
</setBody>

Caught exception inaccessible after routing

I'm building an exception handler for a jboss/camel project. Catching the exception with an onException clause works, and I can access the caught exception from a bean, but that work's only directly in exception handler. Since I need this handler in multiple different projects, I want to direct from the handler to a different route (in a different context), and handle the exception there centrally. But after the routing, the caught exception is not accessible any longer. Why is this, and how can I fix that. I'd like not to have to add another EJB to all of my projects.
Code:
<camelContext id="project1Context" xmlns="http://camel.apache.org/schema/spring">
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean ref="projectSpecificBean" method="peekException" />
<to uri="activemq:queue:centralExceptionHandling" />
</onException>
[... the routes of the context, where the exception happens ...]
</camelContext>
In the bean:
public void peekException(Exchange camelExchange) {
Exception e = camelExchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
log.warn("Caught general Exception", e);
}
This gives me access to the Exception inside the bean. But when I remove the <bean ref="projectSpecificBean" method="peekException" /> from the onException clause and add the same thing to the route at activemq:queue:centralExceptionHandling (to a bean local to that project there, in a different camelContext), I cannot find the exception anywhere in the exchange.
To be able to receive exception on the client of ActiveMQ queue you need use camel jms component option "transferExchange". This option must be enabled on both sides (during sending to ActiveMQ queue and receiving from it). In that case not only message body/headers will be sent but whole exchange will be transfered. See http://camel.apache.org/jms.html (transferExchange option). In general it will then looks like there are no queue between exception route and exception handler route. Here are some details from documentation:
You can transfer the exchange over the wire instead of just the body and headers. The following fields are transferred: In body, Out body, Fault body, In headers, Out headers, Fault headers, exchange properties, exchange exception. This requires that the objects are serializable. Camel will exclude any non-serializable objects and log it at WARN level. You must enable this option on both the producer and consumer side, so Camel knows the payloads is an Exchange and not a regular payload.
Example of code:
<camelContext id="project1Context" xmlns="http://camel.apache.org/schema/spring">
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean ref="projectSpecificBean" method="peekException" />
<to uri="activemq:queue:centralExceptionHandling?transferExchange=true" />
</onException>
[... the routes of the context, where the exception happens ...]
</camelContext>
// Somewhere in the central exception handler.
<camelContext id="exceptionHandlerContext" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq:queue:centralExceptionHandling?transferExchange=true" />
<!-- Here you will have almost completely the same exchange as it was before it was sent to ActiveMq -->
</route>
</camelContext>

How to send a response to the invoker of a service without reaching the end of the pipeline

I have 2 custom actions exectuting one after the other in the same service like this
<actions mep="RequestResponse">
<action name="ActionA" class="ClassA"/>
<action name="ActionB" class="ClassB"/>
</actions>
Suppose that ActionA does some validations over the received msg. If the validations found that the msg is invalid, how does you send a response to the invoker about that failure?
Right now in my actions I set the response in the message at ActionA and put a mark in it indicating that I found an error in ActionA and ActionB checks for that mark before executing its code. I found this method useful but a burden because all of my actions have to start with:
if (!markIspressent) {
//Code goes here
}
return message.
I have tried setting the response msg at ActionA and returning null to stop the pipeline but that isn't working. I also tried another method that I found of throwing an ActionProcessingFaultException(message,"SomeTextGoesInHere") but that also isn't working.
My main problem with this second one is that the ESB tries to reprocess the msg that thrown that exception and I don't see the response that I set into message until the ESB gives up and sends it back. But that giving up takes up to 60s.
So my question is how can you send a response msg to the invoker before reaching up the end of the service pipeline.
Thanks
The options in finishing earlier the pipeline are:
- return null;
- split the service into three (note service 1 is OneWay and based on your action's results routes to the appropriate service)
Service 1
<actions mep="OneWay">
<action name="ActionA" class="ClassA"/>
<action name="ActionA1" class="RouteBasedOnMsgContentTo(Service 2 or Service 3)"/>
</actions>
Service 2
<actions mep="RequestResponse">
<action name="ActionB" class="ClassB"/>
</actions>
Service 3
<actions mep="RequestResponse">
<action name="ActionC" class="ClassC-SimplyRespondsTheProblem"/>
</actions>