Apache Camel Cannot modify json response - java

I'm bothered with returning an updated JSON, in fact, I receive a JSON defined as an opportunity (it's a business term, we don't need to explain it) from the cxfrs endpoint, convert it to String, then check the method invoked in jax-rs controller, if the we want make an update of Affair json, we publish the message as string in queue q.gestioncougar.cl.creation as described below:
<route id="serviceToCustomerLinksInboundRouting">
<from uri="cxfrs:bean:oab_serviceToObsIT?loggingFeatureEnabled={{gestioncougar.wscall.log.enable}}&loggingSizeLimit={{gestioncougar.wscall.log.size}}" />
<to uri="bean:log?method=info(*,'Body : ${body}')"/>
<convertBodyTo type="java.lang.String" />
<choice>
<when>
<simple>${header.operationName} == 'updateAffair'</simple>
<to uri="activemq:queue:q.gestioncougar.cl.creation?disableReplyTo=true" />
</when>
</choice>
</route>
The config of cxfrs service that is bonded to jax-rs controller:
<cxf:rsServer address="{{gestioncougar.service.in.obsit.url}}" id="oab_serviceToObsIT"
serviceClass="fr.oab.sie.esb.gestioncougar.customerLink.controller.CougarToCL" />
The JAX-RS controller:
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public interface CougarToCL {
#PUT
#Path("/updateAffair")
void updateAffair(String request);
}
Then we create a route that read from the queue q.gestioncougar.cl.creation, next unmarshal the body to POJO format (Java Model), moreover, read it by the method convertToCLFormat defined in the bean traiterCallFromCougar:
<route id="serviceToCustomerLinksCreationOutboundRoute">
<from uri="activemq:queue:q.gestioncougar.cl.creation" />
<!-- Sauvegarde du body initial -->
<unmarshal ref="formatJsonOpportunity" />
<!-- Sauvegarde du body -->
<setProperty propertyName="savedBody">
<simple>${body}</simple>
</setProperty>
<bean ref="traiterCallFromCougar" method="convertToCLFormat"/>
<to uri="bean:log?method=info(*,'CustomerLinks Body Format: ${body}')"/>
</route>
The method convertToCLFormat:
public void convertToCLFormat(Exchange exchange){
Opportunity opportunity = exchange.getProperty("savedBody",Opportunity.class);
EsbLogger.info(exchange, "opportunity {}", opportunity.toString());
exchange.getIn().setHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_JSON);
exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 200);
Affair affair = opportunityAffairMapper.toDealCL(opportunity.getDealCL());
String clBody = affairJsonParser.parsePojoToJson(affair);
EsbLogger.info(exchange, "affair {}", clBody);
exchange.getOut().setHeaders(exchange.getIn().getHeaders());
exchange.getOut().setBody(clBody);
}
and here is the problem !, even if I set the body with the newly mapped JSON defined as Affair, I don't see any change in postman response, but the body is changed when I check the log.
Take a look at the postman request:
Thanks a lot for your time and your help.

Do not put the converted JSON into the "out" message, but rather in the "in" one.
This was so confusing that they now recommend to simply use exchange.getMessage() (without any distinction whether in or out)
See more here:
https://camel.apache.org/manual/latest/camel-3-migration-guide.html#_getout_on_exchange
So this should work:
exchange.getMessage().setBody(clBody);

Related

What is the Camel endpoint for marshalling using bindy dataformat?

I have a requirement to dynamically create camel-bindy endpoint for marshalling the input. Below is the sample code:
<dataFormats>
<bindy id="bookModel" type="Csv" classType="org.camelcookbook.transformation.csv.model.BookModel"/>
</dataFormats>
<route>
<from uri="direct:unmarshal"/>
<!-- <unmarshal ref="bookModel"/> -->
<to uri="dataformat:bindy:unmarshal?ref=bookModel"/>
</route>
Instead of unmarshal tag , I need to pass exchange to the equivalent end-point but getting error "Cannot find dataformat with name bindy"
There are multiple bindy data formats: csv, fixed length, key-value pair.
Their respective data format names to use with the "dataformat:" endpoint uri are:
bindy-csv,
bindy-fixed,
bindy-kvp
So in your case you should specify unmarshalling like so:
<to uri="dataformat:bindy-csv:unmarshal?ref=bookModel"/>

