Get annoted method endpoints for JMS - spring

Is there any way through which I can find all the configured MethodJmsListenerEndpoint's through annotations?
I want to register all these end points with different message listener containers.
#JmsListener(destination = "TestQueue")
public void process(String msg) {
System.out.println(msg);
}
//TODO for all connections
foreach(connections){
//TODO get all annotated endpoints as prototype
foreach(endpoint){
MethodJmsListenerEndpoint processEndpoint = endpoint;
registrar.registerEndpoint(processEndpoint,containerFactory(connection));
}
}

Depends on your provider you can use configuration customizer bean like HornetQConfigurationCustomizer to manipulate any settings during this bean initialization.
If your configuration should be really adoptive and manageable in runtime then you should not use #JmsListener annotation at all. Just register them all in your code like Spring advises: JMS

Related

How to configure request timeouts in Spring Cloud Gateway via code

I need to configure request timeouts in code for all routes. I know global timeouts can be configured via following properties in application.properties, but how can they be configured in code?
spring.cloud.gateway.httpclient.connect-timeout=1000
spring.cloud.gateway.httpclient.response-timeout=5s
I have looked at GatewayAutoConfiguration how timeouts are configured by default. HttpClientProperties holds both properties, however it cannot be overwritten.
#Bean
public HttpClientProperties httpClientProperties() {
return new HttpClientProperties();
}
Can this be done in code?
I solved my problem. I created my own bean and used annotation #Primary to be able to create a separate bean with the same type. GatewayAutoConfiguration now uses my bean instead of the default bean.
#Bean
#Primary
public HttpClientProperties overwrittenHttpClientProperties() {
HttpClientProperties p = new HttpClientProperties();
p.setConnectTimeout(3000);
p.setResponseTimeout(Duration.ofMillis(10000));
return p;
}

Use a custom JMS listener's "containerFactory" on a Camel route

I'd like to receive messages using a Camel route, but having the capability to somehow inject a custom "containerFactory".
Usually (without a Camel route), you'd do something like:
#JmsListener(destination = "${some.virtual-topic.queue}",
containerFactory = "customJmsListenerContainerFactory")
public void receiveMessage(String message) throws Exception {
// do something cool with the received message ...
}
Note how the "containerFactory" property of the "JmsListener" annotation above provides us with a way of using a non default "containerFactory". That works fine, but what if instead we'd like to use a Camel route for reading from the queue? Something like:
#Component
public class TestRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("activemq:queue:{{some.virtual-topic.queue}}")
.bean(MessageFacade.class, "process");
}
}
In this latest case above, I've not been able to "inject" a custom JMS containerFactory. Does anybody knows if this is possible (in a non-hack way)? or if not then we'll have to rely on the standard listener.
See the documentation: https://camel.apache.org/components/latest/activemq-component.html
The options consumerType should be set to Custom and messageListenerContainerFactory should refer to the bean id of your container factory implementation.

Register a custom listener for a custom auto-configuration Spring starter

I wrote a custom spring-boot starter. Among other things this module listens to inbound Rabbit messages.
#RabbitListener(queues = "ladisa.${ladisa.macchina.macchinaName}.command")
public void listenCommand(ComandoStr comando) {
//TODO: call registered methods
}
I wouldn’t like to process incoming messages into Spring starter code but I’d like to delegate the execution to methods belonging to the spring application where my starter in used. I’d like to use a custom annotation.
#CommandEventListerner
public void processCommand(ComandoStr cmd) {
//TODO: code that precess the event
}
Where #CommandEventListerner is my custom annotation to signal my application to use these methods as targets for the execution.
Is it possible instruct Spring to register all methods annotated with i.e. #CommandEventListerner and execute them all inside a method belonging to a custom spring-starter?
There's a spring-boot standard way. In the starter you can autowire the ApplicationEventPublisher
#Autowired
ApplicationEventPublisher publisher;
#RabbitListener(queues = "ladisa.${ladisa.macchina.macchinaName}.command")
public void listenCommand(ComandoStr comando) {
publisher.publishEvent(comando);
}
In the project that uses the starter module, the application can listen to events by registering methoda with the standard annotation #EventListener
#EventListener
public void processCommand(ComandoStr cmd) {
System.out.println(cmd.comando);
}
Spring dispaches events based on the type (in this case ComandoStr).

Is "jmsListenerContainerFactory" the default factory used by SimpleJmsListenerEndpoint

I am working with Spring JMS 4.1 to register messages listeners
In my xml configuration file, I have defined a bean named "jmsListenerContainerFactory":
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory"...p:concurrency="3-5".../>
First question : with Spring 4.x version, isn't it better to declare this factory this way : <jms:listener-container ... />
Second and main question : as stated in official doc (24.6.1) : by default, the infrastructure looks for a bean named jmsListenerContainerFactory as the source for the factory to use to create message listener containers. Is it also the case when programmatically registering endpoints this way:
.
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setDestination("...");
endpoint.setMessageListener(message -> {...});
registrar.registerEndpoint(endpoint);
or do we have to set it explicitly this way for example : registrar.setContainerFactoryBeanName("jmsListenerContainerFactory");
Thanks
I don't understand your first question; in the first case, you are defining a DefaultJmsListenerContainerFactory (a factory that creates listener containers) whereas the XML is creating a listener container directly.
The factory is useful if you need to create lots of containers with similar properties.
For simple container configuration, when you are not using annotated listeners, it certainly might be simpler to use traditional XML, or #Bean definitions for the container.
For the second question, the default registrar is already populated with the container factory bean name when it is passed into the configureListeners method; you don't have to set it.
SimpleJmsListenerEndpoint always looks for named bean "jmsListenerContainerFactory". so even there is no explicitly setting:
registrar.setContainerFactoryBeanName("jmsListenerContainerFactory");
JmsListenerEndpoint still can find the JmsListenerContainerFactory if there exists bean "jmsListenerContainerFactory".
Mean that in case you need to apply JmsListenerContainerFactory with different bean name, then setting on method registrar.setContainerFactoryBeanName("") doesn't effect at all.
Code below for the work and not working cases:
// config factory class
#Bean(name = "customJMSListenerContainerFactory")
public DefaultJmsListenerContainerFactory
listenerQueueFactory() {
DefaultJmsListenerContainerFactory factory = new
DefaultJmsListenerContainerFactory();
//more configs...
return factory;
}
// on consumer class
#Configuration
#EnableJms
public class MyConsumer implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
registrar.registerEndpoint(endpoint);
// this setting will not work => Spring JMS bug
registrar.setContainerFactoryBeanName("customJMSListenerContainerFactory");
// but this setting works
registrar.setContainerFactory(listenerQueueFactory());
}
}
This is bug of spring jms.

Spring 4.1 #JmsListener configuration

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?
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
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!
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

Resources