Spring JMS runtime connection management

1.8k Views Asked by At

I have an app (Spring Boot based) which use JMS to works with messages, and I need to be able to connect to more than one MQ server (ActiveMQ), to listen for messages. One more problem with that is I need to turn-off some existing connection and add some new to another servers at run-time (let's imagine I have some db that stores URLs of servers and rest endpoint that inform app to reload config (URLs) from db and reconfigure/refresh current connection set).

After digging information about Spring, JMS and DI configuration, I understand that it's not the best way to do what I need (maybe I'm wrong), because DI doesn't fit well for dynamic scenarios like mine.

Sample app flow

App start

  • Configure DB beans
  • Load MQ configurations from DB (list of URLs)
  • (???) For each MQ config: configure ConnectionFactory -> Do connection -> attach listeners/endpoints

App life-cycle

  • Handle REST request
  • Load MQ configurations from DB (list of URLs)
  • (???) For each MQ config:
    • new item: configure new ConnectionFactory -> Do connection -> attach listeners/endpoints
    • deleted item: drop connection
2

There are 2 best solutions below

1
On BEST ANSWER

I have done the same by creating a spring component which based on properties to connect to differents topics on AMQ with different protocols.

I run multiple instances of that component from the main component by creating a new context for each one and injecting different properties on creation like this :

Properties source = new Properties();
PropertiesPropertySource ps = new PropertiesPropertySource("id", source);
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(YourConfig.class);
// I worked with xml context but this is the annotation based context creation
ctx.getEnvironment().getPropertySources().addLast(ps);

You can use config class or set the scan by http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/AnnotationConfigApplicationContext.html#scan-java.lang.String...-

Each context can be closed at runtime by calling close method and recreated because I keeps them in a map in the main component. This component was excluded to not be loaded from main context automatically by setting @ComponentScan of the main context config

1
On

There are 2 ways to achieve it using Spring.

1. Spring profiles - You can maintain different profiles such as MQ1,MQ2 and define beans with JNDI configuration (JndiObjectFactoryBean) for each of the profile. At run time, based on your logic you can switch to a new profile (and new behavior) from existing one by activating new profile and refreshing app context. You can get more details at http://docs.spring.io/autorepo/docs/spring-boot/current/reference/html/boot-features-profiles.html

2. Spring custom scope and JndiObjectFactoryBean - You can define all MQ related beans as JndiObjectFactoryBean with pertinent jndi. These beans are defined using custom scope and then you can add conditional to return appropriate bean in Scope#get() api. You can get more details at http://javapapers.com/spring/custom-scope-for-spring-bean/