Camel doesn't retrieve SQS messages attributes - apache-camel

Here is the route:
from("aws-sqs://myQueue?accessKey=RAW(xxx)&secretKey=RAW(yyy)&deleteAfterRead=false")
.log("Attributes: ${header.CamelAwsSqsAttributes}")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Map<String, String> messageAttributes = (Map<String, String>) exchange.getIn().getHeader("CamelAwsSqsAttributes");
...
}
});
The .log() shows an empty map as well as if I print messageAttributes from the processor.
I also tried with the header "CamelAwsSqsMessageAttributes" instead of "CamelAwsSqsAttributes" but still nothing.
I see the attributes from the AWS console though.
By the way I get the message body, and I use Camel 2.15

I figured it out, here is an example to get queue attributes and message attributes:
main.bind("sqsAttributeNames", Collections.singletonList("All"));
main.bind("sqsMessageAttributeNames", Collections.singletonList("All"));
Or add those objects to the registry if you don't use org.apache.camel.main.Main
Then:
from("aws-sqs://myQueue?accessKey=RAW(xxx)&secretKey=RAW(yyy)&deleteAfterRead=false&attributeNames=#sqsAttributeNames&messageAttributeNames=#sqsMessageAttributeNames")
Of course you can replace Collections.singletonList("All") with the list of attributes you need if you don't want all of them.

I faced the same issue. When I am using camel-aws 2.16.x and I have my endpoint configured as follow
from("aws-sqs://myQueue?...&messageAttributeNames=#sqsMsgAttributeNames")
.to(...)
Then I have defined a Collection of String in my spring configuration file
#Bean
public Collection<String> sqsMsgAttributeNames() {
return Arrays.asList("Attr1", "Attr2");
}
Above settings work fine but ever since I upgraded to camel-aws 2.17.3. It no longer works. As mentioned in Camel SQS Component, collection of string no longer will be supported for messageAttributeNames and it should be a String with attributes separated by comma.
Note: The string containing attributes should not contain any white
spaces otherwise camel-aws component will only read the first
attribute. I went through the pain to debug on this. Besides, setting the
attribute value to be "All" does not work for me, none of the message
attributes will be read.
Below is the changes I made that allowed camel-aws's SqsConsumer to work again:
#Bean
public String sqsMsgAttributeNames() {
return String.format("%s,%s", "Attr1", "Attr2");
}

It is not an issue of Camel. It can be the default behavior of SQS or aws-java-sdk-core library.
As a quick solution this aws-sqs URL can be used
aws-sqs://myQueue?<other attributes here>&attributeNames=All
Keep in mind that localstack can work well without attributeNames parameter, unlike SQS.

Related

Overriding or Intercepting Camel Logging

