Stop a spring jms message listener - spring

I have a scenario where i need to stop the spring's DefaultMessageListenerContainer and then later start that again. I have 10 different DefaultMessageListenerContainer listening to 10 different queue.
All 10 different containers are calling the same method of same message listener class.
Now i want to stop the messagelistenercontainer for a particular queue depending on the exception i get in onMessage method.
Please suggest me how i can achieve the above scenario.
Below is my listener configuration
<bean id="msglistenerForAuditError" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="sessionTransacted" value="true"/>
<property name="destinationName" value="test.audit.error2"/>
<property name="messageListener" ref="auditerrorListener" />
</bean>
<bean id="msglistenerForAuditEvent" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="sessionTransacted" value="true"/>
<property name="destinationName" value="test.audit.event2"/>
<property name="messageListener" ref="auditerrorListener" />
</bean>

The DefaultMessageListenerContainer is a lifecycle bean and as such it exposes a start and a stop method that you can use to start and stop the listener, respectively.
You can build a service on your own that is gathering all known instances in the context and you can then loop over those to stop the containers, something like
#Service
public class MyService {
private final Collection<DefaultMessageListenerContainer> containers;
#Autowired
public MyService(Collection<DefaultMessageListenerContainer> containers) {
this.containers = containers;
}
public void stopAll() {
// iterate over the collection and call "stop()" on each item
}
}
That being said:
You should not invoke this service as part of a message listener as attempting to stop the container while the thread is processing a message will have weird side effect
The whole use case looks suspicious to me. Your message listeners should be resilient and, more importantly, they should be independent of each other; if you are stopping listener A because listener B threw an exception, something is definitely wrong in your design

stop method on DefaultMessageListenerContainer did not worked but shutdown method worked perfectly.
for(DefaultMessageListenerContainer defaultCont:containers){
defaultCont.shutdown();
}

Injecting a collection of DefaultMessageListenerContainer did not work for me, I use Spring Boot 1.4.x, with Spring 4.3.x
Here's how I solved it:
package org.example.queue;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
//import other stuffs
#Component
public class QueueManager {
#Autowired
JmsListenerEndpointRegistry endpointRegistry;
public void shutdown() {
endpointRegistry.getListenerContainers().forEach((container) -> {
if (container.isRunning()) {
log.debug("Shutting down listener: " + container.getClass().getName());
container.stop();
}
});
}
public void start() {
endpointRegistry.getListenerContainers().forEach((container) -> {
if (!container.isRunning()) {
log.debug("Starting listener: " + container.getClass().getName());
container.start();
}
});
}
}

Related

Spring AMQP/RabbitMQ transaction rollback in EJB3 CMT

I am trying to test the rollback of a transaction that was initiated by EJB3 container, which involves the Spring JPA repository call and the message sender to the RabbitMQ using Spring AMQP integration. After the CMT rollback I see the DB transaction gets rolled back, but the message is getting delivered to the queue.
I am injecting the spring bean into the EJB that makes a call to the Rabbit template and it has #Transactional annotation. What I see is the TransactionInterceptor is committing the transaction after the Spring bean send message call. I was hoping it would delegate the commit to the container.
Any suggestions/workarounds are appreciated. I wasn't able to figure out how to initialize the TransactionSynchronizationManager without using the #Transactional annotation.
Here is the code that is committing the transaction when the spring bean proxy executes the TransactionInterceptor code:
ResourceHolderSynchronization
#Override
public void afterCommit()
{
if (!shouldReleaseBeforeCompletion()){ -- this method returns false
processResourceAfterCommit(this.resourceHolder); -- this is calling commitAll
}
}
ConnectionFactoryUtils/RabbitResourceSynchronization
#Override
protected boolean shouldReleaseBeforeCompletion() {
return !this.transacted; -- transacted is true (channelTransacted)
}
RabbitResourceHolder
public void commitAll() throws AmqpException {
try {
for (Channel channel : this.channels) {
if (deliveryTags.containsKey(channel)) {
for (Long deliveryTag : deliveryTags.get(channel)) {
channel.basicAck(deliveryTag, false);
}
}
channel.txCommit();
}
} catch (IOException e) {
throw new AmqpException("failed to commit RabbitMQ transaction", e);
}
}
Here is my spring configuration:
<tx:jta-transaction-manager/>
<tx:annotation-driven />
<rabbit:connection-factory id="rabbitConnectionFactory"
addresses="node01:5672,node02:5672" username="user.." password="pwd..." />
<bean id="rabbitTemplate"
class="org.arbfile.monitor.message.broker.api.rabbitmq.CustomRabbitTemplate" >
<property name="connectionFactory" ref="rabbitConnectionFactory" />
<property name="retryTemplate" ref="retryTemplate" />
<property name="exchange" value="user.example.exchange" />
<property name="queue" value="user.example.queue" />
<property name="routingKey" value="user.example.queue" />
<property name="channelTransacted" value="true" />
</bean>

