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>
Related
<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.
I have a camel endpoint where another application sends a post request with some data (Maybe with some other route)
I want to process this data and return something back to the application with the response of the POST request.
This is how my Camel Context looks at the moment:
<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"/>
</route>
</camelContext>
How can I send some answer back from the route sendFinData through the response of the post request?
The response the post request to your route receives is whatever is in your ${body} at the end of the route.
So in your route's end the ${body} contains whatever is the response from
<to uri="aclient://otherClient"/>
I don't use the Camel XML but in Java you would do:
rest("/finData")
.get()
.route()
.to("direct:sendFindData")
.end();
from("direct:sendFindData")
.to("aclient://otherClient")
.process(exchange -> exchange.getIn().setBody("Hello world"))
.setBody(simple("GoodBye world")) // same thing as line above
.end();
If the data you want to pass back to the requester is not the response of last API call in your route you need to save it somewhere temporarily (exchange.properties) and set it back to body later, or aggregate the responses so that the original data does not get overwritten. The route should produce data that the consumer expects. For normal rest requests this should be String type (like "GoodBye world"). If you want to return JSON for example be sure that the response body is JSON string at the end of the route.
Sorry that I'm not able to help with the XML but hope this is of some help to you.
In case you need to send back a response as an acknowledgment to the post request with custom data
then use transform which changes ${body} as you want
<route id="sendFinData">
<from uri="direct:update"/>
<log message="Got some data: ${body}"/>
<transform>
<simple>
I got some data
</simple>
</transform>
</route>
above will send back modified response as ack to request
If you want to respond back and also keep original data to forward to other route or store
then use multicast with transform
<route id="sendFinData">
<from uri="direct:update"/>
<log message="Got some data: ${body}"/>
<multicast>
<to uri="aclient://otherClient"/>
<transform>
<simple>
I got some data
</simple>
</transform>
</multicast>
</route>
above will send a response as ack to request and also forwards original data to otherClient (uri="aclient://otherClient")
or if you want to send the modified body to uri="aclient://otherClient" then send it after transform.
I've implemented a simple API service in WSO2 EI. The Input endpoint is configured to accept 'application/edi-hl7' essentially to read HL7 message. I perform certain transformations on the message and then need to respond with JSON. So, as part of the response JSON payload I also want to have original HL7 message sent as request payload and that too in original shape (not XML / JSON formatted).
Say my input is,
MSH|^~\&|ULTRA_V3.1|LAV|Web Portal|Web Portal|201810030949||ORU^R01|279857418|P|2.3.1|||AL|AL|AU
PID||3914950|21710425491^^^AUSHIC^MC~111111^^^^UR~5548391^^^^ULTRAREL||KAY^BRUCE^^^||19630604|M|||11 NJUONA AVE^^BAIURE BAY^WSN^2000||^^^^^^0243333323||||||21710425491
PV1||O|CSLI^KINUYMBER|||||214082JL^KOPRA^MARUTI^^KAYBB^DR
ORC|RE||18-15768981-000-0||R
OBR|1||18-15768981-000-0|000^PENDING^ULTRA^LAVLEI||20180914|201810030805|||CSKI||||201810030949||214082JL^KOPRA^MARUTI^^KAYBB^DR||TFT-0^GLU-0^CRP-0^RDA-0^MBA-0^LIP-0^FBE-0||683131368452|LAV^false|201810030949||LAB|I||^^^20180914|
And sample output is,
{"message": "MSH|^~\&|.....", "Otherkeys": "someVal"}
As part of message, I want original message as is and not SOAP body.
P.S. I need to remove \n\r available at the end of each line.
The main question is how can get the original message in original shape in the mediation flow.
Here is a sample API. Hope It helps.
Screenshot of the result
Just put payloadFactory Mediator in appropriate place in your Sequence.
<api xmlns="http://ws.apache.org/ns/synapse" name="Test" context="/Test">
<resource methods="POST" uri-template="/testHL">
<inSequence>
<log level="full"/>
<payloadFactory media-type="json">
<format>{"message": "$1", "Otherkeys": "someVal"}</format>
<args>
<arg evaluator="xml" expression="$body/*[1]"/>
</args>
</payloadFactory>
<respond/>
</inSequence>
</resource>
</api>
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}");
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>