DefaultMessageListenerContainer problems with shutdown procedure - spring

I am new in Spring Framework and my questions are below:
I want to instantiate the DefaultMessageListenerContainer programmatically and the code that I use is:
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(cf);
container.setDestination(Queue);
container.setMessageListener(Consumer);
container.setReceiveTimeout(-1);
container.setMaxConcurrentConsumers(15);
container.setConcurrentConsumers(10);
container.start();
Why do I have to shutdown manually the DefaultMessageListenerContainer when my project is undeployed? If i do not shutdown manually the container the consumers stay open on my queues.
When I try to shutdown manually the container (by calling container.shutdown()) the procedure stucks and the project does not continue.
If i initialize the DefaultMessageListenerContainer without giving receiveTimeout the shutdown procedure is executed correctly. Is there any problem with setReceiveTimeout(-1)?

You only have to shutdown your listener manually because you've programmatically started it! If you use an ApplicationContext to load your Spring beans from xml, then shutting down the App Context will shutdown all the beans for you.
The simplest way I've found to control Spring loaded beans is to create a servlet that implements init() and destroy() methods from HttpServlet. Init() loads my Spring configuration from my xml files (i.e. master file called spring.xml), and caches the ApplicationContext object. Then destory() will call close() on the ApplicationContext. This will close/shutdown all the Spring beans (i.e. your JMS listeners will get stopped).
Any particular reason you're programmatically creating your listeners?

receiveTimeout is the problem. To shutdown, the container must have a chance to stop listening to the queue. If your consumer thread has an infinite timeout, it will continue listening and never check to see if the container needs to shutdown. Your container will take up to the receiveTimeout to shutdown. If it is -1, it will never shutdown.

What you need here is to be able to just stop the container (not shut it down or unregister it) and be able to start it back up when you want to, all at runtime. Just use the .start() and .stop(), which are methods inherited from AbstractJmsListeningContainer I think. And do not mix these with .doStart(), .shutDown()... See the spring documentation.
With your listener wired via Spring, you can grab it anytime from the context and run a .stop or .start on it. During the Spring autowiring, you may set the property autoStartup to false, and the listenerContainer will be initialized but will not do any listening at startup.

This is what I did to dynamically create new Listener and let Spring handle shutdown procedure
<beans>
//other beans
<bean id="importReadQueueDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>queue/dummyQueue</value></property>
<property name="resourceRef"><value>true</value></property>
</bean>
<bean id="importQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>ConnectionFactory</value></property>
<property name="resourceRef"><value>true</value></property>
</bean>
<bean id="importReadQueueSenderService" class="com.localhost.ImportReadQueueSenderService" scope="prototype"/>
<!-- this is the Message Driven POJO (MDP) -->
<bean id="importReadMessageListener" class="com.localhost.listener.ImportReadMessageListener" scope="prototype"/>
<!-- and this is the message listener container -->
<bean id="importReadJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" scope="prototype">
<property name="connectionFactory" ref="importQueueConnectionFactory" />
<property name="destination" ref="importReadQueueDestination" />
<property name="messageListener" ref="importReadMessageListener" />
<property name="concurrentConsumers" value="1"/>
</bean>
</beans>
Here, I've created a dummy queue queue/dummyQueue since DMLC requires to set either destination or destinationName property.
ImportReadMessageListener extends MessageListener.
Java code to create and cache dynamic listeners
//Actual queue name where I need to send message. `tenantStore` is obtained from ThreadLocalObject
String queue = tenantStore.getProperty("importReadQueue");
//Obtaining existing senderService for that queue
Object queueSenderService = AppConfigurationManager.getQueueSenderService(queue);
if (queueSenderService != null) {
((IImportReadQueueSenderService) queueSenderService).sendObjectMessage(importReadQueueDO);
} else {
// In-case of call received from new tenant, then dynamically create and cache a separate listener and DMLC for it
InitialContext ic = new InitialContext();
Queue destination = (Queue) ic.lookup(queue);
ConnectionFactory importQueueConnectionFactory =(ConnectionFactory) ServiceContext.getBean("importQueueConnectionFactory");
JmsTemplate importJmsTemplate=new JmsTemplate(importQueueConnectionFactory);
importJmsTemplate.setDefaultDestination(destination);
Object importReadMessageListener = ServiceContext.getBean("importReadMessageListener");
DefaultMessageListenerContainer dmlc = (DefaultMessageListenerContainer)ServiceContext.getBean("importReadJmsContainer");
dmlc.setDestination(destination);
/* below two steps are extremely important else you won't receive any message.
I already wasted a day behind this.*/
// https://stackoverflow.com/a/21364885/4800126
dmlc.afterPropertiesSet();
dmlc.start();
IImportReadQueueSenderService importQueueSenderService = (IImportReadQueueSenderService) ServiceContext
.getBean("importReadQueueSenderService");
AppConfigurationManager.cacheQueueDetails(queue, dmlc, importQueueSenderService,
importReadMessageListener);
importQueueSenderService.setJmsTemplate(importJmsTemplate);
importQueueSenderService.sendObjectMessage(importReadQueueDO);
}
So now when you close the application Spring will automatically shutdown all the listeners.

