I am new to Apache camel. I am trying to create routes to call multiple rest APIs and aggregate the response into one.
But for some reason, the JSON request that I am creating does not reach the rest endpoint.
During debug, I see that Exchange objects do have the values that I have set and get converted into byte array and on the other side, rest API recieves empty objects.
I am working on a Spring boot project and I have tried different ways of marshalling the request to JSON including Gson and Jackson. None of which seem to work.
Please assist.
from("direct:oneResponse")
.multicast(new MyAggregationStrategy()).parallelProcessing()
.to("direct:rest1call", "direct:rest2call")
.end();
from("direct:rest1call")
.routeId("rest1call")
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody(<<valid json>>); //json values as required for the rest call.
}
})
.to("http4://localhost:5555/mock/rest1call")
.setProperty("route", simple("routeId"))
.unmarshal(new JacksonDataFormat(Rest1Response.class));
from("direct:rest2call")
.routeId("rest2call")
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody(<<valid json>>); //json values as required for the rest call.
}
})
.to("http4://localhost:5555/mock/rest2call")
.setProperty("route", simple("routeId"))
.unmarshal(new JacksonDataFormat(Rest2Response.class));
Can you try to create a processor and specify all the headers and body in it?
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
exchange.getOut().setHeader(Exchange.HTTP_METHOD, HttpMethod.POST);
exchange.getOut().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getOut().setHeader("Accept", "application/json");
/* this is one way, string representation of json, but maybe you can try to build Model and fill that model with data */
exchange.getIn().setBody(<<valid json>>); //json values as required for the rest call.
}
})
if you decide to go with models, use marshaling after processor just to be sure your data is converted to JSON.
.marshal(yourDataFormat)
Try GsonDataFormat it works pretty good for me.
Related
In my Springboot Camel application I'm using the Kafka integration to produce message on a topic.
I compared message produced by Camel Kafka integration with message produced by Kafka Spring integration and I noticed one little difference:
in OffsetExplorer, messages produced by Spring Kafka uses an Header "spring_json_header_types" with the class of all other headers, and header Long or Integer are correctly showed and serialized.
With Camel instead, if I set an header not String, in OffsetExplorer it is represented with the square char.
I did my research and found that Spring Kafka uses the DefaultKafkaHeaderMapper to create this header, and this help the inbound process to know how to deserialize the other header.
How can I use it in my Camel Kafka Producer?
Here my code:
#Component
public class KafkaProducerRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:myTimer?period=10000")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Map<String, Object> headers = new HashMap<>();
headers.put("TIMER_COUNTER_1", exchange.getProperty(Exchange.TIMER_COUNTER, Long.class));
headers.put("TIMER_COUNTER_2", String.valueOf(exchange.getProperty(Exchange.TIMER_COUNTER, Long.class)));
exchange.getIn().setHeaders(headers);
}
})
.to("kafka:topic1?brokers=localhost:9092");
}
}
I am trying to use the Apache Camel aws2 DyanamoDB component. In that there is a operation DescribeTable. I was trying that out.I have a camel rest API like so ->
.post("dynamodb-describe-table")
.route()
.process(new Processor(){
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setHeader("CamelAwsDdbTableName", "user");
}
})
.toD("aws2-ddb://user?accessKey=insert&secretKey=insert®ion=us-east-1&operation=DescribeTable")
.endRest();
This operation is run successfully but the response is null. Why is this happening?
Operation DescribeTable does not return body. All attributes are returned in form of Message headers.
All headers returned by this operation are listed in AWS DynamoDB documentation.
You have many options to create body, eg. with MVEL:
.transform().mvel("{" +
"'tableSize': exchange.in.headers.CamelAwsDdbTableSize," +
"'status': 'exchange.in.headers.CamelAwsDdbTableStatus'" +
"}")
Or Processor:
.process( exchange ->
exchange.getIn().setBody(
new HashMap<String, Object>(){{
put("tableSize", exchange.getMessage().getHeader("CamelAwsDdbTableSize"));
put("status", exchange.getMessage().getHeader("CamelAwsDdbTableStatus"));
// ...
}}
)
)
Between your toD() and endRest().
BTW I don't see any dynamic part in your URI, you should be able to use just to(), which i generally faster.
I have code like this:
.from("file://" + FTP_FILES + "?idempotent=true")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
list = parseDataFromExchange(ecxhange);
}
I want to send this data to another pipeline vm:myEndpoint
I believe that Camel can this but google didn't help me.
How can I achieve this ?
Just use a ProducerTemplate from inside your Processor, then you can send any message to any Camel endpoint. You can find more information on the Camel website such as: http://camel.apache.org/producertemplate.html
When sending a SOAP request through Spring's WebServiceTemplate, I would like to provide my payload and perform operation on both the request and the response of it. This is because I need some details from the headers of the request/response.
In the Spring documentation I have found it's possible to alter the request with a WebServiceMessageCallback and the response with a WebServiceMessageExtractor.
The problem I'm having is that the WebServiceTemplate seems to choose between providing a payload and providing MessageCallback/MessageExtractor.
With that, I mean there are the following methods available:
marshalSendAndReceive(Object requestPayload, WebServiceMessageCallback requestCallback)
sendAndReceive(WebServiceMessageCallback requestCallback, WebServiceMessageExtractor<T> responseExtractor)
sendAndReceive(WebServiceMessageCallback requestCallback, WebServiceMessageCallback responseCallback)
But nothing to provide all three. So providing the payload, a WebServiceMessageCallback for operations on the request and a WebServiceMessageCallback/WebServiceMessageExtractor for operations on the response.
In the documentation they do provide the following snippet:
public void marshalWithSoapActionHeader(final Source s) {
final Transformer transformer = transformerFactory.newTransformer();
webServiceTemplate.sendAndReceive(new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
transformer.transform(s, message.getPayloadResult());
},
new WebServiceMessageExtractor() {
public Object extractData(WebServiceMessage message) throws IOException
// do your own transforms with message.getPayloadResult()
// or message.getPayloadSource()
}
});
}
But passing your payload into an innerclass just to pass in a payload doesn't seem like clean code.
It doesn't really seem logical that you can provide a payload and callback for tampering the request, but not the response. Or tampering the request and response without providing a payload. How would I go about if I'd like to send a payload and access both the request and response?
I have a Camel route with an onCompletion() which then hits a Processor. Within this processor it gets a header from the Exchange but this header comes back null.
I know that onCompletion() runs at the end of that particular route but surely the Exchange headers should still be valid and usable. inputLocation below is defined higher up in the class and works for previous routes.
from("file://"+inputLocation+"?initialDelay=5000&delay=2000&recursive=true&delete=true")
.onCompletion()
.process(storedProcProcessor())
.end()
.choice()
.when(appContext.getBean(AppPredicate.class))
.log("Need to check against APP in the database for destination.")
.setHeader(AppConstants.INPUTLOCATION, simple(inputLocation))
.process(databaseProcessor())
.endChoice();
I checked with:
#Override
public void configure() {
from("direct:start")
.onCompletion()
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
LOG.info("Hello, {}", exchange.getIn().getHeader("myHeader"));
}
})
.end()
.setHeader("myHeader").constant("World!");
}
}
This prints
Hello, World!
Thus, the header myHeader is still available in onCompletion. So, I guess that your header is never properly set?