/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.jca.test; import javax.management.ObjectName; import javax.transaction.RollbackException; import junit.framework.Test; import org.jboss.test.JBossTestCase; import org.jboss.test.jca.mbean.MTOperation; /** * Multithreaded Tx JCA tests * * @author <a href="dimitris@jboss.org">Dimitris Andreadis</a> * @version $Revision: 81036 $ */ public class MultiThreadedTxUnitTestCase extends JBossTestCase { static String[] SIG = new String[] { String.class.getName(), new MTOperation[0][0].getClass().getName() }; ObjectName mtMBean; public MultiThreadedTxUnitTestCase(String name) { super(name); try { mtMBean = new ObjectName("jboss.test:test=MultiThreadedTxUnitTestCase"); } catch (Exception e) { throw new RuntimeException(e.toString()); } } public void runTest(MTOperation[][] ops) throws Exception { getServer().invoke(mtMBean, "testMTOperations", new Object[] { getName(), ops }, SIG); } public static Test suite() throws Exception { Test t1 = getDeploySetup(MultiThreadedTxUnitTestCase.class, "mtjcatest.sar"); Test t2 = getDeploySetup(t1, "mttestadapter-ds.xml"); return getDeploySetup(t2, "jbosstestadapter.rar"); } /** * Have thread0 control the tx and thread1 joining the tx. * Both threads get/enlist a connection and close it and * thread1 suspends the tx. Thread0 commits. */ public void testEnlistConnsInSameTxButDifferentThreads() throws Exception { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 999), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.TM_COMMIT) } , { // thread 1 new MTOperation(MTOperation.CF_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.TM_SUSPEND), new MTOperation(MTOperation.XX_POST_SIGNAL, 999) } }); } /** * Same like testEnlistConnsInSameTxButDifferentThreads() * but use a TrackByTx connection factory. */ public void testEnlistConnsInSameTxButDifferentThreadsTrackByTx() throws Exception { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 999), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.TM_COMMIT) } , { // thread 1 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.TM_SUSPEND), new MTOperation(MTOperation.XX_POST_SIGNAL, 999) } }); } /** * Same like testEnlistConnsInSameTxButDifferentThreadsTrackByTx() * but don't suspend in the second thread. It the transaction * reconsiliation policy of allowing only one associated thread * at commit time is active, we should get an exception. */ public void testEnlistConnsInSameTxButDifferentThreadsTrackByTxDontSuspend() throws Exception { Boolean txSyncActive = new Boolean(false); try { ObjectName target = new ObjectName("jboss:service=TransactionSynchronization"); txSyncActive = (Boolean)getServer().getAttribute(target, "EnforceOneThreadActiveAtCommit"); } catch (Exception e) { // ignore } if (txSyncActive.booleanValue() == true) { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 999), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.TM_COMMIT, -1, new RollbackException()) } , { // thread 1 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.XX_POST_SIGNAL, 999) } }); } else { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 999), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.TM_COMMIT) } , { // thread 1 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.XX_POST_SIGNAL, 999) } }); } } /** * Have 3 threads getting connection both in the same * and in different transactions, or not tx. */ public void testEnlistInSameOrDifferentOrNoTx() throws Exception { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 888), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 999), new MTOperation(MTOperation.TM_COMMIT) } , { // thread 1 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.TM_SUSPEND, 10), new MTOperation(MTOperation.TM_BEGIN, 20), new MTOperation(MTOperation.CF_GET_CONN, 3), new MTOperation(MTOperation.CN_CLOSE_CONN, 3), new MTOperation(MTOperation.TM_COMMIT), new MTOperation(MTOperation.XX_POST_SIGNAL, 888) } , { // thread 2 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 4), new MTOperation(MTOperation.CN_CLOSE_CONN, 4), new MTOperation(MTOperation.TM_SUSPEND, 10), new MTOperation(MTOperation.CF_GET_CONN, 5), new MTOperation(MTOperation.CN_CLOSE_CONN, 5), new MTOperation(MTOperation.XX_POST_SIGNAL, 999) } }); } /** * Enlist the first connection (which is tracked by tx) * in a different thread from the one that originally * started the transaction. */ public void testEnlistConnInOtherThreadThanTxBegun() throws Exception { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 888), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 999), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.TM_COMMIT) } , { // thread 1 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.XX_POST_SIGNAL, 888), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.TM_SUSPEND, 10), new MTOperation(MTOperation.XX_POST_SIGNAL, 999) } }); } /** * Simple test to just show in the logs how connections * are reused when track-by-tx is true */ public void testShowConnReuseTrackByTx() throws Exception { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.CF_GET_CONN, 3), new MTOperation(MTOperation.CN_CLOSE_CONN, 3), new MTOperation(MTOperation.CF_GET_CONN, 4), new MTOperation(MTOperation.CF_GET_CONN, 5), new MTOperation(MTOperation.CF_GET_CONN, 6), new MTOperation(MTOperation.CN_CLOSE_CONN, 6), new MTOperation(MTOperation.CN_CLOSE_CONN, 5), new MTOperation(MTOperation.CN_CLOSE_CONN, 4), new MTOperation(MTOperation.CF_GET_CONN, 7), new MTOperation(MTOperation.CF_GET_CONN, 8), new MTOperation(MTOperation.CF_GET_CONN, 9), new MTOperation(MTOperation.CN_CLOSE_CONN, 7), new MTOperation(MTOperation.CN_CLOSE_CONN, 8), new MTOperation(MTOperation.CN_CLOSE_CONN, 9), new MTOperation(MTOperation.TM_COMMIT) } }); } /** * Simple test to just show in the logs how connections * are reused when track-by-tx is false */ public void testShowConnReuse() throws Exception { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.CF_GET_CONN, 2), new MTOperation(MTOperation.CN_CLOSE_CONN, 2), new MTOperation(MTOperation.CF_GET_CONN, 3), new MTOperation(MTOperation.CN_CLOSE_CONN, 3), new MTOperation(MTOperation.CF_GET_CONN, 4), new MTOperation(MTOperation.CF_GET_CONN, 5), new MTOperation(MTOperation.CF_GET_CONN, 6), new MTOperation(MTOperation.CN_CLOSE_CONN, 6), new MTOperation(MTOperation.CN_CLOSE_CONN, 5), new MTOperation(MTOperation.CN_CLOSE_CONN, 4), new MTOperation(MTOperation.CF_GET_CONN, 7), new MTOperation(MTOperation.CF_GET_CONN, 8), new MTOperation(MTOperation.CF_GET_CONN, 9), new MTOperation(MTOperation.CN_CLOSE_CONN, 7), new MTOperation(MTOperation.CN_CLOSE_CONN, 8), new MTOperation(MTOperation.CN_CLOSE_CONN, 9), new MTOperation(MTOperation.TM_COMMIT) } }); } /** * Close the connection inside a different transaction */ public void testCloseConnectionInDifferentTx() throws Exception { runTest(new MTOperation[][] { { // thread 0 new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 10), new MTOperation(MTOperation.CF_GET_CONN, 1), new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, 999), new MTOperation(MTOperation.TM_COMMIT) } , { // thread 1 new MTOperation(MTOperation.TM_BEGIN, 20), new MTOperation(MTOperation.XX_WAIT_FOR_CONN, 1), new MTOperation(MTOperation.CN_CLOSE_CONN, 1), new MTOperation(MTOperation.XX_POST_SIGNAL, 999), new MTOperation(MTOperation.TM_COMMIT) } }); } /** * Thread0 begins a tx, creates a connection and waits. * N Threads resume thead0 tx and create and destroy 3 * connections each. Thread0 waits for them and commits the tx. */ public void testStressConnsMultipleThreadsInSameTx() throws Exception { final int numThreads = 60; MTOperation[][] stressTest = new MTOperation[numThreads + 1][]; // thread 0 MTOperation[] thread0 = new MTOperation[5 + numThreads]; thread0[0] = new MTOperation(MTOperation.CF_BY_TX_LOOKUP); thread0[1] = new MTOperation(MTOperation.TM_BEGIN, 10); thread0[2] = new MTOperation(MTOperation.CF_GET_CONN, 0); for (int i = 0; i < numThreads; i++) { thread0[3+i] = new MTOperation(MTOperation.XX_WAIT_FOR_SIGNAL, i+1); } thread0[3 + numThreads] = new MTOperation(MTOperation.CN_CLOSE_CONN, 0); thread0[4 + numThreads] = new MTOperation(MTOperation.TM_COMMIT); stressTest[0] = thread0; // threads 1 -> numThreads for (int i = 1; i <= numThreads; i++) { stressTest[i] = new MTOperation[] { new MTOperation(MTOperation.CF_BY_TX_LOOKUP), new MTOperation(MTOperation.XX_WAIT_FOR_TX, 10), new MTOperation(MTOperation.TM_RESUME, 10), new MTOperation(MTOperation.CF_GET_CONN, 1000+i), new MTOperation(MTOperation.XX_SLEEP_RANDOM), new MTOperation(MTOperation.CF_GET_CONN, 2000+i), new MTOperation(MTOperation.XX_SLEEP_RANDOM), new MTOperation(MTOperation.CF_GET_CONN, 3000+i), new MTOperation(MTOperation.XX_SLEEP_RANDOM), new MTOperation(MTOperation.CN_CLOSE_CONN, 3000+i), new MTOperation(MTOperation.XX_SLEEP_RANDOM), new MTOperation(MTOperation.CN_CLOSE_CONN, 2000+i), new MTOperation(MTOperation.XX_SLEEP_RANDOM), new MTOperation(MTOperation.CN_CLOSE_CONN, 1000+i), new MTOperation(MTOperation.TM_SUSPEND, 10), new MTOperation(MTOperation.XX_POST_SIGNAL, i) }; } runTest(stressTest); } /** * Create multiple threads that get and close connections * within different transactions. */ public void testStressMultipleThreadsDifferentTx() throws Exception { final int numThreads = 60; MTOperation[][] stressTest = new MTOperation[numThreads][]; // threads 0 -> numThreads for (int i = 0; i < numThreads; i++) { stressTest[i] = new MTOperation[] { new MTOperation(MTOperation.CF_LOOKUP), new MTOperation(MTOperation.TM_BEGIN, 500+i), new MTOperation(MTOperation.CF_GET_CONN, 1000+i), new MTOperation(MTOperation.XX_SLEEP_RANDOM), // Uncomment to following to run out of connections! // new MTOperation(MTOperation.CF_GET_CONN, 2000+i), // new MTOperation(MTOperation.XX_SLEEP_RANDOM), // new MTOperation(MTOperation.CN_CLOSE_CONN, 2000+i), // new MTOperation(MTOperation.XX_SLEEP_RANDOM), new MTOperation(MTOperation.CN_CLOSE_CONN, 1000+i), new MTOperation(MTOperation.TM_COMMIT) }; } runTest(stressTest); } }