/* * JBoss, Home of Professional Open Source * Copyright 2009 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt 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.infinispan.test; import org.infinispan.AdvancedCache; import org.infinispan.Cache; import org.infinispan.affinity.KeyAffinityService; import org.infinispan.affinity.KeyAffinityServiceFactory; import org.infinispan.affinity.RndKeyGenerator; import org.infinispan.config.Configuration; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.distribution.MagicKey; import org.infinispan.distribution.rehash.XAResourceAdapter; import org.infinispan.manager.CacheContainer; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.remoting.transport.Address; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.test.fwk.TransportFlags; import org.infinispan.transaction.TransactionTable; import org.infinispan.util.concurrent.locks.LockManager; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static junit.framework.Assert.assertEquals; /** * Base class for tests that operates on clusters of caches. The way tests extending this class operates is: * <pre> * 1) created cache managers before tests start. The cache managers are only created once * 2) after each test method runs, the cache instances are being cleared * 3) next test method will run on same cacheManager instance. This way the test is much faster, as CacheManagers * are expensive to create. * </pre> * If, however, you would like your cache managers destroyed after every <i>test method</i> instead of the </i>test * class</i>, you could set the <tt>cleanup</tt> field to {@link MultipleCacheManagersTest.CleanupPhase#AFTER_METHOD} in * your test's constructor. E.g.: * <pre> * <p/> * public void MyTest extends MultipleCacheManagersTest { * public MyTest() { * cleanup = CleanupPhase.AFTER_METHOD; * } * } * <p/> * </pre> * <p/> * Note that this will cuse {@link #createCacheManagers()} to be called befpre each method. * * @author Mircea.Markus@jboss.com */ public abstract class MultipleCacheManagersTest extends AbstractCacheTest { protected List<EmbeddedCacheManager> cacheManagers = new ArrayList<EmbeddedCacheManager>(); protected IdentityHashMap<Cache, ReplListener> listeners = new IdentityHashMap<Cache, ReplListener>(); @BeforeClass (alwaysRun = true) public void createBeforeClass() throws Throwable { if (cleanupAfterTest()) callCreateCacheManagers(); } private void callCreateCacheManagers() throws Throwable { try { log.debug("Creating cache managers"); createCacheManagers(); log.debug("Cache managers created, ready to start the test"); } catch (Throwable th) { log.error("Error in test setup: ", th); throw th; } } @BeforeMethod(alwaysRun = true) public void createBeforeMethod() throws Throwable { if (cleanupAfterMethod()) callCreateCacheManagers(); } @AfterClass(alwaysRun = true) protected void destroy() { if (cleanupAfterTest()) TestingUtil.killCacheManagers(cacheManagers); cacheManagers.clear(); listeners.clear(); } @AfterMethod(alwaysRun=true) protected void clearContent() throws Throwable { if (cleanupAfterTest()) { // assertSupportedConfig(); log.debug("*** Test method complete; clearing contents on all caches."); if (cacheManagers.isEmpty()) throw new IllegalStateException("No caches registered! Use registerCacheManager(Cache... caches) to do that!"); TestingUtil.clearContent(cacheManagers); } else { TestingUtil.killCacheManagers(cacheManagers); cacheManagers.clear(); } } /** * Reason: after a tm.commit is run, multiple tests assert that the new value (as within the committing transaction) * is present on a remote cache (i.e. not on the cache on which tx originated). If we don't use sync commit, * than this (i.e. actual commit of the tx on the remote cache) might happen after the tm.commit() returns, * and result in an intermittent failure for the assertion */ protected void assertSupportedConfig() { for (EmbeddedCacheManager cm : cacheManagers) { for (Cache cache : TestingUtil.getRunningCaches(cm)) { Configuration config = cache.getConfiguration(); try { assert config.isSyncCommitPhase() : "Must use a sync commit phase!"; assert config.isSyncRollbackPhase(): "Must use a sync rollback phase!"; } catch (AssertionError e) { log.error("Invalid config for cache in test: " + getClass().getName()); throw e; } } } } final protected void registerCacheManager(CacheContainer... cacheContainers) { for (CacheContainer ecm : cacheContainers) { this.cacheManagers.add((EmbeddedCacheManager) ecm); } } /** * Creates a new cache manager, starts it, and adds it to the list of known cache managers on the current thread. * Uses a default clustered cache manager global config. * * @return the new CacheManager */ protected EmbeddedCacheManager addClusterEnabledCacheManager() { return addClusterEnabledCacheManager(new TransportFlags()); } /** * Creates a new cache manager, starts it, and adds it to the list of known * cache managers on the current thread. Uses a default clustered cache * manager global config. * * @param flags properties that allow transport stack to be tweaked * @return the new CacheManager */ protected EmbeddedCacheManager addClusterEnabledCacheManager(TransportFlags flags) { EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(flags); cacheManagers.add(cm); return cm; } /** * Creates a new non-transactional cache manager, starts it, and adds it to the list of known cache managers on the * current thread. Uses a default clustered cache manager global config. * * @param defaultConfig default cfg to use * @return the new CacheManager * @deprecated Use {@link #addClusterEnabledCacheManager( * org.infinispan.configuration.cache.ConfigurationBuilder)} instead */ @Deprecated protected EmbeddedCacheManager addClusterEnabledCacheManager(Configuration defaultConfig) { return addClusterEnabledCacheManager(defaultConfig, new TransportFlags()); } protected EmbeddedCacheManager addClusterEnabledCacheManager(ConfigurationBuilder defaultConfig) { return addClusterEnabledCacheManager(defaultConfig, new TransportFlags()); } /** * Creates a new optionally transactional cache manager, starts it, and adds it to the list of known cache managers on * the current thread. Uses a default clustered cache manager global config. * * @param defaultConfig default cfg to use * @return the new CacheManager * @deprecacted Use {@link #addClusterEnabledCacheManager( * org.infinispan.configuration.cache.ConfigurationBuilder, org.infinispan.test.fwk.TransportFlags)} instead */ @Deprecated protected EmbeddedCacheManager addClusterEnabledCacheManager(Configuration defaultConfig, TransportFlags flags) { EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(defaultConfig, flags); cacheManagers.add(cm); return cm; } protected EmbeddedCacheManager addClusterEnabledCacheManager(ConfigurationBuilder builder, TransportFlags flags) { EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(builder, flags); cacheManagers.add(cm); return cm; } /** * Creates a new cache manager, starts it, and adds it to the list of known cache managers on the current thread. * @param mode cache mode to use * @param transactional if true, the configuration will be decorated with necessary transactional settings * @return an embedded cache manager */ protected EmbeddedCacheManager addClusterEnabledCacheManager(Configuration.CacheMode mode, boolean transactional) { return addClusterEnabledCacheManager(mode, transactional, new TransportFlags()); } protected EmbeddedCacheManager addClusterEnabledCacheManager(Configuration.CacheMode mode, boolean transactional, TransportFlags flags) { Configuration configuration = getDefaultClusteredConfig(mode, transactional); return addClusterEnabledCacheManager(configuration, flags); } protected void createCluster(Configuration.CacheMode mode, boolean transactional, int count) { for (int i = 0; i < count; i++) addClusterEnabledCacheManager(mode, transactional); } protected void createCluster(ConfigurationBuilder builder, int count) { for (int i = 0; i < count; i++) addClusterEnabledCacheManager(builder); } protected void createCluster(Configuration config, int count) { for (int i = 0; i < count; i++) addClusterEnabledCacheManager(config); } protected void createCluster(Configuration.CacheMode mode, int count) { for (int i = 0; i < count; i++) addClusterEnabledCacheManager(mode, true); } protected void defineConfigurationOnAllManagers(String cacheName, Configuration c) { for (EmbeddedCacheManager cm : cacheManagers) { cm.defineConfiguration(cacheName, c); } } protected void defineConfigurationOnAllManagers(String cacheName, ConfigurationBuilder b) { for (EmbeddedCacheManager cm : cacheManagers) { cm.defineConfiguration(cacheName, b.build()); } } private List<Cache> getCaches(String cacheName) { List<Cache> caches; caches = new ArrayList<Cache>(); for (EmbeddedCacheManager cm : cacheManagers) { caches.add(cacheName == null ? cm.getCache() : cm.getCache(cacheName)); } return caches; } protected void waitForClusterToForm(String cacheName) { List<Cache> caches; caches = getCaches(cacheName); Cache<Object, Object> cache = caches.get(0); TestingUtil.blockUntilViewsReceived(10000, caches); if (cache.getConfiguration().getCacheMode().isDistributed()) { TestingUtil.waitForRehashToComplete(caches); } } protected void waitForClusterToForm() { waitForClusterToForm((String) null); } protected void waitForClusterToForm(String... names) { for (String name : names) { waitForClusterToForm(name); } } protected TransactionManager tm(Cache<?, ?> c) { return c.getAdvancedCache().getTransactionManager(); } protected TransactionManager tm(int i, String cacheName) { return cache(i, cacheName ).getAdvancedCache().getTransactionManager(); } protected TransactionManager tm(int i) { return cache(i).getAdvancedCache().getTransactionManager(); } protected Transaction tx(int i) { try { return cache(i).getAdvancedCache().getTransactionManager().getTransaction(); } catch (SystemException e) { throw new RuntimeException(e); } } /** * @deprecated Use {@link #createClusteredCaches( * int, String, org.infinispan.configuration.cache.ConfigurationBuilder)} instead */ @Deprecated protected <K, V> List<Cache<K, V>> createClusteredCaches( int numMembersInCluster, String cacheName, Configuration c) { return createClusteredCaches(numMembersInCluster, cacheName, c, new TransportFlags()); } protected <K, V> List<Cache<K, V>> createClusteredCaches( int numMembersInCluster, String cacheName, ConfigurationBuilder builder) { return createClusteredCaches(numMembersInCluster, cacheName, builder, new TransportFlags()); } /** * @deprecated Use {@link #createClusteredCaches( * int, String, org.infinispan.configuration.cache.ConfigurationBuilder, org.infinispan.test.fwk.TransportFlags)} instead */ @Deprecated protected <K, V> List<Cache<K, V>> createClusteredCaches( int numMembersInCluster, String cacheName, Configuration c, TransportFlags flags) { List<Cache<K, V>> caches = new ArrayList<Cache<K, V>>(numMembersInCluster); for (int i = 0; i < numMembersInCluster; i++) { EmbeddedCacheManager cm = addClusterEnabledCacheManager(flags); cm.defineConfiguration(cacheName, c); Cache<K, V> cache = cm.getCache(cacheName); caches.add(cache); } waitForClusterToForm(cacheName); return caches; } protected <K, V> List<Cache<K, V>> createClusteredCaches( int numMembersInCluster, String cacheName, ConfigurationBuilder builder, TransportFlags flags) { List<Cache<K, V>> caches = new ArrayList<Cache<K, V>>(numMembersInCluster); for (int i = 0; i < numMembersInCluster; i++) { EmbeddedCacheManager cm = addClusterEnabledCacheManager(flags); cm.defineConfiguration(cacheName, builder.build()); Cache<K, V> cache = cm.getCache(cacheName); caches.add(cache); } waitForClusterToForm(cacheName); return caches; } protected <K, V> List<Cache<K, V>> createClusteredCaches(int numMembersInCluster, Configuration defaultConfig) { List<Cache<K, V>> caches = new ArrayList<Cache<K, V>>(numMembersInCluster); for (int i = 0; i < numMembersInCluster; i++) { EmbeddedCacheManager cm = addClusterEnabledCacheManager(defaultConfig); Cache<K, V> cache = cm.getCache(); caches.add(cache); } waitForClusterToForm(); return caches; } protected ReplListener replListener(Cache cache) { ReplListener listener = listeners.get(cache); if (listener == null) { listener = new ReplListener(cache); listeners.put(cache, listener); } return listener; } protected EmbeddedCacheManager manager(int i) { return cacheManagers.get(i); } protected <K, V> Cache<K, V> cache(int managerIndex, String cacheName) { return manager(managerIndex).getCache(cacheName); } protected void assertClusterSize(String message, int size) { for (EmbeddedCacheManager cm : cacheManagers) { assert cm.getMembers() != null && cm.getMembers().size() == size : message; } } protected void removeCacheFromCluster(String cacheName) { for (EmbeddedCacheManager cm : cacheManagers) { TestingUtil.killCaches(cm.getCache(cacheName)); } } /** * Returns the default cache from that manager. */ protected <A, B> Cache<A, B> cache(int index) { return manager(index).getCache(); } /** * Create the cache managers you need for your test. Note that the cache managers you create *must* be created using * {@link #addClusterEnabledCacheManager()} */ protected abstract void createCacheManagers() throws Throwable; protected Address address(int cacheIndex) { return manager(cacheIndex).getAddress(); } protected AdvancedCache advancedCache(int i) { return cache(i).getAdvancedCache(); } protected AdvancedCache advancedCache(int i, String cacheName) { return cache(i, cacheName).getAdvancedCache(); } protected <K, V> List<Cache<K, V>> caches(String name) { List<Cache<K, V>> result = new ArrayList<Cache<K, V>>(); for (EmbeddedCacheManager ecm : cacheManagers) { Cache<K, V> c; if (name == null) c = ecm.getCache(); else c = ecm.getCache(name); result.add(c); } return result; } protected <K, V> List<Cache<K, V>> caches() { return caches(null); } protected Address address(Cache c) { return c.getAdvancedCache().getRpcManager().getAddress(); } protected LockManager lockManager(int i) { return TestingUtil.extractLockManager(cache(i)); } protected LockManager lockManager(int i, String cacheName) { return TestingUtil.extractLockManager(getCache(i, cacheName)); } public List<EmbeddedCacheManager> getCacheManagers() { return cacheManagers; } /** * Kills the cache manager with the given index and waits for the new cluster to form. */ protected void killMember(int cacheIndex) { List<Cache<Object, Object>> caches = caches(); caches.remove(cacheIndex); manager(cacheIndex).stop(); TestingUtil.blockUntilViewsReceived(60000, false, caches.toArray(new Cache[0])); } /** * Creates a {@link org.infinispan.affinity.KeyAffinityService} and uses it for generating a key that maps to the given address. * @param nodeIndex the index of tha cache where to be the main data owner of the returned key */ protected Object getKeyForCache(int nodeIndex) { final Cache<Object, Object> cache = cache(nodeIndex); return getKeyForCache(cache); } protected Object getKeyForCache(int nodeIndex, String cacheName) { final Cache<Object, Object> cache = cache(nodeIndex, cacheName); return getKeyForCache(cache); } protected Object getKeyForCache(Cache cache) { return new MagicKey(cache); } protected void assertNotLocked(final String cacheName, final Object key) { eventually(new Condition() { @Override public boolean isSatisfied() throws Exception { boolean aNodeIsLocked = false; for (int i = 0; i < caches(cacheName).size(); i++) { final boolean isLocked = lockManager(i, cacheName).isLocked(key); if (isLocked) log.trace(key + " is locked on cache index " + i + " by " + lockManager(i, cacheName).getOwner(key)); aNodeIsLocked = aNodeIsLocked || isLocked; } return !aNodeIsLocked; } }); } protected void assertNotLocked(final Object key) { assertNotLocked((String)null, key); } protected boolean checkTxCount(int cacheIndex, int localTx, int remoteTx) { final int localTxCount = TestingUtil.getTransactionTable(cache(cacheIndex)).getLocalTxCount(); final int remoteTxCount = TestingUtil.getTransactionTable(cache(cacheIndex)).getRemoteTxCount(); log.tracef("Cache index %s: local tx %4s, remote tx %4s \n", cacheIndex, localTxCount, remoteTxCount); return localTxCount == localTx && remoteTxCount == remoteTx; } protected void assertNotLocked(int cacheIndex, Object key) { assertNotLocked(cache(cacheIndex), key); } protected void assertLocked(int cacheIndex, Object key) { assertLocked(cache(cacheIndex), key); } protected boolean checkLocked(int index, Object key) { return checkLocked(cache(index), key); } protected Cache getLockOwner(Object key) { return getLockOwner(key, null); } protected Cache getLockOwner(Object key, String cacheName) { Configuration c = getCache(0, cacheName).getConfiguration(); if (c.getCacheMode().isReplicated() || c.getCacheMode().isInvalidation()) { return getCache(0, cacheName); //for replicated caches only the coordinator acquires lock } else { if (!c.getCacheMode().isDistributed()) throw new IllegalStateException("This is not a clustered cache!"); final Address address = getCache(0, cacheName).getAdvancedCache().getDistributionManager().locate(key).get(0); for (Cache cache : caches(cacheName)) { if (cache.getAdvancedCache().getRpcManager().getTransport().getAddress().equals(address)) { return cache; } } throw new IllegalStateException(); } } protected void assertKeyLockedCorrectly(Object key) { assertKeyLockedCorrectly(key, null); } protected void assertKeyLockedCorrectly(Object key, String cacheName) { final Cache lockOwner = getLockOwner(key, cacheName); assert checkLocked(lockOwner, key); for (Cache c : caches(cacheName)) { if (c != lockOwner) assert !checkLocked(c, key) : "Key " + key + " is locked on cache " + c + " (" + cacheName + ") and it shouldn't"; } } private Cache getCache(int index, String name) { return name == null ? cache(index) : cache(index, name); } protected void forceTwoPhase(int cacheIndex) throws SystemException, RollbackException { TransactionManager tm = tm(cacheIndex); Transaction tx = tm.getTransaction(); tx.enlistResource(new XAResourceAdapter()); } protected void assertNoTransactions() { eventually(new Condition() { @Override public boolean isSatisfied() throws Exception { for (int i = 0; i < caches().size(); i++) { int localTxCount = transactionTable(i).getLocalTxCount(); int remoteTxCount = transactionTable(i).getRemoteTxCount(); if (localTxCount != 0 || remoteTxCount != 0) { log.tracef("Local tx=%s, remote tx=%s, for cache %s ", localTxCount, remoteTxCount, i); return false; } } return true; } }); } protected void assertTransactionCount(int cacheIndex, int local, int remote) { assertEquals(local ,transactionTable(cacheIndex).getLocalTxCount()); assertEquals(remote, transactionTable(cacheIndex).getRemoteTxCount()); } protected TransactionTable transactionTable(int cacheIndex) { return advancedCache(cacheIndex).getComponentRegistry().getComponent(TransactionTable.class); } protected void assertEventuallyEquals(final int cacheIndex, final Object key, final Object value) { eventually(new Condition() { @Override public boolean isSatisfied() throws Exception { return value == null ? value == cache(cacheIndex).get(key) : value.equals(cache(cacheIndex).get(key)); } }); } }