I'm entering the world of JTA, due to need of distributed transactions, and I'm uncertain about the differences between javax.jms.ConnectionFactory and javax.jms.XAConnectionFactory or more accurately how can it be that javax.jms.ConnectionFactory performed what I expected only javax.jms.XAConnectionFactory can do for me.
The details: I'm using Atomikos essentials as my transaction manager and my app is running on Apache Tomcat 6.
I'm running a small POC with a dummy app where I have my JMS provider (OpenMQ) registered as a JNDI resource.
<Resource name="jms/myConnectionFactory" auth="Container"
type="com.atomikos.jms.AtomikosConnectionFactoryBean"
factory="com.atomikos.tomcat.EnhancedTomcatAtomikosBeanFactory"
uniqueResourceName="jms/myConnectionFactory"
xaConnectionFactoryClassName="com.sun.messaging.XAConnectionFactory"
maxPoolSize="3"/>
And the strange issue is that in my code I do this:
Context ctx = new InitialContext();
ConnectionFactory queueConnectionFactory =
(ConnectionFactory)ctx.lookup("java:comp/env/jms/myQueueFactory");
javax.jms.Connection connection = queueConnectionFactory.createConnection();
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
And later in the code I use this session in a UserTransaction and it performs flawlessly with two MessageProducers with either Commit or Rollback.
What I don't understand is how can it be that I'm using javax.jms.XAConnectionFactory.createConnection() method and I get a Session which does the job? What's javax.jms.XAConnectionFactory role?
I'll also add that I've looked at the source code of both classes (and javax.jms.BasicConnectionFactory) and I verified that the XA class does not override createConnection.
The core of the difference between ConnectionFactory and XAConnectionFactory is that the XAConnectionFactory creates XAConnections which create XASessions. XASessions represent the real difference because (to quote from the JMS JavaDocs:)
The XASession interface extends the capability of Session by adding access to a JMS provider's support for the Java Transaction API (JTA) (optional). This support takes the form of a javax.transaction.xa.XAResource object.
In other words, the XASession is what gives the XA instances their transactional awareness. However, this specific implementation is optional, even for a fully compliant JMS provider. From the same JavaDoc:
An XAResource provides some fairly sophisticated facilities for interleaving work on multiple transactions, recovering a list of transactions in progress, and so on. A JTA aware JMS provider must fully implement this functionality. This could be done by using the services of a database that supports XA, or a JMS provider may choose to implement this functionality from scratch.
A client of the application server is given what it thinks is a regular JMS Session. Behind the scenes, the application server controls the transaction management of the underlying XASession.
In other words, the provider may require that you specify an XA or non-XA JMS resource, or, as would seem to be in your case, the provider may perform all the JTA plumbing transparently with what appears to be a regular JMS Session.
Actually, none of the example code you provided would exercise XA functionality. If all that is required is that your messages are under syncpoint, then you can get by with 1-phase commit (1PC). However if you want, for example, that JMS messages and DB updates occur in a single coordinated unit of work then you would use 2-phase commit (2PC) which is XA. Coordinating across two message producers on the same transport provider does not require XA 2PC.
If you were using 2PC, then in addition to COMMIT and ROLLBACK you would be calling BEGIN somewhere in the code. The lack of that verb in your example is why I said it looks like you are not doing 2PC. The BEGIN call would communicate with the transaction manager to establish a transaction context across the participating resource managers. Then the COMMIT would cause the messages and the DB updates to finalize in one unit of work. The interesting thing here is that if you have only one participating resource manager, some transports will silently optimize you back down to 1PC. In that case it looks as though you are doing 2PC but are really getting 1PC. Since there is only one resource manager there is no loss of reliability in this optimization.
On the other hand, if you are doing 1PC you won't see any difference between the two types of connection factory. It would exhibit exactly the behavior you describe.
The last case to consider is that you use ConnectionFactory and try to call BEGIN. Since the non-XA connection factory cannot participate in a coordinated XA transaction, this call should fail.
Related
Due to remote invocation nature of REST services, they are in constant situation to run into race condition with each other. One of the everyday resources to race for is session. In order to be practical, you need to be able to put a lock over the resource at the beginning of your process and lift it up whenever you are done with it.
Now my question is, does Spring Session have any feature to deal with race condition over the session entries?
Or any other library / framework in Java!!!
If you're using Spring Controllers, then you can use
RequestMappingHandlerAdapter.setSynchronizeOnSession-boolean-
This will make every Controller method synchronized in presence of a session.
HttpSession.setAttribute is thread safe. However getAttribute followed by setAttribute has to be manually made tread safe.
synchronized(session) {
session.setAttribute("foo", "bar");
session.getAttribute("foo");
}
Same can be done in case of spring session beans.
synchronized(session) {
//do something with the session bean
}
#Edit
In case of multiple containers with normal spring session beans you would have to use sticky sessions. That would ensure that one session state is stored on one container and that container is accessed every single time the same session is requested. This has to be done on the load balancer with the help of something like BigIP cookies. Rest would would work the same way as for a single session there exists a single container, so locking session would suffice.
If you would like to use session sharing across instances there are supports on the containers like Tomcat and Jetty
These approaches use a back-end database or some other persistence mechanism to store state.
For the same purpose you can try using Spring Session. Which is trivial to configure with the Redis. Since Redis is single threaded, it ensures that one instance of an entry is accessed atomically.
Above approaches are non invasive. Both the database and Redis based approaches support transactions.
However if you want more control over the distributed state and locking you can try using the distributed data grids like Hazelcast and Gemfire.
I have personally worked with the Hazelcast and it does provide methods to lock entries made in the map.
#Edit2
Though I believe that handling transactions should suffice with Spring Session and Redis, to make sure you would need distributed locking. Lock object would have to be acquired from the Redis itself. Since Redis is single threaded a personal implementation would also work by using something like INCR
Algorithm would go something like below
//lock_num is the semaphore/lock object
lock_count = INCR lock_num
while(true) {
if(lock_count != 1) {
DECR lock_num
} else {
break
}
wait(wait_time_period)
}
//do processing in critical section
DECR lock_num
However, thankfully Spring already provides this distributed lock implementation via RedisLockRegistry. More documentation on usage is here.
If you decide to use plain Jedis without spring then here is a distributed lock as for Jedis : Jedis Lock.
//from https://github.com/abelaska/jedis-lock
Jedis jedis = new Jedis("localhost");
JedisLock lock = new JedisLock(jedis, "lockname", 10000, 30000);
lock.acquire();
try {
// do some stuff
}
finally {
lock.release();
}
Both of these should work exactly like Hazelcast locking.
As a previous answer stated. If you are using Spring Session and you are concerned for thread safety on concurrent access of a Session, you should set:
RequestMappingHandlerAdapter.setSynchronizeOnSession(true);
One example can be found here EnableSynchronizeOnSessionPostProcessor :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
public class EnableSynchronizeOnSessionPostProcessor implements BeanPostProcessor {
private static final Logger logger = LoggerFactory
.getLogger(EnableSynchronizeOnSessionPostProcessor.class);
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// NO-OP
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
logger.info("enable synchronizeOnSession => {}", adapter);
adapter.setSynchronizeOnSession(true);
}
return bean;
}
}
Sticky Sessions and Session Replication
With regards to a clustered application and Sessions, there is a very good post here on SO, that discusses this topic: Sticky Sessions and Session Replication
In my experience, you would want both Sticky Session and Session replication.
You use sticky session to eliminate the concurrent Session access across nodes, because sticky session will pin a session to a single node and each subsequent request for the same session will always be directed to that node. This eliminates the cross-node session access concern.
Replicated sessions are helpful mainly in case a node goes down. By replicating sessions, when a node goes down, future requests for existing sessions will be directed to another node that will have a copy of the original session and makes the fail over transparent to the user.
There are many frameworks that support session replication. The one I use for large projects is the open-source Hazelcast.
In response to your comments made on #11thdimension post:
I think you are in a bit of a challenging area. Basically, you want to enforce all session operations to be atomic across nodes in a cluster. This leads me to lean towards a common session store across nodes, where access is synchronized (or something similar).
Multiple Session store / replication frameworks surely support an external store concept and I am sure Reddis does. I am most familiar with Hazelcast and will use that as an example.
Hazelcast allows to configure the session persistence to use a common database.
If you look at Map Persistence section, it shows an example and a description of options.
The description for the concept states:
Hazelcast allows you to load and store the distributed map entries from/to a persistent data store such as a relational database. To do this, you can use Hazelcast's MapStore and MapLoader interfaces.
Data store needs to be a centralized system that is accessible from all Hazelcast Nodes. Persistence to local file system is not supporte
Hazelcast supports read-through, write-through, and write-behind persistence modes which are explained in below subsections.
The interesting mode is write-through:
Write-Through
MapStore can be configured to be write-through by setting the write-delay-seconds property to 0. This means the entries will be put to the data store synchronously.
In this mode, when the map.put(key,value) call returns:
MapStore.store(key,value) is successfully called so the entry is persisted.
In-Memory entry is updated.
In-Memory backup copies are successfully created on other JVMs (if backup-count is greater than 0).
The same behavior goes for a map.remove(key) call. The only difference is that MapStore.delete(key) is called when the entry will be deleted.
I think, using this concept, plus setting up your database tables for the store properly to lock entries on insert/update/deletes, you can accomplish what you want.
Good Luck!
I struggeling with JTA, two-phase-commit, JMS- and JDBC-transactions. The idea is (in short) to
receive a message on a queue
perform some database operations
acknowledge the message, when the db operations have been successful
So I got the XAQueueConnectionFactory, create the XAQueueSession, create a receiver from the session and set a message listener.
Inside the listener, in the onMessage method, I begin my user transaction, do the jdbc stuff and commit the transaction or do a rollback, if something went wrong. Now I expected (aka "hoped") that the message would be acknowledged, when the user transaction commits.
But that doesn't happen, the messages are still on the queue and get redelivered again and again.
What am I missing? I double-checked the session and the acknowledge mode really is "SESSION_TRANSACTED" and getTransacted returns true.
I don't have a Java EE container, no spring, no message driven beans. I use the standalone JTA bitronix.
You don't really need XA for this. Just following your algorithm: receive the message, perform the DB operations, then acknowledge the message... Literally, that's the solution. (And instead a transacted session, you probably would just choose explicit CLIENT_ACKNOWLEDGE.) If your application should fail while performing the DB operations, don't ack the JMS msg and it will be redelivered. If your app fails after the DB txn and before the ack, then the message will be redelivered -- but you can detected this (redelivered flag will be set to true on the message), and you can decide to reprocess the message or not, based on the state of the database.
When you say that inside the listener, you begin your user transaction, this seems to hint that you are using Bean Managed Transaction (BMT). Is there a good reason for doing so?
If you used Container Managed Transaction (CMT), what you want would come for free.
As far as I remember, it is not possible with BMT, since the UserTransaction will not participate and will not be able to participate in the transaction created for the message. But you might want to double check with the Java EE spec.
Edit:
Sorry, I realized too late that you are not using a Java EE container.
Are you sure that the user transaction that you start inside the listener is part of the transaction started for the message? It seems that you start an independent transaction for the db work.
If you use no container, who provides the JMS implementation, i.e. XAQueueConnectionFactory etc?
I think with XA you shoulndn't use transacted session.
I have a Java SE(!) scenario with JMS and JPA where I might need distributed transactions as well as "regular" JDBC transactions. I have to listen to a queue which sends service requests, persist log on receiving, process the request and update the log after the request has been processed. The message shall only be acknowledged if the request has been processed successfully.
The first idea was to only use JTA (provided by Bitronix). But there I face two problems:
no log will be persisted if the request can't be processed
the request won't be processed if the log can't be updated (unlikely but yet possible)
So the other idea is to create and update the log with regular JDBC transactions. Only the entitymanager(s) for the request transaction(s) would join the user transactions and the entity managers for creating and updating the log would commit directly.
Is it possible to "mix" JTA and JPA on a single persistence unit? Or do we already have patterns for those kinds of JMS and JDBC transactions?
I actually solved my problem with a slightly different approach. Instead of "mixing" JTA and JDBC transactions I used suspend and resume to work with different user transaction.
The task is still the same: I start a (JTA) user transaction that contains some JMS and JDBC transactions (receiving a message, performing some database operations). And in the middle of that workflow, I want to write a message log but that logging shall not be rolled back when the "outer" transaction fails.
So the solution is, in pseudo code:
transactionManager.begin()
doSomeJdbcStuff();
Transaction main = transactionManager.suspend();
// do the logging
transactionManager.begin() // <- now a new transaction is created and active!
doSomeLogging();
transactionManager.commit()
// continue
transactionManager.resume(main);
doSomeMoreJdbcStuff();
transactionManager.commit();
Context of problem I want to solve: I have a java spring http interceptor AuditHttpCommunicationInterceptor that audits communication with an external system. The HttpClieant that does the communication is used in a java service class that does some business logic called DoBusinessLogicSevice.
The DoBusinessLogicSevice opens a new transaction and using couple of collaborators does loads of stuff.
Problem to solove: Regardless of the outcome of any of the operations in DoBusinessLogicSevice (unexpected Exceptions, etc) I want audits to be stored in the database by AuditHttpCommunicationInterceptor.
Solution I used: The AuditHttpCommunicationInterceptor will open a new transaction this way:
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
new TransactionTemplate(platformTransactionManager, transactionDefinition).execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// do stuff
}
});
Everything works fine. When a part of DoBusinessLogicSevice throws unexpected exception its transaction is rolled back, but the AuditHttpCommunicationInterceptor manages to store the audit in the database.
Problem that arises from this solution: AuditHttpCommunicationInterceptor uses a new db connection. So for every DoBusinessLogicSevice call I need 2 db connections.
Basicly, I want to know the solution to the problem: how to make TransactionTemplate "suspend" the current transaction and reuse the connection for a new one in this case.
Any ideas? :)
P.S.
One idea might be to take a different design approach: drop the interceptor and create an AuditingHttpClient that is used in DoBusinessLogicSevice directly (not invoked by spring) but I cannot do that because I cannot access all http fields in there.
Spring supports nested transactions (propagation="NESTED"), but this really depends on the database platform, and I don't believe every database platform is capable of handling nested transactions.
I really don't see what's a big deal with taking connection from a pool, doing a quick audit transaction and returning connection back.
Update: While Spring supports nested transactions, it looks like Hibernate doesn't. If that's the case, I say: go with another connection for audit.
Does anyone have a good tutorial or some advice on how to implement one's own XAResource? I need Spring's MailSender to be transactional, so that the mail will only be sent once the transaction commits, and it seems there isn't any existing transactional wrapper.
If you just need to wait for the commit, as you say in a comment, you can investigate using TransactionSynchronizationManager.registerSynchronization() to trigger email sending on commit.
You can use a TransactionSynchronizationManager.registerSynchronization (like gpeche mentioned) with a TransactionSynchronizationAdapter which has a variety of methods that are called at various stages of the current transaction. I think the most suitable method for the question is the afterCommit.
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
super.afterCommit();
sendEmail();
}
});
I doubt that it's possible to implement true XAResource for SMTP. There should be transaction support on the resource manager (SMTP server in this case) and I don't believe there are any. I would say your best bet is 'Last resource commit' pattern - which allows one non XA resource participate in XA transaction. Search Google, there are plenty of info. Most Java EE servers supports this.
One other option next to the one mentioned by gpeche, is sending a transactional JMS message from within the transaction. Then let the message listener (like e.g. a MDB bean) send the email.
Another trick in EJB is scheduling a timer from within a transaction. The timer is also transactional and will only be started when the transaction commits. Simply use a timer with timeout = 0, so it will start immediately after the transaction commits.