[jboss-user] [JBossCache] - Re: Problem with transaction timeout [critical]
jacek187
do-not-reply at jboss.com
Fri Jan 5 19:36:58 EST 2007
Your test is not correct - If rollback() or commit() from DummyTransactionManager is invoked in separate thread, DummyBaseTransactionManager can't find transaction stored in thread_local and IllegalStateException("thread not associated with transaction"); is throwed instead real rollback operation execution.
This is unit test that recreates this problem outside WebLogic. AsyncRollbackTransactionManager works like weblogic transaction manager.
Note: only rollbacks (and not all !!!!! Only timed out transactions and only if transaction was checked by internal transactionManager timer!) are executed in separate thread!
AsyncRollbackTxTest
| package org.jboss.cache.transaction;
|
| import javax.transaction.SystemException;
| import javax.transaction.TransactionManager;
|
| import junit.framework.TestCase;
|
| import org.jboss.cache.CacheException;
| import org.jboss.cache.Fqn;
| import org.jboss.cache.TreeCache;
|
| /**
| * Test behaviour of async rollback timeouted transaction
| *
| * @author <a href="mailto:jhalat at infovide.pl">Jacek Halat</a>
| * @since 1.4.0
| */
| public class AsyncRollbackTxTest extends TestCase
| {
| private TreeCache cache;
| private TransactionManager tm;
| private Fqn fqn = Fqn.fromString("/test");
|
| protected void setUp() throws Exception
| {
| cache = new TreeCache();
| cache.setTransactionManagerLookupClass("org.jboss.cache.transaction.AsyncRollbackTransactionManagerLookup");
| cache.startService();
| tm = cache.getTransactionManager();
| }
|
| protected void tearDown()
| {
| try
| {
| if (tm != null && tm.getTransaction() != null)
| {
| try
| {
| tm.rollback();
| }
| catch (SystemException e)
| {
| // do nothing
| }
| }
| }
| catch (SystemException e)
| {
| // do nothing
| }
| if (cache != null) cache.stopService();
| cache = null;
| tm = null;
| }
|
| public void testControl() throws Exception
| {
| assertEquals(0, cache.getNumberOfLocksHeld());
| tm.begin();
| cache.put(fqn, "k", "v");
| assertEquals(1, cache.getNumberOfLocksHeld());
| tm.rollback();
| assertEquals(0, cache.getNumberOfLocksHeld());
| }
|
| public void testRollbackInDifferentThread() throws Exception
| {
| tm.setTransactionTimeout(2);
| tm.begin();
| cache.put(fqn, "k", "v");
| assertEquals(1, cache.getNumberOfLocksHeld());
| Thread.sleep(5000);
| try{
| tm.commit();
| }catch(Exception e){
| e.printStackTrace();
| }
| assertEquals(0, cache.getNumberOfLocksHeld());
| cache.put(fqn, "k", "v");//Executed in Not transactional context
| // assertEquals(0, cache.getNumberOfLocksHeld());
| SeparateThread t = new SeparateThread();
| t.start();
| t.join();
| if (t.getException()!=null){
| throw t.getException();
| }
| }
| private class SeparateThread extends Thread{
| Exception e = null;
| public Exception getException() {
| return e;
| }
| @Override
| public void run() {
| try {
| cache.put(fqn, "k", "v");
| assertEquals(0, cache.getNumberOfLocksHeld());
| } catch (CacheException e) {
| this.e = e;
| }
| }
| };
| }
|
|
AsyncRollbackTransactionManager
| package org.jboss.cache.transaction;
|
| import java.util.HashMap;
| import java.util.Iterator;
| import java.util.Map;
| import java.util.Properties;
|
| import javax.naming.Context;
| import javax.naming.InitialContext;
| import javax.naming.NamingException;
| import javax.transaction.HeuristicMixedException;
| import javax.transaction.HeuristicRollbackException;
| import javax.transaction.InvalidTransactionException;
| import javax.transaction.NotSupportedException;
| import javax.transaction.RollbackException;
| import javax.transaction.SystemException;
| import javax.transaction.Transaction;
|
| public class AsyncRollbackTransactionManager extends DummyTransactionManager{
| static AsyncRollbackTransactionManager instance=null;
|
| public static DummyTransactionManager getInstance() {
| if(instance == null) {
| instance=new AsyncRollbackTransactionManager();
| try {
| Properties p=new Properties();
| p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
| Context ctx=new InitialContext(p);
| ctx.bind("java:/TransactionManager", instance);
| ctx.bind("UserTransaction", new DummyUserTransaction(instance));
| }
| catch(NamingException e) {
| log.error("binding of DummyTransactionManager failed", e);
| }
| }
| return instance;
| }
|
| private Thread timedOutTransactionsChecker = null;
| private int timeout = 30;
| private Map txMap = new HashMap();
| @Override
| public void setTransactionTimeout(int seconds) throws SystemException {
| this.timeout = seconds;
| }
| public AsyncRollbackTransactionManager(){
| timedOutTransactionsChecker = new TimedOutTransactionsChecker();
| timedOutTransactionsChecker.start();
| }
| private class TimedOutTransactionsChecker extends Thread{
| private boolean running;
| public TimedOutTransactionsChecker(){
| }
| public void run() {
| running = true;
| while (running){
| try {
| Thread.sleep(500);
| synchronized(this)
| {
| Iterator iterator = txMap.values().iterator();
| do
| {
| if(!iterator.hasNext())
| break;
| AsyncRollbackTransaction t = (AsyncRollbackTransaction)iterator.next();
| try {
| t.wakeUp();
| } catch (SystemException e) {
| e.printStackTrace();
| }
|
| } while(true);
| }
| } catch (InterruptedException e) {
| }
| }
| }
|
| }
| @SuppressWarnings("unchecked")
| @Override
| public void begin() throws NotSupportedException, SystemException {
| Transaction currentTx;
| if((currentTx=getTransaction()) != null)
| throw new NotSupportedException(Thread.currentThread() +
| " is already associated with a transaction (" + currentTx + ")");
| AsyncRollbackTransaction tx=new AsyncRollbackTransaction(this,timeout);
| setTransaction(tx);
| txMap.put(tx.generateTransactionId(), tx);
| }
| @Override
| public void rollback() throws IllegalStateException, SecurityException, SystemException {
| removeTxFromMap();
| super.rollback();
| }
| public void removeTxFromMap() throws SystemException {
| AsyncRollbackTransaction tx=(AsyncRollbackTransaction) getTransaction();
| if (tx!=null){
| txMap.remove(tx.getTransactionId());
| }
| }
| @Override
| public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {
| AsyncRollbackTransaction tx=(AsyncRollbackTransaction) getTransaction();
| if (tx!=null){
| txMap.remove(tx.getTransactionId());
| }
| super.commit();
| }
| @Override
| public void resume(Transaction tx) throws InvalidTransactionException, IllegalStateException, SystemException {
| //TODO Not implemented TX timeout counting
| super.resume(tx);
| }
| @Override
| public Transaction suspend() throws SystemException {
| //TODO Not implemented TX timeout counting
| return super.suspend();
| }
| }
|
|
|
AsyncRollbackTransactionManagerLookup
| package org.jboss.cache.transaction;
|
| import javax.transaction.TransactionManager;
|
| import org.jboss.cache.TransactionManagerLookup;
|
| public class AsyncRollbackTransactionManagerLookup implements TransactionManagerLookup{
| public TransactionManager getTransactionManager() throws Exception {
| return AsyncRollbackTransactionManager.getInstance();
| }
|
| }
|
AsyncRollbackTransaction
| package org.jboss.cache.transaction;
|
| import javax.transaction.SystemException;
|
|
| public class AsyncRollbackTransaction extends DummyTransaction {
| private static long transactionNums = 0;
|
| private long transactionId;
|
| private long beginTimeMillis;
|
| private int timeoutSec;
|
| public AsyncRollbackTransaction(DummyBaseTransactionManager tm, int timeout) {
| super(tm);
| this.timeoutSec = timeout;
| this.beginTimeMillis = System.currentTimeMillis();
| }
|
| /**
| * @return the transactionId
| */
| public long getTransactionId() {
| return transactionId;
| }
|
| public long generateTransactionId() {
| long result = 0;
| synchronized (AsyncRollbackTransaction.class) {
| transactionNums++;
| result = transactionNums;
| }
| this.transactionId = result;
| return result;
| }
|
| final int getTimeoutSeconds() {
| return timeoutSec;
| }
|
| protected final void asyncRollback() throws SystemException {
| Thread asyncRollbackThread = new Thread() {
| public void run() {
| try {
| rollback();
| } catch (Exception exception) {
| }
| }
| };
| ((AsyncRollbackTransactionManager)tm_).removeTxFromMap();
| asyncRollbackThread.start();
| }
|
| public void wakeUp() throws SystemException {
| if (isTransactionTimedOut()) {
| asyncRollback();
| }
| }
| private boolean isTransactionTimedOut() {
| return (System.currentTimeMillis() - beginTimeMillis) > (timeoutSec * 1000);
| }
| }
|
View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3998433#3998433
Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=3998433
More information about the jboss-user
mailing list