spring-cloud-aws icon indicating copy to clipboard operation
spring-cloud-aws copied to clipboard

MessageListener Generic type ignored (String instead of S3EventNotification)

Open jeusdi opened this issue 1 year ago • 1 comments

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?

jeusdi avatar Feb 26 '24 21:02 jeusdi

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.

tomazfernandes avatar Feb 26 '24 23:02 tomazfernandes

Closing due to lack of feedback, we can reopen if necessary.

tomazfernandes avatar Mar 10 '24 19:03 tomazfernandes