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
Related
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.
I am having multiple datasource and one one database configured with JPA. I am using websphere 7. I want all these datasouces to be configured as global transactions. I am using below spring configurations but the transactions are not working as expected global transaction. If one db is failing then the other db is getting commited which is not expected as single global transactions. Can you please help me where i m doing incorrect,
I am having 2 datasouce one as configured below with id="us_icfs_datasource" and another using JPA
<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/persistenceUnit"/>
<bean id="pabpp" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- Needed for #Transactional annotation -->
<tx:annotation-driven/>
<jee:jndi-lookup id="US_ICFS_DATASORCE"
jndi-name="jdbc/financing_tools_docgen_txtmgr"
cache="true"
resource-ref="true"
proxy-interface="javax.sql.DataSource" />
also I have added below code in web.xml
<persistence-unit-ref>
<persistence-unit-ref-name>persistence/persistenceUnit</persistence-unit-ref-name>
<persistence-unit-name>persistenceUnit</persistence-unit-name>
</persistence-unit-ref>
<persistence-context-ref>
<persistence-context-ref-name>persistence/persistenceUnit</persistence-context-ref-name>
<persistence-unit-name>persistenceUnit</persistence-unit-name>
</persistence-context-ref>
below is my code where i m using transaction
> #Transactional public TemplateMapping addTemplateMapping(User user,
> TemplateMapping templateMapping) throws
> TemplateMappingServiceException { .... }
On Websphere you should use this bean to hook into the Websphere transaction manager:
<bean id="transactionManager"
class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>
See also this article
EDIT:
In order to use 2-phase commit (i.e. ensuring consistency across multiple resources), you will need to use XA data sources. See this article for details.
First of all your data sources that participate in global transaction must be of javax.sql.XADataSource type.
You also have to set transaction type in your persistence unit to JTA (not RESOURCE_LOCAL).
And you need to inform your JPA implementation that you want to do global transactions.
I am working on a POC which does the following
Uses a message driven channel adapter to recieve message in a transaction
Calls the Service Activator which uses a handler to insert the message recieved from the adapter to DB and also post message to outbound channel.
Now, if the DB insert of the message fails i want the JMS message returned back to the queue so that it can be re-tried later.
With my below configuration it doesnt seems to work.(i.e. even if there is a failure while inserting into the database the message is removed from the queue.
Any pointers or sample configuration would be helpful.
<integration:channel id="jmsInChannel">
<integration:queue/>
</integration:channel>
<int-jms:message-driven-channel-adapter id="jmsIn"
transaction-manager="transactionManager"
connection-factory="sConnectionFactory"
destination-name="emsQueue"
acknowledge="client" channel="jmsInChannel"
extract-payload="false"/>
<integration:service-activator input-channel="jmsInChannel"
output-channel="fileNamesChannel" ref="handler" method="process" />
<bean id="handler" class="com.irebalpoc.integration.MessageProcessor">
<property name="jobHashTable" ref="jobsMapping" />
</bean>
Set acknowledge="transacted" and, I presume the transactionManager is a JDBC (or JTA) transaction manager.
You also need to remove <queue/> from JmsInChannel so that the database transaction occurs on the same thread.
Spring will synchronize the database transaction with the JMS transaction.
However, read http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html for the implications.
If you can't make your service idempotent, you may need to look at an XA transaction manager.
I am trying to use Spring to send a message to a queue. It works fine when I don't try to enable transaction handling. However, when I add transaction handling the message doesn't seem to send to the appropriate queue. All i add is a #Transactional attribute on the method and the following to the application context.
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
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.