Related

jms:listener-container acknowledge="transacted" not working

We are trying to implement a durable subscriber using Spring JMS. Given below is how I've configured the durable subscribers. We are using Jackson message coverter, to convert incoming JSON to java object.
As per my understanding, if we mention destination-type="durableTopic" and acknowledge="transacted" in the jms:listener-container, message re-delivery would happen if exceptions are thrown while processing message in subscriber. However, for us, the message re-delivery is not happening if we encounter exceptions on our subscriber side. I've also given the java code snippet below.
We want to save the message into database. So, we tried a scenario where database is not started. So, here, the exception com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server. Attempted reconnect 3 times. Giving up is thrown, but message re-delivery is not happening after this, whereas, we want the message to be redelivered (2-3 retries at least) for this scenario.
Also, I tried this same configuration and code inside a simple Spring MVC application and there, the messages are getting re-delivered whenever exceptions occur in app. So, not able to understand what is going wrong in this case. Could anybody help us here, identify what is the problem with configuration or code?
Configuration in root-context.xml
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
</bean>
<bean id="messageConverter" class="org.springframework.jms.support.converter.MappingJackson2MessageConverter">
<property name="typeIdPropertyName" value="__type" />
</bean>
<jms:listener-container connection-factory="amqConnectionFactory" destination-type="durableTopic" message-converter="messageConverter" acknowledge="transacted" client-id="svcOrdersSubscriber">
<jms:listener destination="topicOrders" ref="ordersSubscriber" method="receive" subscription="ordersSubscription" />
</jms:listener-container>
<jms:listener-container connection-factory="amqConnectionFactory" destination-type="durableTopic" message-converter="messageConverter" acknowledge="transacted" client-id="svcResultsSubscriber">
<jms:listener destination="topicResults" ref="resultsSubscriber" method="receive" subscription="resultsSubscription" />
</jms:listener-container>
Java code
#Component("ordersSubscriber")
public class OrdersSubscriber {
#Autowired
OrderService orderService;
public void receive(Order order) {
orderService.saveOrders(order);
}
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
The problem got resolved. I was using simple ActiveMQConnectionFactory in my MVC app, and here we were using ActiveMQXAConnectionFactory, but I think since the container was not supporting XA, transactions were not happening. Now, we switched to simple ActiveMQConnectionFactory, and things are working fine.

Messaging exception: org.springframework.messaging.MessageDeliveryException in sftp outbound-channel-adapter

Messaging exception: org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'org.springframework.web.context.WebApplicationContext:/.sftpChannel'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
I am trying to send file from local to remote server.
My application context is below
<bean id="startupBean" class="com.SchedulerImpl" init-method="run"/>
<bean id="applicationContextProvider" class="com.ApplicationContextProvider"></bean>
<bean id="sftpSessionFactory" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="defaultSftpSessionFactory" />
</bean>
<bean id="defaultSftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${destination.host}"/>
<property name="privateKey" value="${destination.privateKey}"/>
<property name="privateKeyPassphrase" value="${destination.privateKeyPassphrase}"/>
<property name="port" value="${destination.port}"/>
<property name="user" value="${destination.user}"/>
<property name="sessionConfig" ref="props"/>
</bean>
<util:properties id="props">
<prop key="PreferredAuthentications">publickey</prop>
</util:properties>
<int:channel id="sftpChannel"/>
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
session-factory="sftpSessionFactory"
channel="sftpChannel"
auto-startup ="false"
charset="UTF-8"
remote-directory="/destinationFolder/"
remote-file-separator="/">
</int-sftp:outbound-channel-adapter>
<int:publish-subscribe-channel id="contextRefreshEvents"/>
<int:outbound-channel-adapter channel="contextRefreshEvents"
expression="sftpOutboundAdapter.start()" />
so I get same instance of applicationContext inside code of sftp class as:
ApplicationContext context = appContext.getApplicationContext();
MessageChannel sftpChannel = (MessageChannel)context.getBean("sftpChannel");
and #Autowired private ApplicationContextProvider appContext;
inside same sftp class.
there's another class ApplicationContextProvider which implements ApplicationContextAware which helps me to get current ApplicaitonContext.
I dont understand why I get No subscriber found.
I have put auto-startup=false.
what would be correct way of getting current sftpChannel bean which gives me same instance of applicationContext.
If I do appContext = new classpathxmlapplicationcontext(applicationcontext.xml )
I get error in startupBean, so I dont want to do that.
Now I am implementing ApplicationContextAware, and I get Messaging exception.
could anyone please help me out?
I am using spring 3
You have shared a lot from your mind and in the end your question becomes in a mess.
From other side let me explain what's going on.
Since you have auto-startup ="false" on the <int-sftp:outbound-channel-adapter>, any send to the sftpChannel will cause that Exception until you start that adapter.
From other side I see you do that, although we don't see who initiates contextRefreshEvents messages, but we believe you that it is <int-event:inbound-channel-adapter>.
And one more point here that you don't show us that file from local. OK, I guess it is <int-file:inbound-channel-adapter>. Which is pollable and starts to poll the local director just after application start up. So, that may cause Dispatcher has no subscribers, because sftpOutboundAdapter hasn't been started yet.
Let me know, if I went wrong way in my reasoning, but you expected something different.
UPDATE
To start endpoint (or any other Lifecycle component) programmatically you should inject it to your service:
#Autowired
#Qualifier("sftpOutboundAdapter")
private Lifecycle sftpOutboundAdapter;
....
sftpOutboundAdapter.start();

Update database and send JMS message in single transacion?

I'm using Spring's DataSourceTransactionManager for transaction managment and JmsTemplate for sending messages to ActiveMQ queue. My problem is force to work in single transaction next algorithm:
Step 1: update DB;
Step 2: send message to queue;
Step 3: update DB;
Step 4: send message to queue.
As I understand from documentation for JmsTemplate, in my case I must set parameter "sessionTransacted" = true:
Setting this flag to "true" will use a short local JMS transaction when running outside of a managed transaction, and a synchronized local JMS transaction in case of a managed transaction (other than an XA transaction) being present. The latter has the effect of a local JMS transaction being managed alongside the main transaction (which might be a native JDBC transaction), with the JMS transaction committing right after the main transaction.(c)
My jms-configuration file contains only this:
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" ref="url"/>
<property name="userName" ref="username"/>
<property name="password" ref="password"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestinationName" value="SomeQueue"/>
<property name="sessionTransacted" value="true"/>
</bean>
After that I try to test it in simple way:
Case A:
#Transactional
public void sendMessageTransactionalErr(Object message, List<String> queueDestinationNames) throws Exception {
sender.sendMessage(message, queueDestinationNames);
throw new Exception("FatalException!");
}
Case B:
#Transactional
public void sendMessageTransactionalOK(Object message, List<String> queueDestinationNames) throws Exception {
sender.sendMessage(message, queueDestinationNames);
}
But in both cases after request execution message is send to queue. Even if JDBC transaction rolled back, JMS transaction commit succesfull.
What should I do to make it work as I need to?
You need to use a transaction manager that handles BOTH your JMS transaction and database transaction. Your JMS transaction is separate from the database transaction.
I don't recall exactly, but when I had this problem I created an instance of org.springframework.jms.connection.JmsTransactionManager. Create a JTA transaction manager and make sure your it is aware of this AND the database transaction manager.
Use #Transactional("jtaTransactionManager") for the annotation. I may have tried Bitronix or JOTM for this use case.
See Spring Integration and Transaction with JMS and DB
Reference: http://www.javaworld.com/article/2077963/open-source-tools/distributed-transactions-in-spring--with-and-without-xa.html?page=2

Publish subscribe implementation with Spring JMS

I have a JMS queue implementation with JmsTemplate. I want to have more than one listener when a message is put in the queue, i.e. I want to use topic instead of queue.
I have configuration without JMS namespacing. What are the changes that need to be made to have multiple listeners listen on a topic when someone sends a message in a topic.
I guess you are probably using DefaultMessageListenerContainer. Just to be sure, you want that several individual components receive the same message (i.e. you don't want to process messages in parallel).
Assuming I got this right and component A and compoent B should receive the same message, you simply create two DefaultMessageListenerContainer instance on the same topic and you set the pubSubDomain property to true. Make sure you haven't set any concurrency on the listener container, or better yet, set the concurrency to 1 to make that explicit.
This would give something like
<bean id="listener1"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="pubSubDomain" value="true"/>
<property name="concurrency" value="1"/>
<property name="destinationName=" value="...."/> <!-- topic name -->
<property name="messageListener" ref="...."/>
</bean>
Then you should create a similar bean for the second component.

Spring, XA and WebSphere

I am trying to get XA transactions working in a Spring v3 application inside WebSphere v7.
My App Context reads:
<bean id="jmsConnectionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jms/MQConnectionFactory"/>
<property name="resourceRef" value="true"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
</bean>
<jee:jndi-lookup id="myDB" jndi-name="jdbc/myDB"/>
<bean id="txManager"
class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />
<tx:annotation-driven transaction-manager="txManager"/>
I'm referencing this article that says mix in the UOW txn manager and you'll be fine. But it doesn't work that way. Instead, in the following code, the message is destructively read and is not rolled back when an exception is thrown.
The transactional logic is (in scala):
#Transactional(rollbackFor = Array(classOf[Throwable]))
def processNextMessage(category: String) = {
val maybeMessage = readNextMessage(category) // <- this is a destructive read
for (message <- maybeMessage) {
// this is temporary code for testing
throw new RuntimeException("blaaaaaah")
// end temporary code
// sendToQueue(message, queue)
// writeToMessageStore(message)
}
}
Can anyone advise how I can use WebSphere's JTA transaction manager with Spring?
First of all, I would really like to see the code for readNextMessage as that may be the culprit.
Is the queue connection factory set up as an XA resource. You are trying to use JTA for transactions, so as far as I know you need to configure the message qcf accordingly.
You do not have to setup the JmsTemplate for transactions, as these are handled by the QueueConnectionFactory.
On a side note: if you are just dealing with mq, you can skip the UOW JTA provider and use transacted JMS sessions, which should work fine.

Resources