Failed to parse server's response: Unknown type: nil

Im trying to make a camel route that would get a xml-rpc data from a 3rd party web site. Problem is that some values are NIL. Is there some way to configure route to accept such values?
site: https://i.ifne.eu/om3-dev/ca/xml-rpc/get-upcoming-events-compact
In a worst case scenario i could pre-process it and replace all NIL`s, but i would like to know if there is not some simple solution
EDIT: adding example route
<route>
<from uri="timer://mainTimer?period=5000"/>
<to uri="xmlrpc:https://i.ifne.eu/om3-dev/ca/xml-rpc/get-upcoming-events-compact?defaultMethodName=test" />
<to uri="mock:result" />
</route>
Also I tried to play with XmlRpcClientConfigurer but had no luck there - im a java noob so no idea if im doing it right
public class xmlrpcConfigurer implements XmlRpcClientConfigurer {
#Override
public void configureXmlRpcClient(XmlRpcClient client) {
XmlRpcClientConfigImpl clientConfig = (XmlRpcClientConfigImpl)client.getClientConfig();
clientConfig.setEnabledForExtensions(true);
}
}
Idea behind that was that acording to http://en.wikipedia.org/wiki/XML-RPC nil tag is an extension I dont really know if it does what i think it does...
Well i was not able to find a solution so i have to solve it another way - im removing the nil elements
<dataFormats>
<xmlrpc id="xmlrpcResponse" request="false"/>
</dataFormats>
<route>
<from uri="timer://mainTimer?period=60000"/>
<to uri="https://i.ifne.eu/om3-dev/ca/xml-rpc/get-upcoming-events-compact?defaultMethodName=test" />
<process ref="removeNilFromXml"/>
<unmarshal ref="xmlrpcResponse"/>
<to uri="mock:result" />
</route>
removeNilFromXml.java:
public class removeNilFromXml implements Processor {
public void process(Exchange exchange) throws Exception {
String data = exchange.getIn().getBody(String.class);
String nonildata = data.replace("<nil/>", "");
exchange.getIn().setBody(nonildata);
}
}
Not the best solution but it works...

how to split an object up in camel routebuilder

Hi there I'm pretty new to Java, camel, etc. Here's my problem:
I have code that passes an order containing order items and some other info in xml format from one camel Processor to another. This particular Processor then splits up the order and creates multiple orders, and then passes them all on to the next endpoint as separate messages.
Currently this Processor uses ProducerTemplate to explicitly accomplish this. I would like to move this behaviour to RouteBuilder itself and not use ProducerTemplate. I've looked at Splitter and MessageTranslator, but I don't have all the pieces yet I think.
So basically I want to split the message in the RouteBuilder using Splitter, but I want to supply custom code that will take the message, then deserialize it into an Order object, then create multiple Order objects, and then send them all as separate messages to the next endpoint. How do I accomplish this?
e.g. I want to be able to do something like
from("startPoint").split(MyCustomStrategy).to("endPoint")
//where MyCustomStrategy will take the message,
//and split it up and pass all the newly created messages to endPoint.
You can have a bean or processor in your route which creates the Order objects and returns them as a collection (e.g., List<Order> or similar). Splitter EIP can then be used to process each Order in that collection, one at a time by e.g. passing each order to another processor/bean that handles a single order, possibly continuing on to another endpoint as needed, etc.
// Pseudocode:
from(input).
to(bean-which-returns-a-collection-of-orders).
split(on-the-body).
to(bean-which-processes-a-single-order).
to(anywhere-else-needed-for-your-purposes).
// etc...
Or something along those lines; sorry, I use Spring DSL not Java DSL but camel docs show both. Here's some actual spring DSL code where a collection is being split to process each item in the collection:
<split>
<simple>${body}</simple>
<doTry>
<log message="A.a1 -- splitting batches for transfer" loggingLevel="DEBUG" />
<setHeader headerName="currentBatchNumber">
<simple>${body.batchNumber}</simple>
</setHeader>
<setHeader headerName="CamelFileName">
<simple>${body.batchNumber}.xml</simple>
</setHeader>
<log message="A.a2 -- marshalling a single batch to XML" loggingLevel="DEBUG" />
<marshal>
<jaxb prettyPrint="true" contextPath="generated.gov.nmcourts.ecitation"
partClass="generated.gov.nmcourts.ecitation.NMCitationEFileBatch"
partNamespace="EFileBatch" />
</marshal>
<log message="A.a3 -- xslt transform to add schema location" loggingLevel="DEBUG" />
<to uri="{{addSchemaLocationXsltUri}}"/>
<log message="A.a4 -- ftp now initiating" loggingLevel="DEBUG" />
<log message="ftping $simple{in.header.CamelFileName}" loggingLevel="DEBUG"/>
<bean ref="markFtpStatusOnTickets"/>
<to uri="{{ftpOdysseyInputPath}}"/>
<log message="A.a5 -- ftp now complete" loggingLevel="DEBUG" />
<doCatch>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean ref="ticketExceptionHandler" method="handleException"/>
</doCatch>
</doTry>
</split>

How can I merge the response from an external service call with the original message in camel

From a logical perspective this is the kind of routing behaviour I wish to implement:
I want to be able to merge the response of an external service with the original request.
I have been able to implement this using multicasting, an aggregator, and a mock endpoint but I was wondering if there is a cleaner way. My current implementation looks like this:
<multicast strategyRef="serviceAggregator" stopOnException="false">
<to uri="mock:foo" />
<to uri="http://0.0.0.0:9999/service/?throwExceptionOnFailure=false" />
</multicast>
<camel:to uri="log:uk.co.company.aggregated?showAll=true" />
<to uri="http://0.0.0.0:9999/anotherService/
The part I particularly don't like is using a mock endpoint but I also don't think that this is a very readable way to express the above diagram. So I was wondering if there was a more elegant way of doing this?
I suggest to read about the EIP patterns, for example the content enricher
http://camel.apache.org/content-enricher.html
Where you can merge the reply message with the request message.
Mind the Content Enricher has 2 modes
- enrich
- pollEnrich
Make sure to notice the difference, from the docs in the link above.
<route>
<from uri="...">
<enrich uri="http://0.0.0.0:9999/service/?throwExceptionOnFailure=false" strategyRef="serviceAggregator"/>
<to uri="log:uk.co.company.aggregated?showAll=true" />
<to uri="http://0.0.0.0:9999/anotherService/>
...
</route>
And yes you diagram is showing splitter, but the sample code is using multicast EIP.
You could simply store the original message in a header or property and later do some merge in a bean. Using the header and the current body.
.setHeader("orig", body())
.to("externalService")
.bean(new MyMergeBean())

accessing initial exchange message in camel

Hi guys I have the following situation when I need to have "composite" process for the XML request. This is snippet from my spring route definition:
<route id="request1">
<from uri="activemq:request1" />
<unmarshal ref="integrationServerJaxb" />
<bean ref="createTINValidationMessage" />
<bean ref="switchComponent" />
<bean ref="createCreateTINMessage" />
<bean ref="switchComponent" />
<bean ref="createResponse1" />
<marshal ref="integrationServerJaxb" />
</route>
Basically what I want to achieve is:
grab a message from request1 message queue
create TIN VALIDATION message from it
send TIN VALIDATION message to the switch
wait for the switch response
with the switch response from step 4 AND initial request 1 XML message, create CREATE TIN message
send message (from step 5) to the switch
create response XML based on response from switch (from step 6)
So in other words, I need to get hold of initial message during processing. In the CreateCreateTINMessage bean I can change the method parameter to Message type, then it will be mapped to the actual Camel Message (from the previous step though), not the very first message in the exchange. Inspecting the message's exchange, I could trace the initial JMS message with request1 xml payload but getting that deep does not seem right. My question is, is it possible to somehow perform integration with intermediary result and initial message from the route? Thank you.
sure, just preserve it explicitly in an exchange property (or message header)...
from(...)
.setProperty("ORIG_EXCH",body())
...
.process(new Processor() {
public void process(Exchange exch) throws Exception {
Object orig = exch.getProperty("ORIG_EXCH");
...
}
})
...
or you can use this API to retrieve it from your route...
exchange.getUnitOfWork().getOriginalInMessage();

Categories