Spring 4.1 @JmsListener configuration
Asked Answered
P

3

12

I would like to use the new annotations and features provided in Spring 4.1 for an application that needs a JMS listener.

I've carefully read the notes in the Spring 4.1 JMS improvements post but I continue to miss the relationship between @JmsListener and maybe the DestinationResolver and how I would setup the application to indicate the proper Destination or Endpoint.

Here is the suggested use of @JmsListener

@Component
public class MyService {

    @JmsListener(containerFactory = "myContainerFactory", destination = "myQueue")
    public void processOrder(String data) { ... }
}

Now, I can't use this in my actual code because the "myQueue" needs to be read from a configuration file using Environment.getProperty().

I can setup an appropriate myContainerFactory with a DestinationResolver but mostly, it seems you would just use DynamicDestinationResolver if you don't need JNDI to lookup a queue in an app server and didn't need to do some custom reply logic. I'm simply trying to understand how Spring wants me to indicate the name of the queue in a parameterized fashion using the @JmsListener annotation.

Further down the blog post, I find a reference to this Configurer:

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
    registrar.setDefaultContainerFactory(defaultContainerFactory());

    SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
    endpoint.setDestination("anotherQueue");
    endpoint.setMessageListener(message -> {
        // processing
    });
    registrar.registerEndpoint(endpoint);
}

Now, this makes some amount of sense and I could see where this would allow me to set a Destination at runtime from some external string, but this seems to be in conflict with using @JmsListener as it appears to be overriding the annotation in favor of endpoint.setMessageListener in the code above.

Any tips on how to specify the appropriate queue name using @JmsListener?

Patric answered 17/9, 2014 at 20:38 Comment(10)
By using a placeholder. destination="${name.of.your.property}".Gillespie
I would prefer not to use PropertyPlaceHolderConfiguration in favor of Environment.getProperty but clearly I can't take that route as a parameter to an annotation.Patric
Actually you will use a PropertySourcePlaceHolderConfigurer which basically does the same as Environment.getProperty does (it consults all PropertySources. Could you elaborate why you would prefer the Environment.getProperty over the placeholder in this case?Gillespie
It just seemed like using @PropertySource in @Configuration beans was the latest recommended way to read from configuration. I was trying for a "no XML" approach.Patric
And why would a @PropertySource wouldn't work with a placeholder? It works the same. The only difference is that you need to use a PropertySourcesPlaceHolderConfigurer. So that fact of using a placeholder doesn't mean you cannot use @PropertySource anymore.Gillespie
I wanted to only use @PropertySource and didn't want to have the redundancy of multiple methods for properties. It's not a big deal, just didn't want the additional overhead. I'll just setup the placeholder and use SpEL to parameterize the destination.Patric
It isn't SpEL it is just a placeholder. I don't really see the redundancy in this case but that is probably my lack of vision then.Gillespie
I am +1 with @M.Deinum here and would be interested to understand what the issue is using Placeholder.Hallock
I'm not against using a placeholder, but my configuration is already being set using @Configuration beans with @PropertySource. It's trivial to setup the Placeholder and I don't have a problem doing so, but not only is it redundant it seems like I should be able to set the destination in a way that does not require the destination to be passed in via annotation. What if I wanted to determine the destination dynamically at runtime? Thanks, @Stéphane Nicoll.Patric
Thanks @M.Deinum, would you mind repeating your comment in an answer. I think it deserves to be there. It's not immediately obvious from the documentation that you can do this.Edrei
H
2

You could eventually do that right now but it's a bit convoluted. You can set a custom JmsListenerEndpointRegistry using JmsListenerConfigurer

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        registrar.setEndpointRegistry(customRegistry());
    }

}

and then override the registerListenerContainer method, something like

public void registerListenerContainer(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) {
    // resolve destination according to whatever -> resolvedDestination
    ((AbstractJmsListenerEndpoint)endpoint).setDestination(resolvedDestination);
    super.registerListenerContainer(endpoint, factory);
}

But we could do better. Please watch/vote for SPR-12280

Hallock answered 2/10, 2014 at 9:17 Comment(1)
Has there been any update with this? The ticket appears to have been declined but this is 5 years old nowMerlynmermaid
S
17

Also note that depending on use case you can already parameterize using properties file per environment and PropertySourcesPlaceholderConfigurer

@JmsListener(destinations = "${some.key}")

As per https://jira.spring.io/browse/SPR-12289

Shaneka answered 16/12, 2015 at 23:57 Comment(1)
This is exactly what I was after. Templating is demonstrated for @Value annotations but it wasn't made clear in the docs that it was available more generally. Thanks!Teens
F
5

In case people are using @JmsListener with spring boot, you do not have to configure PropertySourcesPlaceholderConfigurer. It work's out the box

Sample:

class

@JmsListener(destination = "${spring.activemq.queue.name}")
    public void receiveEntityMessage(final TextMessage message) {
    // process stuff 
}
}

application.properties

spring.activemq.queue.name=some.weird.queue.name.that.does.not.exist

Spring boot output

[26-Aug;15:07:53.475]-[INFO ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[931 ]-Successfully refreshed JMS Connection 
[26-Aug;15:07:58.589]-[WARN ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[880 ]-Setup of JMS message listener invoker failed for destination 'some.weird.queue.name.that.does.not.exist' - trying to recover. Cause: User user is not authorized to read from some.weird.queue.name.that.does.not.exist 
[26-Aug;15:07:59.787]-[INFO ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[931 ]-Successfully refreshed JMS Connection 
[26-Aug;15:08:04.881]-[WARN ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[880 ]-Setup of JMS message listener invoker failed for destination 'some.weird.queue.name.that.does.not.exist' - trying to recover. Cause: User user is not authorized to read from some.weird.queue.name.that.does.not.exist 

This proves that @JmsListener is able to pickup property values from application.properties without actually setting up any explicit PropertySourcesPlaceholderConfigurer

I hope this helps!

Felloe answered 26/8, 2016 at 22:11 Comment(0)
H
2

You could eventually do that right now but it's a bit convoluted. You can set a custom JmsListenerEndpointRegistry using JmsListenerConfigurer

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        registrar.setEndpointRegistry(customRegistry());
    }

}

and then override the registerListenerContainer method, something like

public void registerListenerContainer(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) {
    // resolve destination according to whatever -> resolvedDestination
    ((AbstractJmsListenerEndpoint)endpoint).setDestination(resolvedDestination);
    super.registerListenerContainer(endpoint, factory);
}

But we could do better. Please watch/vote for SPR-12280

Hallock answered 2/10, 2014 at 9:17 Comment(1)
Has there been any update with this? The ticket appears to have been declined but this is 5 years old nowMerlynmermaid

© 2022 - 2024 — McMap. All rights reserved.