Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using @JmsListener with multiple payload types for the same destination?

I can write instances of multiple types to a given destination, e.g.:

JmsTemplate template = ...
Alpha alpha = new Alpha(...);
Beta beta = new Beta(...);

template.convertAndSend("my-destination", alpha);
template.convertAndSend("my-destination", beta);

I thought I'd then be able to provide multiple @JmsListener annotated methods to receive the different message payloads - like so:

@JmsListener(destination = "my-destination")
public void receiveAlpha(Alpha alpha) { ... }

@JmsListener(destination = "my-destination")
public void receiveBeta(Beta beta) { ... }

But if I try this I get exceptions in PayloadArgumentResolver.resolveArgument as it can't distinguish that Beta values should be sent to one method and Alpha values to another.

How should I be handling this? As I encode the payload type as a message property I can specify a selector to get things to work like so:

@JmsListener(destination = "my-destination", selector = "_type = 'Alpha'")
public void receiveAlpha(Alpha alpha) { ... }

@JmsListener(destination = "my-destination", selector = "_type = 'Beta'")
public void receiveBeta(Beta beta) { ... }

But as Spring is doing all kinds of cleverness behind the scenes I'm surprised I have to help it out explicitly like this.

Is there a more implicit / better way to do this?

like image 216
George Hawkins Avatar asked Mar 07 '17 19:03

George Hawkins


2 Answers

JmsListener doesn't support method routing - each annotated method gets its own listener container.

The only other alternative is to use ...(Object object) and downcast yourself.

like image 102
Gary Russell Avatar answered Oct 29 '22 17:10

Gary Russell


You could set the JmsTemplate's message converter to set the message's Java type into the message's "type" property, and then in the JmsListener, configure the selector for receiving only the desired message type.

Below is an example in Kotlin, and Java should be the same

The default message convert, annotates the message type into "type" property:

@Bean // Serialize message content to json using TextMessage
fun jacksonJmsMessageConverter(): MessageConverter {
    val converter = MappingJackson2MessageConverter()

    converter.setTargetType(MessageType.TEXT)
    converter.setTypeIdPropertyName("type")
    return converter
}

With the annotating message converter, JMS listener could specify message type in selector:

// Uses default JmsListenerContainerFactory
@JmsListener(destination = "mailbox", containerFactory = "myFactory", selector="type='com.garnetsoft.sf.service.queue.CopyFolderJob'")
fun receiveMessage(job: CopyFolderJob) {
    println("Received copy job <$job>")
}

@JmsListener(destination = "mailbox", containerFactory = "myFactory", selector="type='com.garnetsoft.sf.service.queue.CutFolderJob'")
fun receiveMessage(job: CutFolderJob) {
    println("Received cut job <$job>")
}

Hope this helps.

like image 32
Houcheng Avatar answered Oct 29 '22 15:10

Houcheng