Spring AMQP/RabbitMQ transaction rollback in EJB3 CMT - spring

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>

Related

JPA entityMangerFactory not found through Persistence.createEntityManagerFactory("XYZ")

Any help will be greatly appreciated.
We are working on a web application. Which uses a JAR file (a java maven project) and has been added as a maven dependency in the web application.
Combination of this JAR file and web application itself creating problem.
Both web application and JAR are using Hibernate JPA to interact with database. But both are using 2 different ways for creating/initializing entityManagerFactory.
Web Application uses Spring xml based configuration to initialize entityManagerFactory.
CODE:
persistence.xml code:
<persistence-unit name="org.jbpm.persistence.jpa.local"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<mapping-file>META-INF/JBPMorm-JPA2.xml</mapping-file>
<class>org.drools.persistence.info.SessionInfo</class>
<class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
<class>org.drools.persistence.info.WorkItemInfo</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
<property name="hibernate.max_fetch_depth" value="3" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
Spring configuration:
<context:component-scan base-package="com.company.rd.core" />
<context:component-scan base-package="com.company.rd.services" />
<jee:jndi-lookup id="testDataSource" jndi-name="java:comp/env/jdbc/SybaseDB" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="testDataSource"/>
<property name="defaultTimeout" value="120"></property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="org.jbpm.persistence.jpa.local" />
<property name="dataSource" ref="testDataSource" />
<property name="jpaDialect" ref="jpaDialect" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="jpaDialect" ref = "jpaDialect"></property>
<property name="defaultTimeout" value="120"></property>
</bean>
<jee:jndi-lookup id="logDataSource" jndi-name="java:comp/env/jdbc/DRMLOG" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
And Here is the code to initializing entitymanagerFactory in JAR file.
persistence.xml
<persistence-unit name="codeAuthorization" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/comp/env/jdbc/SybaseDB</non-jta-data-source>
<class>com.company.auth.entity.AuthorizationCode</class>
<class>com.company.auth.entity.UserInvalidAttempt</class>
<class>com.company.auth.entity.AuthorizationProperty</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
And a java file which is injected into Base DAO through spring.
#Service
public class AuthorizationEntityMangerService {
#PersistenceUnit(name = "codeAuthorization")
private EntityManagerFactory entityManagerFactory;
public AuthorizationEntityMangerService() {
entityManagerFactory = Persistence
.createEntityManagerFactory("org.jbpm.persistence.jpa.local");
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
public EntityManager getEntityManager() {
return this.entityManagerFactory.createEntityManager();
}
public void closeEntityManager(EntityManager entityManager) {
if (entityManager != null && entityManager.isOpen()) {
entityManager.close();
}
}
public EntityTransaction getTransaction(EntityManager entityManager) {
return entityManager.getTransaction();
}
public void rollBackTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
}
public void commitTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.commit();
}
}
}
Calling code from Base DAO.
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
EntityTransaction transaction = null;
EntityManager entityManager = null;
try {
entityManager = this.entityMangerService.getEntityManager();
transaction = entityMangerService.getTransaction(entityManager);
transaction.begin();
final Query query = entityManager.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
final Object result = query.getSingleResult();
entityMangerService.commitTransaction(transaction);
return result;
} catch (final NoResultException e) {
entityMangerService.rollBackTransaction(transaction);
logger.error("Error" : " + e.getMessage());
return null;
} finally {
entityMangerService.closeEntityManager(entityManager);
}
}
Now Here is the problem when ever line entityManager.createQuery(queryString); execute it throws the exception.
2015-06-05 17:39:46,363 WARN DefaultExceptionHandler:94 - Unhandled exception caught by the Stripes default
exception handler.
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: AuthorizationProperty is
not mapped [SELECT pe.value FROM AuthorizationProperty pe WHERE pe.name=:propertyName AND pe.deleted=0]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1364)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke
(ExtendedEntityManagerCreator.java:334)
at com.sun.proxy.$Proxy53.createQuery(Unknown Source)
at com.company.authentication.dao.AuthorizationBaseDAO.getSingleResult(AuthorizationBaseDAO.java:40)
at com.company.authentication.dao.PropertyDAOImlp.getPropertyValue(PropertyDAOImlp.java:22)
at com.company.authentication.services.AuthorizationPropertyService.getPropertyValueByName
(AuthorizationPropertyService.java:19)
at com.company.rd.servlet.JspAuthorizationRestFilter.hasAuthorizationCode
(JspAuthorizationRestFilter.java:105)
at com.company.rd.servlet.AbstractAuthorizationRestFilter.isRequestAuthenticated
(AbstractAuthorizationRestFilter.java:120)
at com.company.rd.servlet.JspAuthorizationRestFilter.doFilter(JspAuthorizationRestFilter.java:84)
I have debugged the code and found entityManagerFactory for persistenceUnit "codeAuthorization" is not initialized. Only "org.jbpm.persistence.jpa.local" is available (verified through eclipse debugger) inside this method.
Note: This JAR is working fine in some other application where web application and JAR using same way to initialize entityMangerFactory [through Persistence.createEntityManagerFactory("")].
Please let me know How can I get "codeAuthorization" entiryManagerFactory
You are using Spring then use Spring, currently you are doing a lot of work to work around Spring and dependency injection and managed transaction. Don't use Persistence.createEntityManagerFactory(""). Just inject the EntityManager where you need it using an EntityManager field annotated with #PersistenceContext and specify the name of the one you want.
Also don't manage the transactions, entity manager yourself, spring does that for you. For this use the right PlatformTransactionManager the JpaTransactionManager and not the DatasourceTransactionManager as that won't work in a JPA environment. (At least not to manage your JPA transactions).
Doing this will really simplify your code and your life.
So basically ditch the service that is doing those nasty things and simple do things like this in your dao.
#Repository
public class YourDao {
#PersistenceContext(name="codeAuthorization")
private EntityManager em;
#Transactional
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
final Query query = em.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
return query.getSingleResult();
}
}
In your configuration replace the DatasourceTransactionManager with the JpaTransactionManager and add <tx:annotation-driven />. Then clean your code.
Note: The JpaTransactionManager is perfectly capable of managing plain JDBC transactions if you still need those, ideally you would have a single transaction manager.