An existing application uses Camel logging (bog the "log()" DSL, and also the Log component.
We would like to either intercept or override so that every log message also logs out a specific Header value (e.g. x-correlation-id=ABC-123)
What is a good, idiomatic way to achieve this?
Apache Camel supports pluggable LogListener since version 2.19.0. This is pretty powerful, because its method onLog, which is invoked right before logging, have instances of Exchange, CamelLogger and message. You can customize the message there with almost no limitations.
Implementation of LogListener:
public class MyLogListener implements LogListener {
#Override
public String onLog(Exchange exchange, CamelLogger camelLogger, String message) {
return String.format("%s: %s", exchange.getIn().getHeader(Exchange.CORRELATION_ID), message);
}
}
LogListener registration:
getContext().addLogListener(new MyLogListener());
If you are using Apache Camel version 2.21.0 and newer, you dont need register it to context, because it is looked up in Registry, so annotating MyLogListener as #Bean is enough.

Camel REST (restlet) URL - confusing with path params

I'm using Camel Rest (with restlet component) and I have the following APIs:
rest("/order")
.get("/{orderId}").produces("application/json")
.param().dataType("int").type(RestParamType.path).name("orderId").endParam()
.route()
.bean(OrderService.class, "findOrderById")
.endRest()
.get("/customer").produces("application/json").route()
.bean(OrderService.class, "findOrderByCustomerId")
.endRest()
The problem is that the /order/customer doesn't works (see Exception below). The parameters for /customer comes from JWT...
java.lang.String to the required type: java.lang.Long with value
customer due Illegal characters: customer
I think that camel is confusing the ../{orderId} parameter with .../customer.
If I change the /customer for /customer/orders it's works.
The same idea in Spring Boot could have done with:
#RequestMapping("/order/{orderId}")
public Order getOrder(#PathVariable Long orderId) {
return orderRepo.findOne(orderId);
}
#RequestMapping("/order/customer")
public List<Order> getOrder() {
return orderRepo.listOrderByCustomer(1l);
}
Any idea about what's happening?
Try changing the order of your GET operations in the Camel Rest DSL. The restlet component has some issues in matching the best possible methods.
There is a couple of JIRA tickets related to this:
https://issues.apache.org/jira/browse/CAMEL-12320
https://issues.apache.org/jira/browse/CAMEL-7906

how to set an exchange property globally in apache camel

For example:
from("direct:test")
.multicast()
.to("direct:req1","direct:req2");
from("direct:req1")
.to(cxf:bean:endpoint1)
.process("response1");
from("direct:req2")
.process("requestProcessor2")
.to(cxf:bean:endpoint2)
.process(response2);
I am new to apache camel, i just wanna know is there any way to use the response which i get from the endpoint1 in "requestProcessor2" .
You could do something like this
from("direct:test")
.setProperty("test.body", body())
.to(cxf:bean:endpoint1)
.setProperty("endpoint1.body", body())
.process("response1")
.setBody(exchangeProperty("test.body"))
.to("direct:req2")
from("direct:req2")
.process("requestProcessor2")
.to(cxf:bean:endpoint2)
.process(response2);
You save the original body in an property and also the body from endpoint1. You then send the exchange to direct:req2 with the original body in the exhcnage body and the body form endpoint1 in a property which you then can access (in you processor or else where).
To access the the property in your processor:
public void process(final Exchange exchange) throws Exception {
Object body = exchange.getProperty("endpoint1.body");
}
You question already has the answer , use and you can get the property from the exchange whichever route you want. Also consider removing the property at the final route.

Avoid automatic binding with RabbitMQ and Camel

I'm trying to use RabbitMQ with Camel. I am using Camel 2.14.1.
I want to open an fanout exchange on RabbitMQ and then later bind queues to it. This seems to work fine. However, everytime I create an Exchange, it is automatically bound to queue with a system name (a number). Can't I avoid that?
Here is a simple example which posts 100 messages to an Exchange. But they get delivered to an automatically created queue, I want to avoid this.
#Override
public void configure() throws Exception
{
final String testGUID = "xxxx";
from("timer://publish?repeatCount=100&period=10&fixedRate=true").process(new Processor()
//from("timer://publish?repeatCount=100&period=1&fixedRate=true").process(new Processor()
{
#Override
public void process(Exchange _exchange) throws Exception
{
String message = String.valueOf(_exchange.getProperty(Exchange.TIMER_COUNTER));
_exchange.getOut().setBody(message+testGUID);
}
})
.to("rabbitmq://localhost/exchange=logs1237?autoDelete=false&username=guest&password=guest&exchangeType=fanout");
}
Best regards,
Morten Knudsen
UPDATE:
It seems from looking at the source, that the triggering of the automatic queue happens if "queue" in RabbitMQEndPoint is not null. But "queue" is automatically assigned to "String.valueOf(UUID.randomUUID().toString().hashCode());" at construction.
If you don't want to bind the exchange with queue, you can setup the declare option to be false. BTW, declare option is new added since Camel 2.14.0.
As Bal has already described here add "declare=false" to your RabbitMQ URI. This should solve your problem.
Optionally, you can also use "skipQueueDeclare=true&skipQueueBind=true" this properties in your URI as well.
declare: If the option is true, camel declare the exchange and queue name and bind them together. If the option is false, camel won’t declare the exchange and queue name on the server.
skipQueueDeclare: If true the producer will not declare and bind a queue. This can be used for directing messages via an existing routing key.
skipQueueBind: If true the queue will not be bound to the exchange after declaring it
You can reach out all the properties you can use in Camel for RabbitMQ here.
From Camel 2.16.1 on, there's a new option for the rabbitmq component, skipQueueDeclare, which properly solves this issue.

Apache Camel: can message have multiple objects in body (with different classes)?

I have almost ready application in java that use jms with Camel. Pop up that we I have to add additional infomations in exchange/message. Lets say that those additional infomations are in fact new java object. What is the best way to add my new object to exchange?
I have a lot of Camel processors processing the message that look like this:
public class MyProcessor implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
String s = exchange.getIn().getBody(String.class);
s = magicalTransform(s);
exchange.getIn().setBody(s, String.class);
//Now I have to add object of some Info.cass:
Info info = new Info( new Date() );
//Can I add it like this? :
exchange.getIn().setBody(info, Info.class);
}
}
The problem is that I can't find information if I can add many objects to Message. The Message method: setBody(Object body, Class type) suggest that it is possible, but there is also method: getBody() that sugesst that there is only one body class.
If I can't do it in this way, then what's the best way? I could try to wrap String that I transform and info in to one class, and put that new class in to message, but It will cause change the way obtaining String in every Processor. I want to avoid that.
The body of an Exchange is a single Object. If you want to add multiple objects to the body of your exchange you need to make the body of the exchange a map, list, or pojo with fields that you set all of your objects within.

Resources