Correct usage of transactions in JMS and EJB - java

I wonder how to use JMS transactions correctly inside an EJB container. I found this code, that sends messages using JMS in a stateless bean:
#Stateless
public class SendEventsBean {
private static final Logger log = Logger.getLogger(SendEventsBean.class);
#Resource(mappedName = "jms/MyConnectionFactory")
private ConnectionFactory jmsConnectionFactory;
#Resource(mappedName = "jms/myApp/MyQueue")
private Queue queue;
public void sendEvent() {
Connection jmsConnection = null;
try {
connection = jmsConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
MyObj obj = new MyObj(1, "Foo");
ObjectMessage myObjMsg = session.createObjectMessage(obj);
producer.send(myObjMsg);
} catch (JMSException jmxEx) {
log.error("Couldn't send JMS message: ", jmsEx);
}finally{
if (jmsConnection != null) {
try {
jmsConnection.close();
}catch(JMSException ex) {
log.warn("Couldn't close JMSConnection: ", ex);
}
}
}
}
(from When should I close a JMS connection that was created in a stateless session bean? )
At default the transactions are container managed with transaction attribute 'required'. Suppose a client calls sendEvent() directly, so that the transaction starts at the beginning and ends at the end of sendEvent() (-> a commit is executed at the very end of the method).
Isn't it wrong to close the connection (jmsConnection.close()) BEFORE the commit occurs at the very end of the method?
Furthermore I'm wondering how setting the transactional attribute and setting true/false at createSession() interacts.
Does it make sense setting createSession(true,...) if there is already a transaction started by the container (using container managed transactions)? Does this create a new transaction just for JMS messages (and not for DB also) inside the JTA transaction?
And with createSession(false, ...) am I right, that messages are nevertheless transactional because of the transaction started by the container?

Isn't it wrong to close the connection (jmsConnection.close()) BEFORE the commit occurs at the very end of the method?
No. Closing connections has got nothing to do with commit within a JTA transaction (which is the case here, it being an ejb with CMT). It is just proper and essential cleanup. Note these are connections returned by the container and the underlying transaction manager knows how to work with the resource to commit the transactions. Same goes for JDBC connections as well.
Does it make sense setting createSession(true,...) if there is already a transaction started by the container (using container managed transactions)?
For Weblogic, you should definitely be using non transacted sessions. But what is important to use XA connection factories for your JMS connections.
http://docs.oracle.com/cd/E11035_01/wls100/jms/trans.html#wp1031645
http://www.informit.com/articles/article.aspx?p=26137&seqNum=8
However articles related to JBOSS suggests setting the createSession(true...) as a good practise even within a CMT ejb
https://developer.jboss.org/thread/213629?tstart=0&_sscc=t
http://www.coderanch.com/t/606786/EJB-JEE/java/EJB-CMT-sending-JMS-message
Irrespective of the setting, JCA/XA based connection factories have to be used compulsorily.
And with createSession(false, ...) am I right, that messages are
nevertheless transactional because of the transaction started by the
container?
No. As mentioned above, you will have to use XA connection factories.

Related

spring boot xa transaction datasource and jms

I make a POC with spring-boot-starter-data-jpa and spring-boot-starter-activemq. I would like to push the jms message on the broker (activeMQ) when the jpa transaction was commited.
My code :
UtilsateurService with have the "main" transaction:
#Service
public class UtilisateurService {
#Autowired
private UtilisateurRepository utilisateurRepository;
#Autowired
private SendMessage sendMessage;
#Transactional(rollbackOn = java.lang.Exception.class)
public Utilisateur create(Utilisateur utilisateur) throws Exception {
final Utilisateur result = utilisateurRepository.save(utilisateur);
sendMessage.send("creation utilisateur : " + result.getId());
throw new Exception("rollback");
//return result;
}
}
The SendMessage class witch "manage" Jms message:
#Component
public class SendMessage {
#Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
#Value("${jms.queue.destination}")
private String destinationQueue;
public void send(String msg) {
this.jmsMessagingTemplate.convertAndSend(destinationQueue, msg);
}
}
My main class :
#SpringBootApplication
#EnableJms
#EnableTransactionManagement
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The JMS message was push on the activeMq broker before exception was throw. So I don't have "rollback" on the broker.
How can I configure to have xa transaction running?
is your jmsTemplate Transacted ?
jmsTemplate.setSessionTransacted(true);
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/support/JmsAccessor.html#setSessionTransacted-boolean-
public void setSessionTransacted(boolean sessionTransacted)
Set the transaction mode that is used when creating a JMS Session.
Default is "false". Note that within a JTA transaction, the parameters
passed to create(Queue/Topic)Session(boolean transacted, int
acknowledgeMode) method are not taken into account. Depending on the
Java EE transaction context, the container makes its own decisions on
these values. Analogously, these parameters are not taken into account
within a locally managed transaction either, since the accessor
operates on an existing JMS Session in this case.
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. This 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.
http://www.javaworld.com/article/2077963/open-source-tools/distributed-transactions-in-spring--with-and-without-xa.html
30.2.5 Transaction management
Spring provides a JmsTransactionManager that manages transactions for
a single JMS ConnectionFactory. This allows JMS applications to
leverage the managed transaction features of Spring as described in
Chapter 17, Transaction Management. The JmsTransactionManager performs
local resource transactions, binding a JMS Connection/Session pair
from the specified ConnectionFactory to the thread. JmsTemplate
automatically detects such transactional resources and operates on
them accordingly.
In a Java EE environment, the ConnectionFactory will pool Connections
and Sessions, so those resources are efficiently reused across
transactions. In a standalone environment, using Spring’s
SingleConnectionFactory will result in a shared JMS Connection, with
each transaction having its own independent Session. Alternatively,
consider the use of a provider-specific pooling adapter such as
ActiveMQ’s PooledConnectionFactory class.
JmsTemplate can also be used with the JtaTransactionManager and an
XA-capable JMS ConnectionFactory for performing distributed
transactions. Note that this requires the use of a JTA transaction
manager as well as a properly XA-configured ConnectionFactory! (Check
your Java EE server’s / JMS provider’s documentation.)
Reusing code across a managed and unmanaged transactional environment
can be confusing when using the JMS API to create a Session from a
Connection. This is because the JMS API has only one factory method to
create a Session and it requires values for the transaction and
acknowledgment modes. In a managed environment, setting these values
is the responsibility of the environment’s transactional
infrastructure, so these values are ignored by the vendor’s wrapper to
the JMS Connection. When using the JmsTemplate in an unmanaged
environment you can specify these values through the use of the
properties sessionTransacted and sessionAcknowledgeMode. When using a
PlatformTransactionManager with JmsTemplate, the template will always
be given a transactional JMS Session.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html#jms-tx
Hassen give the solution. So I change the SendMessage class to :
#Component
public class SendMessage {
private final JmsMessagingTemplate jmsMessagingTemplate;
#Value("${jms.queue.destination}")
private String destinationQueue;
#Autowired
public SendMessage(JmsMessagingTemplate jmsMessagingTemplate) {
this.jmsMessagingTemplate = jmsMessagingTemplate;
this.jmsMessagingTemplate.getJmsTemplate().setSessionTransacted(true);
}
public void send(String msg) {
this.jmsMessagingTemplate.convertAndSend(destinationQueue, msg);
}
}

How to avoid commit for mybatis sql session in a multi-thread context

I have a restful service which use mybatis for the persistence layer. I use SqlSessionTemplate. Now for test, I start the war in a embedded tomcat in Maven and then share the context with my tests. So they can use the same db connection. I was hoping the tests can be done in transactions. For each test scenario, data get setup at beginning and rollback after the test. Meanwhile, I used SingleConnectionDataSource for datasource, so all the session will share the same db connection.
But I found since the restful service is multi-thread. It turns out every session will in a thread, so the SqlSession need to be closed, so the session forced commit on the connection. Just wondering how can i avoid the commit?
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
Here is the code of SqlSessionInterceptor, you can see it checks isSqlSessionTransactional(), then it force sqlSession to commit.

Auto retry after EJB transaction rollback

Basically, I have a JMS queue and an MDB to collects the messages from the JMS queue, does some processing on them, and then persists the messages into the database via JPA. I marked the method, which is responsible for persisting the message into DB, to be started in a new transaction:
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void create(T entity)
{
try
{
getEntityManager().persist(entity);
}
catch(Exception e)
{
throw new RuntimeException("DB Exception");
}
}
If a transaction is rolled back, is it going to be retired automatically until the transaction completes? If not, how to enable that?
If the exception propagates to the MDB, the transaction will not commit, the message will not be acknowledged as received and will be retried. From the EJB 3.1 specs:
Message acknowledgment is automatically handled by the container. If
the message-driven bean uses container-managed transaction
demarcation, message acknowledgment is handled automatically as a part
of the transaction commit.
I'm not familiar with Weblogic but there should be a JMS queue parameter that set the number of retries, retry interval, etc. until the message is dropped or put on an undelivered queue.
But usually is better to catch the exception in the MDB because a RuntimeException thrown from the MDB results in the bean to discarded by the container. From the EJB 3.1 specs:
Message-driven beans should not, in general, throw RuntimeExceptions.
A RuntimeException that is not an application exception thrown from
any method of the message-driven bean class (including a message
listener method and the callbacks invoked by the container) results in
the transition to the “does not exist” state.
For example, is better to have:
public class MyMDB implements MessageListener {
#Resource
private MessageDrivenContext context;
public void onMessage() {
try {
//some other processing
someService.create(entity);
}
catch(Exception e) {
//mark the message as undelivered
context.setRollbackOnly();
}
}
}

Working with JDBC in EJB-Hibernate env

I have a little of confusion about JDBC connection, transactions and their integration in EJB, JTA, Hibernate environment. My doubts are:
when we use #Resource DataSource ds; ... ds.getConnection() , are we working in the same transaction used by the managed bean? Should we close the connection, statement, resultset?
what about session.doWork? Are we in the same transaction? What about closing statement and result set?
aggressive release mode in Hibernate means that connections are closed after each statement. Does it mean that transaction is committed too? (I don't think this is true, but I can't understand how Hibernate works here)
There are a few things you need to figure out. First thing you need to identify what is your unit of work.
The session-per-request pattern is one of the most used and unless you have specific needs stick with that.
If you are using Hibernate you don't use statements and result sets directly. Hibernate will do that for you. what you need to close is the hibernate session
What you use is a SessionFactory and a Session object. The session pretty much represents your unit of work. Inside the hibernate session you get your objects, you change them and save them back.
The session per request pattern opens a session when a request is received and closes it when the response is sent back.
In a container managed EJB session bean a transaction is available and the datasource you (or hibernate) use in such a container is automatically handled by a JTA TransactionManager.
Now because Hibernate is smart it can automatically bind the "current" Session to the current JTA transaction.
This enables an easy implementation of the session-per-request strategy with the getCurrentSession() method on your SessionFactory:
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
So to answer your questions:
If you are using Hibernate with JTA in a container you'd be better off using a JPA EntityManager or maybe spring hibernate template.
Here are some references:
http://community.jboss.org/wiki/sessionsandtransactions#Transaction_demarcation_with_JTA
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/HibernateTemplate.html

Make transactionless EJB call inside a transaction

I'll try to describe the situation. We have a web service; on each request web service starts a JTA transaction. It performs several database calls through XA datasource within that and calls some other web services (out of transaction context) and also makes a couple of remote EJB calls on other server.
The problem is that container seems to try to involve EJB into transaction (and that seems logical), but in fact I want it to not particpate in that transaction as when it does participate in that transcation it always times out in the final commit phase, but when I exclude EJB call it works fine.
I cann't change EJB implementation and only control web service code. So, my question is: how do I make an EJB call to transaction-aware EJB, but out of my JTA transaction, but still being in JTA transaction for other XA resourse? I hope I made my question clear :).
EDIT: Trying to make it more clear with pseudo-code example:
// Begin transaction
UserTransaction tx = (UserTransaction) ctx.lookup(USER_TRANSACTION);
tx.begin();
// Do some database operations on XA datasource
// Call remote EJB which has transcation attribute set to 'Supports'
AccountInfo account = accountEjb.getAccountInfo(userId, accountId); // <-- Is it possible to make this to be not be part of user transction?
// Do some more database operations on XA datasource
// Commit transaction
tx.commit();
You can create another bean with some appropriate transaction attribute. This bean can delegate all calls to the first bean.
Or you can invoke this ejb from another thread.
EJB transaction is declarative: for a given deployment of a given EJB, you specify its transaction semantics. The exact EJB can be deployed (under a different name, of course) and you can specify different requirements for that deployment. This is assuming that (a) you at least have the jar for the ejb, and, (b) that the ejb in question is stand alone and doesn't have dependencies on other components, and (c) the developer of the ejb hasn't violated the idea of the declarative transactions and his bean works outside of a transaction context as well.
You can create another method with the suitable tx attribute and then call it with through the self-injected proxy (pseudo-code):
#Stateless
public class LocalEJB1 {
#EJB
private LocalEJB1 localEJB1;
#EJB
private AccountEJB accountEjb;
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public AccountInfo callNonTx() {
return accountEjb.getAccountInfo(userId, accountId);
}
public void yourCurrentMethod() {
// Begin transaction
UserTransaction tx = (UserTransaction) ctx.lookup(USER_TRANSACTION);
tx.begin();
AccountInfo account = localEJB1.callNonTx();
// Do some more database operations on XA datasource
// Commit transaction
tx.commit();
}
}

Categories

Resources