Spring Cloud - SQS - java

I'm trying to get a simple queue handler working with the Spring Cloud framework. I've successfully got the message handler polling the queue, However. The problem I'm seeing is that when I post a message to the queue, my handler is failing to unmarshall the payload in to the required java Object.
#MessageMapping("MyMessageQueue")
#SuppressWarnings("UnusedDeclaration")
public void handleCreateListingMessage(#Headers Map<String, String> headers, MyMessage message) {
//do something with the MyMessage object
}
The error I'm getting is
No converter found to convert to class MyMessage
As I understand it, the #MessageMapping should use Jackson to unmarshall my JSON payload into a MyMessage object. However its complaining that it cannot find a converter.
Has anyone come across this?
I'm using the 1.0.0.BUILD-SNAPSHOT version of Spring Cloud.

Jackson is only used if a contentType header is set with value application/json on the SQS message. Otherwise the converters do not know what type of content is contained in the message's payload and the right converter cannot be chosen.
If you use QueueMessagingTemplate#convertAndSend as in the reference application, the contentType header will automatically be set.

Related

Spring AMQP: Amqp.outboundAdapter to #RabbitListener, error sending response due to missing "reply-to" property

So,
I have 2 Spring Boot applications (2.6.10) that need to communicate via RabbitMQ / AMQP. App #1 is Spring Integration based and uses
Amqp.outboundAdapter(rabbitTemplate).routingKey("app1-to-2")
to send messages in flow #1
Amqp.inboundAdapter(connectionFactory, "app2-to-1")
to receive messages in flow #2
App #2 is using RabbitListener to receive messages from App #1, process them immediately and return a reply:
#RabbitListener(queues={"app1-to-2"})
public MyResponse handleMessage(final MyRequest request) {
var myResponse = this.process(myRequest);
rabbitTemplate.convertAndSend("app2-to-1",myResponse);
}
However, upon receiving myRequest in App #2, the response cannot be send, due to:
AmqpException: Cannot determine ReplyTo message property value: Request message does not contain reply-to property, and no default response Exchange was set.
Question is: Why is the routing key from convertAndSend ignored or how to set reply-to property with Amqp.outboundAdapter?
Your error has nothing to do with the convertAndSend().
It is the problem of the #RabbitListener method signature.
Since you don't deal with request-reply pattern, your listener method must have a void return type. Otherwise it tries to send that MyResponse object as a reply message body and therefore we see that error trying to resolve ReplyTo from default strategy.

How to deserialize Json string from Kafka topic in spring boot