Spring 4 WebSockets subscribe to a topic

Is it possible to map a method in a spring managed bean to a topic subscription using spring messaging?
I've looked at the examples here: http://assets.spring.io/wp/WebSocketBlogPost.html
including the example stock application but it looks like all topic subscription is on the client side. Is it possible to also subscribe to topics on the server side?
You can use JMS to subscribe to the topic.
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener" />
</bean>
More here: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/jms.html

Spring Data JPA repositories, transactions and eventing in Spring

The amount of grey hair has dramatically increased in last couple of days while trying to resolve the following problem. I'm using Spring Data JPA repositories in custom event listeners that utilises simple Spring 3.2 eventing mechanism. The problem I'm having is that if ListenerA creates an entity and calls assetRepository.save(entity) or assetRepository.saveAndFlash(entity) the subsequent calls to retrieve this same entity from another listener fails. The cause seems to be that the ListenerB can not find the original entity in the database, it seem to be still in Hibernate's cache.
The trigger for ListenerB to lock up the entity is an event fired as a result of a runnable task execution from a thread pool.
Here is my configuration:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="spring-jpa" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="database" value="#{appProps.database}" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.hbm2ddl.auto">#{appProps['hibernate.hbm2ddl.auto']}</prop>
<prop key="hibernate.show_sql">#{appProps['hibernate.show_sql']}</prop>
<prop key="hibernate.format_sql">#{appProps['hibernate.format_sql']}</prop>
<prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.impl.FSDirectoryProvider</prop>
<prop key="hibernate.search.default.indexBase">#{appProps.indexLocation}</prop>
<prop key="hibernate.search.lucene_version">#{appProps['hibernate.search.lucene_version']}</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
I'm omitting the dataSource configuration which is an instance of ComboPooledDataSource that defines connection to Oracle database. As a side note, component scanning is used and the project is Spring MVC.
Now Java classes.
ListenerA
#Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {
#Autowired
private AssetRepository assetRepository;
#Autowired
private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor
#Override
#Transactional
public void onApplicationEvent(FileUploadedEvent event) {
Asset target = event.getTarget();
Job job = new Job(target);
assetRepository.save(job);
executor.execute(job);
}
ListenerB
#Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {
#Autowired
private AssetRepository assetRepository;
#Override
#Transactional
public void onApplicationEvent(JobStartedEvent event) {
String id = event.getJobId();
Job job = assetRepository.findOne(id); // at this point we can not find the job, returns null
job.setStartTime(new DateTime());
job.setStatus(Status.PROCESSING);
assetRepository.save(job);
}
JobStartedEvent is fired from a runnable task within TaskExecutor.
What I'm doing wrong here? I have tried to use custom event publisher that is transaction aware, but that doesn't seem to solve the problem. I have also tried to wire appropriate service instead of data repository and remove #Transactional annotations from listeners, which also have failed. Any reasonable suggestions of how to solve the problem would be welcome.
I have managed to resolve the problem thanks to the hint from #Kresimir Nesek. So the solution was to replace Spring Data repositories with appropriate services.
Here are modified classes.
Listener A
#Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {
#Autowired
private JobService service;
#Autowired
private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor
#Override
public void onApplicationEvent(FileUploadedEvent event) {
Job job = service.initJobForExecution(event.getTarget());
executor.execute(job);
}
}
In the JobService method initJobForExecution(Asset target) had to be annotated with #Transactional(propagation=Propagation.REQUIRES_NEW) for everything to work.
Listener B
#Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {
#Autowired
private JobService service;
#Override
public void onApplicationEvent(JobStartedEvent event) {
service.updateStatus(event.getJobId(), Status.PROCESSING);
}
}
While this is slightly old, I ran in to this same problem but now with Spring 4.1.1.RELEASE, Spring Data JPA 1.7.0 and Hibernate 4.3.5.Final
My scenario occurred during testing with some tests failing. During testing, our problems were caused by H2 in single connection mode, broadcasting asynchronous events and event transactionality.
Solutions
First problem was due to transaction timeout and was solved by adding MVCC=true to the H2 URL string. See: https://stackoverflow.com/a/6357183/941187
Asynchronous Events were causing issues during tests since they executed on different threads. In the event configuration, a task executor and thread pool were used. To fix, just provided an overridden configuration bean using the SyncTaskExecutor as the task executor. This will cause all events to occur synchronously.
The event transactionality was tricky. In our framework, event get broadcast from within a transaction (#Transactional). The event was then handled on another thread outside of the transaction context. This introduced a race condition since the handler often depended on the object from the transaction to have had been committed. We didn't notice the problem on our Windows development machines but it became apparent when deployed to production on Linux. The solution uses TransactionSynchronizationManager.registerSynchronization() with an implementation of TransactionSynchronization.afterCommit() to broadcast the event after committing. See http://www.javacodegeeks.com/2013/04/synchronizing-transactions-with-asynchronous-events-in-spring.html for more info and examples.
Related to #3, we had to add #Transactional(propagation = REQUIRES_NEW) for some of the service methods called from some of the event handlers.
Hopefully this helps some late comers.

Autowiring in Spring 3 MDP

Firstly I've checked some of the possible answers that come up when posting a new question and none that I have come across deals with my issue.
I have a Spring MDP which works nicely i.e. can receive messages. The problem is when I try to autowire a dependency, the autowiring doesn't seem to work. I'm using Netbeans and Glassfish 3.1.2 so I'm able to step through the code and can confirm that the dependencies are null. Autowiring in other parts of the application are working fine. The MDP is picked up in the component-scan.
I used the example from springsource to create my MDP:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jms.html
And I've autowired the dependencies by setter methods.
I cannot figure out why this won't work. I've checked around and I don't think anyone else has had this issue.
Any ideas, pointers in the right direction, examples I can reference will be much appreciated.
Thanks.
KSS
MDP Class:
public class ExampleListener implements MessageListener {
private Transformer transformer;
private MurexService murexService;
#Autowired
public void setTransformer(Transformer transformer) {
this.transformer = transformer;
}
#Autowired
public void setMurexService(MurexService murexService) {
this.murexService = murexService;
}
#Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
}
}
ApplicationContext:
<jee:jndi-lookup id="connectionFactory" jndi-name="jms/QueueConnectionFactory" />
<jee:jndi-lookup id="testQueueOne" jndi-name="jms/ITFS_RECEIVE" />
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="com.scm.service.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="testQueueOne"/>
<property name="messageListener" ref="messageListener" />
</bean>
An AutowiredAnnotationBeanPostProcessor needs to be registered for wiring in the #Autowired fields. The javadoc has more details. See here for the solution to a similar issue.
Essentially adding this should get the autowiring to work:
<context:annotation-config/>

