MessageListener Generic type ignored (String instead of S3EventNotification)
Type: Bug
Component:
Describe the bug Here my configuration:
@Configuration
@EnableConfigurationProperties({ AppProperties.class })
@RequiredArgsConstructor
@Slf4j
public class AppConfiguration {
private final AppProperties appProperties;
@Bean
MessageListenerContainer<S3EventNotification> listenerContainer(SqsAsyncClient sqsAsyncClient,
SQSS3CreatedNotificationService sqsS3CreatedNotificationService) {
log.atInfo()
.setMessage("Creating S3 created notification listener container with queue: {}")
.addArgument(appProperties.getAws().getS3CreatedNotificationQueueName())
.log();
return SqsMessageListenerContainer
.<S3EventNotification>builder()
.configure(options -> options
.maxConcurrentMessages(30)
.maxMessagesPerPoll(15)
.pollTimeout(Duration.ofSeconds(10)))
.sqsAsyncClient(sqsAsyncClient)
.messageListener(sqsS3CreatedNotificationService::onMessage)
.queueNames(appProperties.getAws().getS3CreatedNotificationQueueName())
.build();
}
}
As you can see, I'm getting generic to S3EventNotification class from com.amazonaws.services.s3.event.S3EventNotification.
My message listener:
@RequiredArgsConstructor
@Slf4j
@Service
public class SQSS3CreatedNotificationService implements MessageListener<S3EventNotification> {
@Override
public void onMessage(Message<S3EventNotification> message) {
S3EventNotification payload = message.getPayload();
log.info(payload.toJson());
}
}
When S3EventNotification payload = message.getPayload(); code is reached, I'm getting an:
ClassCastException: class java.lang.String cannot be cast to class com.amazonaws.services.s3.event.S3EventNotification
I'm been debugging and I see that message payload is of type string.
Id I change previous MessageListener::onMessage code to:
@Override
public void onMessage(Message<S3EventNotification> message) {
Object payload = message.getPayload();
log.info(payload.toString());
}
Then it all works and logs are printed:
SQSS3CreatedNotificationService : {"Records": [{"eventVersion": "2.1", "eventSource": "aws:s3", "awsRegion": "us-east-1", "eventTime": "2024-02-26T14:31:36.901Z", "eventName": "ObjectCreated:Put", "userIdentity": {"principalId": "AIDAJDPLRKLG7UEXAMPLE"}, "requestParameters": {"sourceIPAddress": "127.0.0.1"}, "responseElements": {"x-amz-request-id": "958bc71b", "x-amz-id-2": "eftixk72aD6Ap51TnqcoF8eFidJG9Z/2"}, "s3": {"s3SchemaVersion": "1.0", "configurationId": "04c4dc8c", "bucket": {"name": "espaidoc", "ownerIdentity": {"principalId": "A3NL1KOZZKExample"}, "arn": "arn:aws:s3:::espaidoc"}, "object": {"key": "5622b70b-aebe-4e7b-b1c4-0959f38e6a34", "sequencer": "0055AED6DCD90281E5", "size": 2333, "eTag": "1d11469e9d81f07729548d7708bbab82"}}}]}
I don't quite figure out what I'm doing wrong.
Any ideas?
Hey @jeusdi,
The issue there is, since you're instantiating the Container manually, the framework doesn't use the conventional Spring handler method conversion you get when using @SqsListener
If you want to have the message payload deserialized in your listener, you can configure the MessageConverter to do so, but you'll need to inform the class the payload should be deserialized to.
There are many ways to do that - you can for example examine the Message's headers, or simply hardcode it in the corresponding function.
Please refer to the docs for more information on message conversion, or feel free to ask if you have any further questions.
Thanks.
Closing due to lack of feedback, we can reopen if necessary.