Using passed in Value for @JmsListener's destination paramter
Asked Answered
R

1

6

Is there a specific way to accomplish this? I tried to find a solution on here but couldn't find what I need. I have a Spring Boot application that will be accepting multiple arguments from the command line. The argument in question is the queue name (i.e. the destination). It can be one of several of our many queues. The JmsListener is in the form

@JmsListener(destination="dest_goes_here")
public void processOrder(Message message){. . .}

I have a class that basically looks like this

 public class Arguments {
 private static queue
 private static antoherArg
 :
 :
getters and setters
 }

And what I would like to say is destination = Arguments.getQueue(), but it seems destination can only be a static final variable? I assume this because the error presents a little tooltip that alludes to that.

I also tested it, as I have yet another class called Constants, that obvioulsy contains constants, and if I hard code the queue name as public static final String QUEUE = "MyQ"; then say destination = Constants.QUEUE it is ok with that.

So then I assumed I could do something like this in my listener class private static final String QUEUE = Arguments.getQueue(); But it doesn't like that either. Alas, I am stumped.

So really two questions here if anyone is willing to knowledge share. Why is the @JmsListener ok with having destination set to my second solution, but not the first and the last?

And then the main question (that I'd prefer you answer over the first) is, what strategies can I make use of to set destination to a variable that originates from the command line (i.e. be dynamic)?

Edit: To clarify, I cannot keep the value in my Constants class, as the value will be coming from the command line and needs to be passed to the JmsListener class to be used as the destination.

Rifkin answered 10/8, 2016 at 0:3 Comment(0)
M
11

That's how Java works, destination must be a compile-time constant expression and a function invocation isn't considered one. Take a look at the official language specification for more details. EDIT: you can also look at this answer.

As far as your second (and more important) question goes, I have several suggestions for you.

First, you can read the queue name from a configuration property, like so: destination="${jms.queue.name1}" where jms.queue.name1 is your configuration property. Then, since you are using Spring Boot, you can use command-line arguments to override your configuration properties (see externalized configuration documentation for more details). That way, you'll be able to specify the queue name at runtime by passing it as a command-line argument like so --jms.queue.name1=foo.

Second, you can use programmatic listener registration, like so:

@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
        endpoint.setId("myJmsEndpoint");
        endpoint.setDestination(Arguments.getQueue());
        endpoint.setMessageListener(message -> {
            // processing
        });
        registrar.registerEndpoint(endpoint);
    }
}
Mushroom answered 10/8, 2016 at 10:4 Comment(12)
Ah, I failed to recognize it was a specification of Java itself and not of Spring. I'll give one of these a go and see how that out turns out, seems very promising.Rifkin
I'd recommend trying it with properties first since it should require minimum effort.Fraze
Had to painfully move some code around from my misunderstanding and set up a PropertySourcesPlaceholderConfigurer bean class, but the overriding the property suggestion worked like a charm. Thanks.Rifkin
But also for learning purposes. With the second suggestion where I setup the configureJmsListeneres method, does my listener then do its "listening" inside the block where you wrote //processing or would the Listener still be attached to the @JmsListener method in the other class?Rifkin
Hmm, Spring Boot should have allowed you to set the default queue name in application.properties. And yeah, the listening will be done within the //processing block, no other classes and @JmsListener`s needed.Fraze
I'm not actually using an application.properties, Using an application.yml with active profiles that determines what URL for what environment to retrieve properties from. Am still able to overwrite in the same way though :)Rifkin
It makes no difference if it's .properties or .yml, my point was that you already had a default placeholder configurer and there wasn't a need to declare a new one.Fraze
I'm not sure I follow. The properties that I need to overwrite are defined in a class annotated with @Configuration and @PropertySource("http://${path}/to/envionrment/${properties} I don't see how it would know about the properties if I didn't declare a new one as a bean. (Sorry for the extended comment, I'm already getting the extended disscussion message from SO)Rifkin
Yeah, I really don't follow now. What has that have to do with specifying a queue name at runtime? You said that you needed to declare a PropertySourcesPlaceholderConfigurer bean in order for the overriding to work, but I was saying that you had no need for that. You could have specified the default within application.yml or not at all since you'll always be overriding it through command-line anyway.Fraze
Well I feel like an idiot, sorry for wasting your time. I willy nilly responded to you without explaining my changes. I still need the destination to have the ability to overridden and be dynamic, it's just that it can also be specified in our props file in our repo. So command line trumps repo definition. Hope that clears it up. I'm in coding haze and forgot how to communicate, sorry about that.Rifkin
Ah, okay, now I get it! No harm done, I was just confused :)Fraze
All correct. I set queue name in application.prop and then override it in kubernetes yml environment and so I can setup different jms channels with same docker image.Allocution

© 2022 - 2024 — McMap. All rights reserved.