/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., 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 com.hp.mwtests.ts.jta.cdi.transactionScoped;
import org.junit.Assert;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.arjuna.ats.jta.common.jtaPropertyManager;
import javax.enterprise.context.ContextNotActiveException;
import javax.inject.Inject;
import javax.naming.InitialContext;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.*;
import static org.junit.Assert.assertTrue;
/**
* @author paul.robinson@redhat.com 01/05/2013
*/
@RunWith(Arquillian.class)
public class TransactionScopedTest {
@Inject
UserTransaction userTransaction;
@Inject
TestCDITransactionScopeBean testTxAssociationChangeBean;
@Inject
TestCDITransactionScopeBean2 testTxAssociationChangeBean2;
private static Set<String> timestamps = new TreeSet<String>();
@Deployment
public static JavaArchive createTestArchive() {
return ShrinkWrap.create(JavaArchive.class, "test.jar")
.addClass(TestCDITransactionScopeBean.class)
.addClass(TestCDITransactionScopeBean2.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
@After
public void tearDown() {
try {
userTransaction.rollback();
} catch (Exception e) {
// do nothing
}
}
private synchronized void addTimestamp(String message) {
timestamps.add(String.format("%d - %s (%s)%n", System.nanoTime(), Thread.currentThread().getId(), message));
}
//Based on test case from JTA 1.2 spec
private void testTxAssociationChange() throws Exception {
TransactionManager transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager(new InitialContext());
addTimestamp("begin1");
userTransaction.begin(); //tx1 begun
assertTrue(testTxAssociationChangeBean.isPostConstructCalled());
assertTrue(testTxAssociationChangeBean2.isPostConstructCalled());
testTxAssociationChangeBean.setValue(1);
testTxAssociationChangeBean2.setValue(10);
addTimestamp("suspend");
Transaction transaction = transactionManager.suspend();
assertContextUnavailable();
addTimestamp("begin2");
userTransaction.begin(); //tx2 begun
Assert.assertEquals(0, testTxAssociationChangeBean.getValue());
Assert.assertEquals(0, testTxAssociationChangeBean2.getValue());
testTxAssociationChangeBean.setValue(2);
testTxAssociationChangeBean2.setValue(20);
addTimestamp("commit1");
userTransaction.commit();
assertContextUnavailable();
addTimestamp("resume");
transactionManager.resume(transaction);
Assert.assertEquals(1, testTxAssociationChangeBean.getValue());
Assert.assertEquals(10, testTxAssociationChangeBean2.getValue());
addTimestamp("commit2");
userTransaction.commit();
assertContextUnavailable();
addTimestamp("finished");
}
@Test
// run the test sequentially to ensure that state from different scopes do not interfere with each other
public void testTxAssociationChangeSequentially() throws Exception {
final int TEST_RUNS = 2;
final int EXPECTED_MINIMUM_DESTROY_CNT = TEST_RUNS + TestCDITransactionScopeBean.getPreDestroyCnt();
// run the test multiple times (sequentially)
for (int i = 0; i < TEST_RUNS; i++) {
try {
testTxAssociationChange();
} catch (Exception e) {
throw new Exception("Sequential testTxAssociationChange failed with exception", e);
}
}
// ensure that preDestroy was called on the bean at least TEST_RUNS times
assertTrue(EXPECTED_MINIMUM_DESTROY_CNT <= TestCDITransactionScopeBean.getPreDestroyCnt());
}
@Test
// run the test concurrently to ensure that state from different scopes do not interfere with each other
public void testTxAssociationChangeConcurrently() throws Exception {
final int TEST_RUNS = 5;
final int EXPECTED_MINIMUM_DESTROY_CNT = TEST_RUNS + TestCDITransactionScopeBean.getPreDestroyCnt();
ExecutorService executor = Executors.newFixedThreadPool(TEST_RUNS);
Collection<Future<Boolean>> tasks = new ArrayList<Future<Boolean>>();
timestamps.clear();
// run the test multiple times (concurrently)
for (int i = 0; i < TEST_RUNS; i++) {
tasks.add(executor.submit(new Callable<Boolean>() {
public Boolean call() throws Exception {
testTxAssociationChange();
return true;
}
}));
}
// see if each individual test run completed successfully
for (Future<Boolean> task : tasks) {
try {
if (!task.get())
throw new Exception("Concurrent testTxAssociationChange failed");
} catch (Exception e) {
throw new Exception("Concurrent testTxAssociationChange failed with exception", e);
}
}
// ensure that preDestroy was called on the bean at least TEST_RUNS times
assertTrue(EXPECTED_MINIMUM_DESTROY_CNT <= TestCDITransactionScopeBean.getPreDestroyCnt());
for (String s : timestamps)
System.out.print(s);
}
private void assertContextUnavailable() {
try {
testTxAssociationChangeBean.getValue();
Assert.fail("Accessing bean should have thrown a ContextNotActiveException as it should not be available");
} catch (ContextNotActiveException e) {
//Expected
}
}
}