Stop a spring jms message listener

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();
}
});
}
}

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

why transaction management using annotation is failing in spring with the below configuration

Why transaction management is failing in spring with the following configuration. The transaction is not rolled back even though a RuntimeException is thrown. Well, i am manually throwing it for learning purposes.
My Dao class:
#Transactional(rollbackFor=ArithmeticException.class)
public class TransactionAnnotationDaoImpl extends JdbcDaoSupport implements JdbcDao {
public void create(Student student) {
try {
String sql = "insert into student values (?,?,?)";
getJdbcTemplate().update(sql,student.getAge(), student.getName(), student.getId());
String marksSql="insert into marks values (?,?,?)";
int i=2/0; //added to depict roll back behaviour of the transaction when exception occurs
getJdbcTemplate().update(marksSql,student.getId(),student.getSubject(),student.getMarks());
System.out.println("transaction committed");
} catch (RuntimeException e) {
e.printStackTrace();
System.out.println("transaction rolled back");
}
}
}
My spring configuration file contents:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
<property name="url" value="jdbc:derby://localhost:1527/db;create=true"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="annotationTransactionDaoImpl" class="org.sujay.springjdbc.dao.TransactionAnnotationDaoImpl">
<property name="dataSource" ref="dataSource"></property></bean>
I am making call to dao from main method with the following call:
JdbcDao annotationDao = context.getBean("annotationTransactionDaoImpl", JdbcDao.class);
annotationDao.create(new Student(25, "p", 4, 19, "cn2"));
But the transaction is not rolled back. There is an entry in the student table but marks table doesnt have which means transaction is failing. Please help me with this issue.
Because you catched the exception.
Remove the try catch or rethrow the exception:
try{
...
} catch (RuntimeException e) {
e.printStackTrace();
System.out.println("transaction rolled back");
throw e; //rethrow so spring will recognize it
}

Spring JPA - Injecting transaction manager vs injecting entity manager

If I wanted manage transactions programmatically, what is the difference between starting the transaction by injecting a PlatformTransactionManager vs directly injecting EntityMangerFactory/EntityManager and getting transaction from Entitymanager
public class MyDAO {
#PersistenceContext(unitName="test") EntityManager em;
JpaTransactionManager txnManager = null;
public void setTxnManager(JpaTransactionManager mgr) {
txnManager = mgr;
}
public void process(Request request) throws Exception {
TransactionStatus status =
txnManager.getTransaction(new DefaultTransactionDefinition());
try {
em.persist(request);
txnManager.commit(status);
} catch (Exception up) {
txnManager.rollback(status);
throw up;
}
}
As apposed to injecting EntityManager directly
public class MyDAO {
#PersistenceContext(unitName="test")
EntityManager em;
public void process(Request request) throws Exception {
EntityTransaction txn = em.getTransaction();
try {
em.persist(request);
txn.commit();
} catch (Exception up) {
txn.rollback();
throw up;
}
}
where as spring config snippet looks like this
<beans>
<bean id="MyDAO" class="com.xyz.app.dao.MyDAO">
<context:annotation-config />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerE ntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistence" />
<property name="dataSource" ref="dataSourceProvider" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
</bean>
<bean id="transactionManagerJpa" class="org.springframework.orm.jpa.JpaTransactionM anager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
</beans>
Transaction managers should not be injected into DAOs, because a DAO has no way to tell whether they're one participant in a larger transaction or not.
I think the transaction manager belongs with the service layer, not the persistence layer. The services know about use cases and units of work. They orchestrate other services, DAOs and model objects to fulfill their use cases.

Resources