micronaut-projects / micronaut-jms

Integration between Micronaut and JMS
Apache License 2.0
15 stars 14 forks source link

SQS does not support message selector, but micronaut defaults to empty string #439

Closed samu-developments closed 10 months ago

samu-developments commented 1 year ago

Expected Behavior

Consuming from SQS queue should work.

Actual Behaviour

@Queue annotation defaults messageSelector to "" https://github.com/micronaut-projects/micronaut-jms/blob/9c77b572519bb463b5b409fb8ef556f46a23547f/jms-core/src/main/java/io/micronaut/jms/annotations/Queue.java#L134

AWS sdk is not happy about non null message selector:

if (messageSelector != null) {
            throw new JMSException("SQSSession does not support MessageSelector. This should be null.");

https://github.com/awslabs/amazon-sqs-java-messaging-lib/blob/4cb91355cb92d9361a2179233c9db89383b1299e/src/main/java/com/amazon/sqs/javamessaging/SQSSession.java#L572

AWS sdk is likely not adding this in the foreseeable future.. https://github.com/awslabs/amazon-sqs-java-messaging-lib/issues/4

Steps To Reproduce

import io.micronaut.jms.annotations.JMSListener
import io.micronaut.jms.annotations.Queue
import io.micronaut.jms.sqs.configuration.SqsConfiguration.CONNECTION_FACTORY_BEAN_NAME
import io.micronaut.messaging.annotation.MessageBody

@JMSListener(CONNECTION_FACTORY_BEAN_NAME)
class TestConsumer {
    @Queue(value = "test_queue")
    fun receive(@MessageBody test: String) = println("received : $test")
}

Environment Information

No response

Example Application

No response

Version

core: 4.1.8, jms: 3.0.1

samu-developments commented 1 year ago

Example application: https://github.com/samu-developments/sqs-mn-issue Created from Intellij with Micronaut project with SQS and JMS dependencies selected.

To test it you need a SQS queue in AWS named "test_queue", and run the application with valid AWS credentials so the application can talk to SQS.

Stacktrace:

09:48:08.618 [main] ERROR i.m.j.c.JMSQueueListenerMethodProcessor - Failed to register listener for destination test_queue
javax.jms.JMSException: SQSSession does not support MessageSelector. This should be null.
        at com.amazon.sqs.javamessaging.SQSSession.createConsumer(SQSSession.java:600)
        at io.micronaut.jms.listener.JMSListener.start(JMSListener.java:134)
        at io.micronaut.jms.listener.JMSListenerRegistry.register(JMSListenerRegistry.java:68)
        at io.micronaut.jms.listener.JMSListenerRegistry.register(JMSListenerRegistry.java:115)
        at io.micronaut.jms.configuration.AbstractJMSListenerMethodProcessor.registerListener(AbstractJMSListenerMethodProcessor.java:168)
        at io.micronaut.jms.configuration.AbstractJMSListenerMethodProcessor.process(AbstractJMSListenerMethodProcessor.java:93)
        at io.micronaut.context.DefaultBeanContext.lambda$initializeContext$32(DefaultBeanContext.java:2044)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
        at java.base/java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1707)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
        at io.micronaut.context.DefaultBeanContext.initializeContext(DefaultBeanContext.java:2018)
        at io.micronaut.context.DefaultApplicationContext.initializeContext(DefaultApplicationContext.java:290)
        at io.micronaut.context.DefaultBeanContext.readAllBeanDefinitionClasses(DefaultBeanContext.java:3337)
        at io.micronaut.context.DefaultBeanContext.finalizeConfiguration(DefaultBeanContext.java:3690)
        at io.micronaut.context.DefaultBeanContext.start(DefaultBeanContext.java:345)
        at io.micronaut.context.DefaultApplicationContext.start(DefaultApplicationContext.java:198)
        at io.micronaut.runtime.Micronaut.start(Micronaut.java:73)
        at io.micronaut.runtime.Micronaut.run(Micronaut.java:322)
        at io.micronaut.runtime.Micronaut.run(Micronaut.java:297)
        at com.example.ApplicationKt.main(Application.kt:6)
sreejithvs1 commented 1 year ago

When could we expect a fix for this ? I cant even pass null for the value in annotation

liefra commented 1 year ago

@samu-developments : Facing the same issue. Are you maybe aware of any workaround for now?

samu-developments commented 1 year ago

@samu-developments : Facing the same issue. Are you maybe aware of any workaround for now?

Yes, I just built the project with the fix from https://github.com/micronaut-projects/micronaut-jms/pull/440 and copy the jms-core jar to projectRoot/libs folder. Then in build.gradle.kts:

implementation(files("libs/micronaut-jms-core-3.0.2-SNAPSHOT.jar"))
liefra commented 1 year ago

Nice. Thank you for sharing this workaround.

cdome commented 11 months ago

The problem here seems to be ksp annotation processor. It works properly with kapt (so - you can use it as a workaround).

The thing is that when MN registers annotation default values in DefaultAnnotationMetadata.registerAnnotationDefaults(), these default values come from the annotation processor. Kapt returns null for a default value of empty string, while Ksp returns empty string as default value. Thus then - under Kapt, null gets propagated to SQSSession.createConsumer and it works and under Ksp it's empty string which gets there and it doesnt work because AWS implementation thinks the messageSelector is defined...

This unfortunately means, that it might be a wider issue, as it affects all "" defaults :-/