We have implemented a spring message listener service and the main operation exposed by this service is multiple event driven database update.
The context looks like as below.
<bean id="consumerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="concurrentConsumers" value="${jms.consumerContainer.concurrentconsumers}"/>
<property name="maxConcurrentConsumers" value="${jms.consumerContainer.maxconcurrentconsumers}"/>
<property name="errorHandler" ref="errorHandler" />
<property name="connectionFactory" ref="jmsQueueConnectionFactory" />
<property name="destination" ref="listenerQueue" />
<property name="messageListener" ref="consumerContainer" />
<property name="receiveTimeout" value="10000" />
<property name="sessionTransacted" value="true" />
<property name="transactionManager" ref="txManager" />
</bean>
TX manager is hibernate.
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager" p:sessionFactory-ref="sessionFactory"/>
and annotated the concrete class with #Transactional.
We use Jboss application server support to integrate with MQ via jndi.
The problem here is if there are any exception at any layer in the listener, the overall transaction is not getting rolled back and the message does not move to back out queue. It's obvious that as we use Hibernate transaction manager, it's not aware of other resource like JMS transactions.
Can I replace this safely with JTA transaction as Jboss will handle overall transaction management? Is there any foreseen risk in doing so?
I believe its no longer needed to annotate the class with #Transactional if we are using JTA as below.
<bean id="jtatxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
Thanks in advance.
Can I replace this safely with JTA transaction as Jboss will handle overall transaction management?
Yes, you can. You should just change your bean txManager definition.
I believe its no longer needed to annotate the class with #Transactional.
This is not true. You still need #Transactional. It allows spring to identify a transaction boundary.
Related
I am trying to connect to and consume from two different clusters of rabbitmq using a spring boot app via xml. It works well when a single rabbit:connection-factory bean is created in the application context. However, when the second one is added, it fails to start the application with the error "Parameter 1 of method rabbitListenerContainerFactory in org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration required a single bean, but 2 were found:". How do I go about creating different factories per cluster? Please suggest an alternative way of doing this, if it's not the right approach?
Here is the xml snippet:
<rabbit:connection-factory id="firstConnectionFactory" connection-factory="firstSpringConnectionFactory" />
<rabbit:connection-factory id="secondConnectionFactory" connection-factory="secondSpringConnectionFactory"/>
<bean id="firstSpringConnectionFactory"
class="org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean">
<property name="useSSL" value="${rabbitmq.ssl.enabled}" />
<property name="host" value="${rabbitmq.first.host}"/>
<property name="virtualHost" value="${rabbitmq.vhost}"/>
<property name="port" value="${rabbitmq.cluster.port}"/>
<property name="username" value="${rabbitmq.user}"/>
<property name="password" value="${rabbitmq.first.password}"/>
</bean>
<bean id="secondSpringConnectionFactory"
class="org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean">
<property name="useSSL" value="${rabbitmq.ssl.enabled}" />
<property name="host" value="${rabbitmq.second.host}"/>
<property name="virtualHost" value="${rabbitmq.vhost}"/>
<property name="port" value="${rabbitmq.cluster.port}"/>
<property name="username" value="${rabbitmq.user}"/>
<property name="password" value="${rabbitmq.second.password}"/>
</bean>
And the listener container code:
ConnectionFactory cf = rabbitConnectionFactory;//One of the connnection factories will be injected here from app context
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(cf);
container.setConcurrentConsumers(count);
container.addQueueNames(queueName);
container.setMessageListener(listener);
container.start();
Since you don't rely on the Spring Boot here and don't use Spring AMQP annotation support I suggest you to exclude RabbitAnnotationDrivenConfiguration from auto-configuration:
#EnableAutoConfiguration(exclude={RabbitAnnotationDrivenConfiguration.class})
spring.autoconfigure.exclude = org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration
If you still need #RabbitListener somewhere in other place of your project, you only have a choice to build all the #EnableRabbit infrastructure manually.
We have configured multiple transaction managers:
<tx:annotation-driven transaction-manager="transactionManager1" />
<tx:annotation-driven transaction-manager="transactionManager2" />
<tx:annotation-driven transaction-manager="transactionManager3" />
<bean id="transactionManage1"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory1" />
</bean>
<bean id="transactionManager2"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory2" />
</bean>
<bean id="transactionManage3"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory3" />
</bean>
However,I don't see a name of transaction manager specified in #Transactional annotation in a service class. I am wondering which transaction manager will be used? The code is working and it looks like it used the first one - "transactionManager1" by default. Do I miss something?
I am wondering which transaction manager will be used?
The behaviour there is probably undefined, but it's most likely to use whichever <tx:annotation-driven> comes first in the context.
There's no reason you should need to do this. If you need fine-grained control over which tx manager to use, then you need to do it programmatically using something like TransactionTemplate. The #Transactional annotation is a convenience, but isn't as flexible as using the underlying API directly.
I am trying to configure a JMS connection pool in spring/camel for Websphere MQ. I am seeing class cast exception, when tried to use CachingConnectionFactory from spring. Could not find a pool from WMQ, have anybody done connection pooling with WMQ, i didnt find any examples. There are lot of examples for ActiveMQ.
here is what i have so far, that is producing class cast exception.
<bean id="inCachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="inboundMqConnectionFactory1" />
<property name="sessionCacheSize" value="5" />
</bean>
<bean id="inboundWebsphereMq1" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="inCachingConnectionFactory" />
<property name="destinationResolver" ref="jmsDestinationResolver" />
<property name="transacted" value="true" />
<property name="transactionManager" ref="txManager1" />
</bean>
<bean id="inboundMqConnectionFactory1" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="${isi.inbound.queue.host2}" />
<property name="port" value="${isi.inbound.queue.port}" />
<property name="queueManager" value="${isi.inbound.queue.queuemanager2}" />
<property name="channel" value="${isi.inbound.queue.channel2}" />
<property name="transportType" value="${isi.queue.transportType}" />
</bean>
The exception i see is here
trying to recover. Cause: com.sun.proxy.$Proxy37 cannot be cast to com.ibm.mq.jms.MQQueueSession
In general:
do not use QueueConnectionFactory or
TopicConnectionFactory, as ConnectionFactory (JMS 1.1) is replacement for both
Each ConnectionFactory from v7 WMQ JMS client jars provide caching logic on each own so in general you don't need CachingConnection Factory.
Now try it this way:
<bean id="mqConnectionFactory" class="com.ibm.mq.jms.MQConnectionFactory"
p:queueManager="${QM_NAME}"
p:hostName="${QM_HOST_NAME}"
p:port="${QM_HOST_PORT}"
p:channel="${QM_CHANNEL}"
p:clientID="${QM_CLIENT_ID}">
<property name="transportType">
<util:constant static-field="com.ibm.msg.client.wmq.WMQConstants.WMQ_CM_CLIENT" />
</property>
</bean>
<bean id="userConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter"
p:targetConnectionFactory-ref="mqConnectionFactory"
p:username="${QM_USERNAME}"
p:password="${QM_PASSWORD}" />
<!-- this will work -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="userConnectionFactory"
p:cacheConsumers="true"
p:reconnectOnException="true" />
Of course you can cache sessions instead of consumers if you want it that way. By my experience WMQ session caching is measurable performance improvement but only if you are limited on CPU power on WMQ machine or by actual message throughput; both situations are rare in majority of world applications. Caching consumers avoids excessive MQ OPEN calls which is expensive operation on WMQ so it helps too.
My rule of the thumb is consumer + session caching performance benefit is equal to 1/2 of performance benefit of connection caching and usually not wort of pursuing in your everyday JEE stack unless you are hardware limited.
Since WMQ v7, asynchronous consumers are realy realy fast with literally no CPU overhead when compared to spring MC, and are preferred way of consuming messages if you are HW limited. Most of the days I still use Spring as I prefer its easy-going nature.
Hope it helps.
I have a legacy code that use the TransactionManager to obtain access to the transaction to associate synchronization objects to the transaction afterCommit.
When I migrate to Spring 3, the recommended transaction manager to my solution is WebSphereUowTransactionManager, in truth, I feel that it is only a factory to the real Transaction Manager. But this transaction manager always return null in the method getTransactionManager().
I'm using EJB's 2.1 (legacy, remember?) with CMT.
My configuration is Spring 3.1.2.RELEASE, Hibernate 3.6.8.Final, Websphere 6.1.0.43, my beans are:
<bean
id="transactionManager"
class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />
<bean
name="auditContextManager"
class="... my class ...">
<property name="transactionManager">
<bean factory-bean="transactionManager" factory-method="getTransactionManager" />
</property>
</bean>
My hibernate configuration is:
<hibernate-configuration>
<session-factory>
<property name="hibernate.jdbc.fetch_size">200</property>
<property name="hibernate.jdbc.use_get_generated_keys">true</property>
<property name="hibernate.bytecode.use_reflection_optimizer">true</property>
<property name="hibernate.connection.datasource">java:comp/env/jdbc/ORDS</property>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle9iDialect</property>
<property name="hibernate.max_fetch_depth">3</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory</property>
<property name="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.WebSphereExtendedJTATransactionLookup</property>
<property name="hibernate.show_sql">false</property>
... too much mappings to show here ...
</session-factory>
</hibernate-configuration>
And my bean auditContextManager has always transactionManager set to null.
It seems to me that WebSphereUowTransactionManager do not expose Transaction Manager intentionally. Is this alright ?
There is any way so I can get the Transaction Manager ? Since WebSphereUowTransactionManager is not really the transaction manager, but only a transaction manager factory.
It is not a factory for the TransactionManager but, as the name suggest, the UOWManager. It uses the Websphere native API for managing transactions (which has broader support for certain things than the plain JTA api).
So there isn't going to be a TransactionManager it will always be null.
Basically if you want the transactionamanager you will have to do a JNDI lookup.
com.ibm.ws.Transaction.TransactionManagerFactory is the factory you should use to get the transaction manager .
I have an application that runs inside of Websphere, and I am having an issue with persisting JPA entities.
Previously, the application was setup with RESOURCE_LOCAL persistence units, with the Spring JpaTransactionManager, and transactions that were committed explicitly in code.
TransactionStatus transactionStatus = transactionManager.getTransaction( new DefaultTransactionDefinition() );
try {
entityManager.persist( someJpaEntity );
}
catch( Exception exception ) {
transactionManager.rollback( transactionStatus );
throw exception;
}
try {
transactionManager.commit( transactionStatus );
}
catch( TransactionException exception ) {
exception
}
I am working on an enhancement to the application that will allow calls through a Message Driven Pojo linked to a Websphere Queue. I was able to setup a configuration through spring that will allow my application to receive messages through a JMS queue. The spring config looks like:
<jee:jndi-lookup id="jmsConnectionFactory" jndi-name="QueueConnectionFactory"/>
<jee:jndi-lookup id="jmsQueue" jndi-name="DIQueue" />
<!-- A dynamic resolver -->
<bean id="jmsDestResolver" class="org.springframework.jms.support.destination.DynamicDestinationResolver"/>
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref bean="jmsConnectionFactory"/>
</property>
<property name="destinationResolver">
<ref bean="jmsDestResolver"/>
</property>
</bean>
<bean id="messageListener" class="my.app.FileMessageListener" />
<bean id="exListener" class="my.app.JmsExceptionListener" />
<bean id="msgListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="destination" ref="jmsQueue" />
<property name="messageListener" ref="messageListener" />
<property name="transactionManager" ref="transactionManager" />
<property name="taskExecutor" ref="myTaskExecutor" />
<property name="exceptionListener" ref="exListener" />
</bean>
<bean id="myTaskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="workManagerName" value="wm/default" />
</bean>
Not sure if there is an issue with my spring setup, but I do receive messages through my Active MQ broker, so that part I seem to be good with.
Now, the issue is, that when I get a message in through JMS, I would call the above code to insert the JPA entity. When the code would run, I would get the message "unable to commit a one phase resource in a two phase transaction", or something similar. What I came to understand is that the Spring JpaTransactionManager does not work with XA or JTA transactions.
So, I worked on moving to the Spring JtaTransactionManager.
I changed everything I Could think of over to use JTA, here is where I declare my transaction manager:
<bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>
Updated my persistence XML:
<persistence-unit name="AppUnit" transaction-type="JTA">
<provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
<jta-data-source>java:APPDS</jta-data-source>
And still, nothing works. My code runs without exception, but nothing gets persisted to the database. The message gets pulled off of the JMS Queue, but no data.
Any suggestions?
I finally got this working, and figured I would post the answer.
There are actually 2 pieces to the puzzle.
First, in Websphere, you need to go to your app server -> TransactionService, and check / enable the "Accept Heuristic Hazard" checkbox. That definitely helped. I am running WAS 7.
The second thing, is you MUST set the property eclipselink.target-server on your persistence unit or your EntityManagerFactory.
That second item definitely did the trick. I tested with the property and without it. Without it, nothing persists. With it, everything works fine.
Here is my EntityManagerFactory, I am using a property placeholder to set the value of the eclipselink.target-server property:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="MyUnit" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="databasePlatform">
<value>${app.databasePlatform}</value>
</property>
<property name="showSql">
<value>${app.showSql}</value>
</property>
</bean>
</property>
<!-- THIS DID THE TRICK -->
<property name="jpaPropertyMap">
<map>
<entry key="eclipselink.target-server" value="${app.targetServer}"/>
</map>
</property>
</bean>