Spring configuration for multiple Activemq remote brokers

How to configure multiple remote activemq brokers (different IP address) in spring context? Below is the configuration for 1 remote broker. I am using camel to create routes that produce and consume messages from and to different queues in multiple remote brokers. Based on the following routes, how do the system knows which remote broker each queue belongs to?
List item
from("direct:start").to("activemq:queue:outgoingRequests")
List item
from("activemq:queue:incomingOrders").to("log:Events?
showAll=true").to("bean:jmsService")
Spring context for 1 broker
org.camel.routes
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://10.1.11.97:61616" />
</bean>
<bean id="pooledConnectionFactory"
class="org.apache.activemq.pool.PooledConnectionFactory" init-
method="start" destroy-method="stop">
<property name="maxConnections" value="8" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="pooledConnectionFactory"/>
<property name="concurrentConsumers" value="10"/>
</bean>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig"/>
</bean>
Just add more components with different names
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig"/>
</bean>
<bean id="activemq2" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="myOtherJmsConfig"/>
</bean>
Then simply use the names:
<from uri="activemq:queue:MY.QUEUE"/><!-- get from "1st" ActiveMQ -->
<to uri="activemq2:queue:MY.QUEUE"/> <!-- put to same queue name on other ActiveMQ -->
Actually, you can call them whatever you want, like "EuropeanMarketBroker" or whatever fits in.
I have been trying to achieve this with the difference that my spring configuration is not in xml. It is helpful to know that you can achieve the same outcome by using spring annotations in a few ways.
The key to achieving this is registering the component with the desired name.
For example:
camelContext.addComponent("activemq2", jmsComponentInstance);
There is two ways of achieving this. Namely by creating two beans with qualifiers which identifies them from each other and then wiring those beans and registering them as components. Alternatively (this is preferable) you can create the bean and register the component all at once. Below are examples of both:
1 - Create Bean and Register elsewhere
#Configuration
public class ClassA{
#Bean #Qualifier("activemq2") public JmsComponent createJmsComponent(){
return JmsComponent.jmsComponentAutoAcknowledge(..);//Initialise component from externalised configs
}
}
#Component
public class ClassB{
#Autowired private CamelContext camelContext;
#Autowired #Qualifier("activemq2")
private JmsComponent jmsComponent;
public void someMethod(){
camelContext.addComponent("activemq2", jmsComponent);
}
}
2 - Create Bean and Register in one place within your #Configuration bean.
#Bean #Autowired public JmsComponent createJmsComponent(CamelContext camelContext){
JmsComponent component = JmsComponent.jmsComponentAutoAcknowledge(..);//Initialise component from externalised configs
camelContext.addComponent("activemq2", component);//Add Component to camel context
return component;//Return component instance
}
I addition of the two answers, here is my working solution with the latest SpringBoot using dedicated properties for both broker:
First, I define two Beans for each ConnectionFactory:
// gatewayRouterProperties is a java `record` mapped to the application.yml property file.
// One ConnectionFactory for the onPremise broker
#Bean
public ConnectionFactory jmsConnectionFactoryOnPrem() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(gatewayRouterProperties.activeMq().brokerOnPrem().url());
activeMQConnectionFactory.setUserName(gatewayRouterProperties.activeMq().brokerOnPrem().user());
activeMQConnectionFactory.setPassword(gatewayRouterProperties.activeMq().brokerOnPrem().pass());
return activeMQConnectionFactory;
}
// Another broker ConnectionFactory for the cloud AWS broker
#Bean
public ConnectionFactory jmsConnectionFactoryAws() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(gatewayRouterProperties.activeMq().brokerAws().url());
activeMQConnectionFactory.setUserName(gatewayRouterProperties.activeMq().brokerAws().user());
activeMQConnectionFactory.setPassword(gatewayRouterProperties.activeMq().brokerAws().pass());
return activeMQConnectionFactory;
}
And then I just define the two Beans ActiveMQComponent (the same as Peter's answer but using annotations):
#Bean(name = "activemq")
public ActiveMQComponent createActiveMQComponentOnPrem() {
ActiveMQConfiguration amqConfig = new ActiveMQConfiguration();
amqConfig.setConnectionFactory(jmsConnectionFactoryOnPrem());
return new ActiveMQComponent(amqConfig);
}
#Bean(name = "activemq2")
public ActiveMQComponent createActiveMQComponentAws() {
ActiveMQConfiguration amqConfig = new ActiveMQConfiguration();
amqConfig.setConnectionFactory(jmsConnectionFactoryAws());
return new ActiveMQComponent(amqConfig);
}
Note that I am using the bean name attribute and no need to add that manually in CamelContext.
After in my Camel route I just use my activemq components beans like this:
// 'activemq' component => AMQ on-prem
// 'activemq2' component => AMQ AWS
from("activemq:queue:QUEUE.TO.SYNC.TO.AWS")
.routeId("gw-router-route-on-prem-to-aws")
.autoStartup("{{autostart-enabled}}")
.to("activemq2:queue:QUEUE.FROM.ON.PREM")
;

Resources