I created a POJO to map a payload received from a Kafka topic.
It works if the format of payload is:
{"payload":{"name":notification,"key":"2637","message":"This is a notif"}
When I produce this message of JSON string type.
"{\"payload\":{\"name\":\"notification\",\"key\":\"2637\",\"message\":\"This is a notif\"}"
It is throwing
Could not read JSON:Cannot construct instance of myfilename : no sting-argument constructor/factory method to deserialize from string value.
How to fix it?
I think you just need to use a StringDeserializer if the message is just a String representing escaped JSON, and handle the message as a String. I don't think you can automatically bind to a POJO because it is not an actual JSON object.
You could write a custom deserializer to convert the String then delegate to an ObjectMapper.
Your issue possibly could be resolved by using below setting in the mapper :
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
On the side note, if you are already using spring-kafka, you can use the default JsonDesrializer or a custom deserializer.
consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
More documentations are available in the spring documentation
Another ref : Deserialize kafka messages in KafkaConsumer using springboot

Spring Message Converter for xml and json

Can someone please help clarify and show how to properly go about this issue?
What I understand so far is that the Spring framework uses Message Converters when a method is annotated with #ResponseBody to convert the return Java object to a format that can be accepted by the Client. If the clients' HTTP request Accept Header includes "application/json", it will use Jackson and the Jackson converter to convert the object and return it in a json format. Similarly if the Accept Header includes "application/xml", then the Message Converter will use Jaxb and the corresponding converter to convert the object to xml.
Now my issue is that I include both the Jackson and Jaxb libraries as specified in Spring documentation so that the corresponding converters can work. This should be enough for Spring to employ #ResponseBody as its supposed to. However, when I send an HTTP Request with the Accept header "application/xml" I get a 406 status code and when I send one with "application/json" I receive a correct json response.
From my research online, I see that some people use the ContentNegotiation technique to work around this, but I would like to use the Message Converter for now. However, every technique to make the Message Converter technique to respond to json and xml resource requests involve formatting my POJO with JAXB annotation. Is this really necessary?
I guess what I am asking is how would one set up their project properly so that Spring can use the Message Converter technique to respond to json and xml requests? What libraries must be included? Does one need to add JAXB annotations or is there an automatic way for Spring to format an object into xml the way it does for json?
I thank you for your time and help with this, but so far I am really loving Spring's implementation of JAX-RS!

Spring MVC repsonsebody

Follwing the examples from Krams. What does the below return, will the Person object be converted to JSON or XML based on the header of submitted request ?
#RequestMapping(value = "/person",
method = RequestMethod.POST,
headers="Accept=application/xml, application/json")
public #ResponseBody Person addPerson(#RequestBody Person person) {
logger.debug("Provider has received request to add new person");
// Call service to here
return personService.add(person);
}
So when I submit data as json I get json back, and the same for xml. Or is something else going on ?
It depends on Accept http request header. If it's json, you get json, if it's xml you get xml.
That's exactly what this part of your code says.:
....
headers="Accept=application/xml, application/json")
...
You can send one mime type and receive another without problems.
edit
Both headers and produces/consumes parameters only say what can be produced/consumed by the requestmapping. They don't force any particular serialization. The type of request/response is decided entirely in request headers. If the dispatcher doesn't find mapping with produces and consumes (or headers) matching the request headers you'll get an error.
The 'headers', 'produces', and 'consumes' parameters to #RequestMapping are one piece to the puzzle. They help the framework route incoming requests, and differentiate between requests based on values in the header.
The other part to this puzzle is the set of MessageConverters that are configured in the application. The controller handler method in the example just returns an object, and the framework needs to know how to convert the object to some text representation (i.e. XML or JSON). MessageConverters are used on the inbound side (on parameters annotated with #RequestBody) and on the outbound side (when the method return value is annotated with #ResponseBody). The appropriate MessageConverter is chosen by the framework based on the media type of the incoming request.
When using the <mvc:annotation-driven> namespace in a Spring XML config file, or when using the #EnableMvc annotation on a Java Config class, MessageConverters supporting JSON and XML and configured automatically.

Camel Serialize MessageContentsList

I receive a SOAP message to a CXF endpoint, with a Long and String value.
eg. [5, 'test']
The camel route receiving messages is already using dataformat=POJO
I need to send these parameters on ActiveMQ to another application.
If I use:
<convertBodyTo type="java.lang.String"/>
The logs show the body contains 5 only. 'test' is not sent.
I tried converting to a POJO before converting to a String, but I can't find proper documentation on making TypeConverters, (seriously, who can read this and figure out actual code from it?)
eg.
<convertBodyTo type="com.company.InfoPojo"/>
<convertBodyTo type="java.lang.String"/>
If I try to just forward the CXF data to the queue without any converting, I get:
Failed to extract body due to: javax.jms.JMSException: Failed to
build body from content. Serializable class not available to broker.
Reason: java.lang.ClassNotFoundException: Forbidden class
org.apache.cxf.message.MessageContentsList! This class is not allowed
to be serialized. Add package with
'org.apache.activemq.SERIALIZABLE_PACKAGES' system property..
Anyone know what the best option here is?
Thanks
You should marshal the parameters to XML or JSON (or any other format that takes your fancy) before sending them to the queue. The consumer will then need to unmarshal them.
No need to mess around with type converters. Camel's data formats make this really easy: https://github.com/apache/camel/blob/master/components/readme.adoc#data-formats
JSON: https://github.com/apache/camel/blob/master/docs/user-manual/en/json.adoc
JAXB: https://github.com/apache/camel/blob/master/components/camel-jaxb/src/main/docs/jaxb-dataformat.adoc
Made a bean and sent to the bean:
public void process(Exchange exchange) throws Exception {
log.info("Converting CXF values for queue.");
Object[] args = exchange.getIn().getBody(Object[].class);
patientKey = String.valueOf((Long)args[0]);
destinationUrl = (String)args[1];
exchange.getOut().setBody(new String(patientKey + "|" + destinationUrl));
}

Categories