14

I'm using spring-cloud-aws's SqsListener to receive AWS SNS HTTP Notifications in JSON Format from AWS's Simple Queue Service (SQS).

This is the code for the listener:

@SqsListener(value = "my-queue", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void handle(final MyObject obj) throws Exception {
// ...
}

The documentation linked above is only about sending and reading plain serialized objects to the Queue and I thought that receiving SNS messages is expected to work out of the box. But I end up receiving conversion errors:

10:45:51.480 [simpleMessageListenerContainer-2] ERROR o.s.c.a.m.l.SimpleMessageListenerContainer - Exception encountered while processing message. org.springframework.messaging.MessagingException: An exception occurred while invoking the handler method; nested exception is org.springframework.messaging.converter.MessageConversionException: No converter found to convert to class com.myproject.model.MyObject, message=GenericMessage

I also tried creating a wrapper object that looks like the expected SNS Json Format linked above, but I keep getting the same exception. The only type that works is a String in the signature. Shouldn't the SNS be converted automatically?

msp
  • 3,272
  • 7
  • 37
  • 49

2 Answers2

9

Yes it should. And it does actually.

In order to have the correct HandlerMethodArgumentResolver invoked (in this case NotificationMessageArgumentResolver) on deserialization, which in turn invokes the correct converter NotificationRequestConverter you simply need to add the annotation org.springframework.cloud.aws.messaging.config.annotation.NotificationMessage to your method signature. E.g.

@SqsListener(value = "my-queue", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void handle(final @NotificationMessage MyObject obj) throws Exception {
// ...
}

This way the Message part of your SNS gets extracted and converted to MyObject.

msp
  • 3,272
  • 7
  • 37
  • 49
  • How are you able to receive objects as strings from SQS? Isn't it true that SQS only supports strings as messages? – teuber789 Sep 26 '17 at 20:12
  • @jtcotton63 it is true. The payload comes as `String` - a JSON object - the `@NotificationMessage` invokes a converter that converts the payload to an object of the desired class. – msp Sep 27 '17 at 13:45
  • 4
    Adding to this answer, for me simply adding @NotificationMessage annotation did not help, as I was overriding QueueMessageHandlerFactory with my own ArgumentResolver. This would be the case for many, as this is necessary in order to customize the Jackson mapper. In such a case, one must change the resolver so: from `factory.setArgumentResolvers(List.of(new PayloadArgumentResolver(jacksonMessageConverter)));` to `factory.setArgumentResolvers(List.of(new NotificationMessageArgumentResolver(jacksonMessageConverter)));` – Yonatan Wilkof Dec 17 '19 at 17:56
4

This works without the @NotificationMessage as well. This way you don't need to send the "Type" and "Message" part, that is required to work with this annotation.

First create a class with the needed attributes.

public class SqsMessage {

   private String myTask;

   public SqsMessage() {
   }

   public SqsMessage(@JsonProperty("MyTask") String myTask ) {
       this.myTask = myTask ;
   }

   //Getter + Setter 
}

Next set up the Listener

@SqsListener(value = {"MyQueue"}, deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void receiveMessage(SqsMessage payload, @Headers Map<String, Object> header) {
   logger.info("Got message with task: " + payload.getTask() 
    + " with custom attribute " + header.get("CustomAttribute").toString());
}

Now you can send a JSON like

{"MyTask":"My task"}

The @JsonProperty("MyTask") annoation in the POJO's constructor can be optional, depending on your spring version and if your attribute has the same name as in the Json string. It's not necessary for example, if your attribute is called task and your Json string is {"task":"My task"}.

Chris
  • 5,109
  • 3
  • 19
  • 40
  • But the question is about receiving SNS messages where `Type` and `Message` are part of [the format](https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html) – msp Mar 26 '20 at 14:00
  • It's some time ago, but as far as I remember, the ```Message``` is mapped to the POJO and the ```Type``` info is part of the headers. My answer does exactly the same as the accepted answer, but without using ```@NotificationMessage``` annotation, which in my case caused other parsing problems. The JSON stuff was just a bonus. – Chris Mar 26 '20 at 17:43