package org.infinispan.tx;
import static org.infinispan.test.TestingUtil.k;
import static org.infinispan.test.TestingUtil.v;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
import java.lang.reflect.Method;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.transaction.TransactionManager;
import org.infinispan.Cache;
import org.infinispan.IllegalLifecycleStateException;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.SingleCacheManagerTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.testng.annotations.Test;
/**
* Test that verifies that Cache.stop() waits for on-going transactions to
* finish before making the cache unavailable.
*
* Also verifies that new transactions started while the cache is stopping
* are not accepted.
*
* @author Galder ZamarreƱo
* @author Dan Berindei
* @since 4.2
*/
@Test(groups = "functional", testName = "tx.TerminatedCacheWhileInTxTest")
public class TerminatedCacheWhileInTxTest extends SingleCacheManagerTest {
@Override
protected EmbeddedCacheManager createCacheManager() throws Exception {
ConfigurationBuilder c = TestCacheManagerFactory.getDefaultCacheConfiguration(true);
c.transaction().cacheStopTimeout(10000);
return TestCacheManagerFactory.createCacheManager(c);
}
/**
* The aim of this test is to make sure that invocations not belonging to
* on-going transactions or non-transactional invocations are not allowed
* once the cache is in stopping mode.
*/
public void testNotAllowCallsWhileStopping(final Method m) throws Throwable {
cacheManager.defineConfiguration("cache-" + m.getName(), cacheManager.getDefaultCacheConfiguration());
final Cache<String, String> cache1 = cacheManager.getCache("cache-" + m.getName());
final CyclicBarrier barrier = new CyclicBarrier(2);
final CountDownLatch latch = new CountDownLatch(1);
final TransactionManager tm = TestingUtil.getTransactionManager(cache1);
Future<Void> waitAfterModFuture = fork(() -> {
log.debug("Wait for all executions paths to be ready to perform calls.");
tm.begin();
cache1.put(k(m, 1), v(m, 1));
log.debug("Cache modified, wait for cache to be stopped.");
barrier.await();
// Delay the commit, but it must still happen while cache.stop() is waiting for transactions
assertFalse(latch.await(5, TimeUnit.SECONDS));
tm.commit();
return null;
});
// wait for the transaction to have started
barrier.await();
Future<Void> callStoppingCacheFuture = fork(() -> {
log.debug("Wait very briefly and then make call.");
Thread.sleep(2000);
cache1.put(k(m, 2), v(m, 2));
return null;
});
cache1.stop(); // now stop the cache
latch.countDown(); // now that cache has been stopped, let the thread continue
waitAfterModFuture.get();
try {
callStoppingCacheFuture.get();
fail("Should have thrown an IllegalLifecycleStateException");
} catch (ExecutionException e) {
assertTrue(e.toString(), e.getCause() instanceof IllegalLifecycleStateException);
}
}
}