package org.infinispan.test; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.infinispan.AdvancedCache; import org.infinispan.Cache; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.Configuration; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; import org.infinispan.container.DataContainer; 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.InCacheMode; import org.infinispan.test.fwk.InTransactionMode; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.test.fwk.TestSelector; import org.infinispan.test.fwk.TransportFlags; import org.infinispan.transaction.LockingMode; import org.infinispan.transaction.TransactionMode; import org.infinispan.transaction.impl.TransactionTable; import org.infinispan.util.concurrent.IsolationLevel; import org.infinispan.util.concurrent.locks.LockManager; import org.testng.IMethodInstance; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Factory; /** * 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 cause {@link #createCacheManagers()} to be called before each method. * * @author Mircea.Markus@jboss.com */ @TestSelector(filters = { MultipleCacheManagersTest.CacheModeFilter.class, MultipleCacheManagersTest.TransactionalModeFilter.class, MultipleCacheManagersTest.TotalOrderFilter.class, MultipleCacheManagersTest.LockingModeFilter.class, MultipleCacheManagersTest.IsolationLevelFilter.class, }) public abstract class MultipleCacheManagersTest extends AbstractCacheTest { protected List<EmbeddedCacheManager> cacheManagers = Collections.synchronizedList(new ArrayList<EmbeddedCacheManager>()); protected IdentityHashMap<Cache<?, ?>, ReplListener> listeners = new IdentityHashMap<>(); // the cache mode set in configuration is shared in many tests, therefore we'll place the field, // fluent setter cacheMode(...) and parameters() to this class. protected CacheMode cacheMode; protected Boolean transactional; protected LockingMode lockingMode; protected Boolean totalOrder; protected IsolationLevel isolationLevel; private boolean parametrizedInstance = false; @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.clearContent(cacheManagers); 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.clearContent(cacheManagers); TestingUtil.killCacheManagers(cacheManagers); cacheManagers.clear(); } } 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(new ConfigurationBuilder(), 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 */ protected EmbeddedCacheManager addClusterEnabledCacheManager(ConfigurationBuilder defaultConfig) { return addClusterEnabledCacheManager(defaultConfig, new TransportFlags()); } protected EmbeddedCacheManager addClusterEnabledCacheManager(GlobalConfigurationBuilder globalBuilder, ConfigurationBuilder defaultConfig) { return addClusterEnabledCacheManager(globalBuilder, 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 builder default cfg to use * @return the new CacheManager */ protected EmbeddedCacheManager addClusterEnabledCacheManager(ConfigurationBuilder builder, TransportFlags flags) { EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(builder, flags); cacheManagers.add(cm); return cm; } protected EmbeddedCacheManager addClusterEnabledCacheManager(ConfigurationBuilderHolder builderHolder) { EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(builderHolder); cacheManagers.add(cm); return cm; } protected EmbeddedCacheManager addClusterEnabledCacheManager(GlobalConfigurationBuilder globalBuilder, ConfigurationBuilder builder, TransportFlags flags) { EmbeddedCacheManager cm = TestCacheManagerFactory.createClusteredCacheManager(globalBuilder, builder, flags); cacheManagers.add(cm); return cm; } protected void createCluster(ConfigurationBuilder builder, int count) { for (int i = 0; i < count; i++) addClusterEnabledCacheManager(builder); } /** * Allows multiple configurations to be defined for a cache manager before it is started, using the supplied * {@link ConfigurationBuilderHolder}. These cannot be shared per node so this method doesn't allow the user to make * the mistake and instead will give you one instance per node. * <p> * This method will wait until all nodes are up before returning * @param consumer consumer to configure the caches * @param count how many nodes to bring up */ protected void createCluster(Consumer<ConfigurationBuilderHolder> consumer, int count) { for (int i = 0; i < count; ++i) { ConfigurationBuilderHolder holder = new ConfigurationBuilderHolder(); holder.getGlobalConfigurationBuilder().clusteredDefault(); consumer.accept(holder); addClusterEnabledCacheManager(holder); } waitForClusterToForm(); } protected void createCluster(GlobalConfigurationBuilder globalBuilder, ConfigurationBuilder builder, int count) { for (int i = 0; i < count; i++) addClusterEnabledCacheManager(new GlobalConfigurationBuilder().read(globalBuilder.build()), builder); } protected void defineConfigurationOnAllManagers(String cacheName, ConfigurationBuilder b) { for (EmbeddedCacheManager cm : cacheManagers) { cm.defineConfiguration(cacheName, b.build()); } } protected <K, V> List<Cache<K, V>> getCaches(String cacheName) { List<Cache<K, V>> caches = new ArrayList<>(); List<EmbeddedCacheManager> managers = new ArrayList<>(cacheManagers); for (EmbeddedCacheManager cm : managers) { Cache<K, V> c; if (cacheName == null) c = cm.getCache(); else c = cm.getCache(cacheName); caches.add(c); } return caches; } protected void waitForClusterToForm(String cacheName) { List<Cache<Object, Object>> caches = getCaches(cacheName); Cache<Object, Object> cache = caches.get(0); TestingUtil.blockUntilViewsReceived(30000, caches); if (cache.getCacheConfiguration().clustering().cacheMode().isClustered()) { TestingUtil.waitForNoRebalance(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); } } protected <K, V> List<Cache<K, V>> createClusteredCaches( int numMembersInCluster, String cacheName, ConfigurationBuilder builder) { return createClusteredCaches(numMembersInCluster, cacheName, builder, new TransportFlags()); } protected <K, V> List<Cache<K, V>> createClusteredCaches( int numMembersInCluster, String cacheName, ConfigurationBuilder builder, TransportFlags flags) { List<Cache<K, V>> caches = new ArrayList<>(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, ConfigurationBuilder defaultConfigBuilder) { List<Cache<K, V>> caches = new ArrayList<>(numMembersInCluster); for (int i = 0; i < numMembersInCluster; i++) { EmbeddedCacheManager cm = addClusterEnabledCacheManager(defaultConfigBuilder); Cache<K, V> cache = cm.getCache(); caches.add(cache); } waitForClusterToForm(); return caches; } protected <K, V> List<Cache<K, V>> createClusteredCaches(int numMembersInCluster, ConfigurationBuilder defaultConfig, TransportFlags flags) { List<Cache<K, V>> caches = new ArrayList<>(numMembersInCluster); for (int i = 0; i < numMembersInCluster; i++) { EmbeddedCacheManager cm = addClusterEnabledCacheManager(defaultConfig, flags); Cache<K, V> cache = cm.getCache(); caches.add(cache); } waitForClusterToForm(); return caches; } /** * Create cacheNames.length in each CacheManager (numMembersInCluster cacheManagers). * * @param numMembersInCluster * @param defaultConfigBuilder * @param cacheNames * @return A list with size numMembersInCluster containing a list of cacheNames.length caches */ protected <K, V> List<List<Cache<K, V>>> createClusteredCaches(int numMembersInCluster, ConfigurationBuilder defaultConfigBuilder, String... cacheNames) { List<List<Cache<K, V>>> allCaches = new ArrayList<>(numMembersInCluster); for (int i = 0; i < numMembersInCluster; i++) { EmbeddedCacheManager cm = addClusterEnabledCacheManager(defaultConfigBuilder); List<Cache<K, V>> currentCacheManagerCaches = new ArrayList<>(cacheNames.length); for (String cacheName : cacheNames) { cm.defineConfiguration(cacheName, defaultConfigBuilder.build()); Cache<K, V> cache = cm.getCache(cacheName); currentCacheManagerCaches.add(cache); } allCaches.add(currentCacheManagerCaches); } waitForClusterToForm(cacheNames); return allCaches; } protected ReplListener replListener(Cache<?, ?> cache) { return listeners.computeIfAbsent(cache, k -> new ReplListener(cache)); } protected EmbeddedCacheManager manager(int i) { return cacheManagers.get(i); } public EmbeddedCacheManager manager(Address a) { for (EmbeddedCacheManager cm : cacheManagers) { if (cm.getAddress().equals(a)) { return cm; } } throw new IllegalArgumentException(a + " is not a valid cache manager address!"); } public int managerIndex(Address a) { for (int i = 0; i < cacheManagers.size(); i++) { EmbeddedCacheManager cm = cacheManagers.get(i); if (cm.getAddress().equals(a)) { return i; } } throw new IllegalArgumentException(a + " is not a valid cache manager address!"); } 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(); } protected DataContainer dataContainer(int index) { return advancedCache(index).getDataContainer(); } /** * This is the method you should override when providing factory method. */ public Object[] factory() { throw new IllegalStateException("Only overridden methods should be called!"); } @Factory public Object[] defaultFactory() { // It is possible to override the factory method, but if we extend a class that defines such overridden // method, the factory method will be inherited, too - that results in running the superclass tests // instead of current class tests. try { Method factory = getClass().getMethod("factory"); if (factory.getDeclaringClass() == getClass()) { return factory(); } } catch (NoSuchMethodException e) { throw new IllegalStateException("Every class should have factory method, at least inherited", e); } Consumer<MultipleCacheManagersTest>[] cacheModeModifiers = getModifiers(InCacheMode.class, InCacheMode::value, MultipleCacheManagersTest::cacheMode); Consumer<MultipleCacheManagersTest>[] transactionModifiers = getModifiers(InTransactionMode.class, InTransactionMode::value, (t, m) -> t.transactional(m.isTransactional())); List<Consumer<MultipleCacheManagersTest>[]> allModifiers = Arrays.asList(cacheModeModifiers, transactionModifiers); int numTests = allModifiers.stream().mapToInt(m -> m.length).reduce(1, (m1, m2) -> m1 * m2); Object[] tests = new Object[numTests]; tests[0] = this; Constructor<? extends MultipleCacheManagersTest> ctor; try { ctor = getClass().getConstructor(); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Missing no-arg constructor in " + getClass()); } for (int i = 1; i < tests.length; ++i) { try { tests[i] = ctor.newInstance(); } catch (Exception e) { throw new IllegalStateException("Cannot create test instances", e); } } int stride = 1; for (Consumer<MultipleCacheManagersTest>[] modifiers : allModifiers) { applyModifiers(tests, modifiers, stride); stride *= modifiers.length; } return tests; } private void applyModifiers(Object[] tests, Consumer<MultipleCacheManagersTest>[] modifiers, int stride) { for (int i = 0, mi = 0; i < tests.length; i += stride, mi = (mi + 1) % modifiers.length) { for (int j = 0; j < stride; ++j) { modifiers[mi].accept((MultipleCacheManagersTest) tests[i + j]); } } } public List<MultipleCacheManagersTest> expand() { return new ArrayList<>(); } private <Mode, A extends Annotation> Consumer<MultipleCacheManagersTest>[] getModifiers(Class<A> annotationClass, Function<A, Mode[]> methodRetriever, BiConsumer<MultipleCacheManagersTest, Mode> applier) { Mode[] classModes = classModes(annotationClass, methodRetriever); Set<Mode> methodModes = methodModes(annotationClass, methodRetriever); if (classModes == null && methodModes == null) { return new Consumer[] { t -> {} }; // no modifications } Set<Mode> allModes = new HashSet<>(); if (classModes != null) { allModes.addAll(Arrays.asList(classModes)); } if (methodModes != null) { allModes.addAll(methodModes); } // if there are only method-level annotations, add a version without setting mode at all if (classModes == null) { Consumer<MultipleCacheManagersTest>[] modifiers = new Consumer[methodModes.size() + 1]; modifiers[0] = t -> {}; int i = 1; for (Mode mode : methodModes) { // we have already added setting with this cache mode, don't do it twice if (mode == cacheMode) continue; modifiers[i++] = t -> applier.accept(t, mode); } return i == modifiers.length ? modifiers : Arrays.copyOf(modifiers, i); } else { return allModes.stream().map(mode -> (Consumer<MultipleCacheManagersTest>) t -> applier.accept(t, mode)).toArray(Consumer[]::new); } } protected <Mode, A extends Annotation> Set<Mode> methodModes(Class<A> annotationClass, Function<A, Mode[]> modeRetriever) { // the annotation is not inherited Set<Mode> modes = null; for (Method m : getClass().getMethods()) { A annotation = m.getAnnotation(annotationClass); if (annotation == null) continue; if (modes == null) { modes = new HashSet<>(); } Collections.addAll(modes, modeRetriever.apply(annotation)); } return modes; } protected <Mode, A extends Annotation> Mode[] classModes(Class<A> annotationClass, Function<A, Mode[]> modeRetriever) { A annotation = getClass().getDeclaredAnnotation(annotationClass); if (annotation == null) return null; return modeRetriever.apply(annotation); } private MultipleCacheManagersTest internalCacheMode(CacheMode cacheMode) { this.parametrizedInstance = true; return cacheMode(cacheMode); } public MultipleCacheManagersTest cacheMode(CacheMode cacheMode) { this.cacheMode = cacheMode; return this; } public MultipleCacheManagersTest transactional(boolean transactional) { this.transactional = transactional; return this; } public MultipleCacheManagersTest lockingMode(LockingMode lockingMode) { this.lockingMode = lockingMode; return this; } public MultipleCacheManagersTest totalOrder(boolean totalOrder) { this.totalOrder = totalOrder; return this; } public MultipleCacheManagersTest isolationLevel(IsolationLevel isolationLevel) { this.isolationLevel = isolationLevel; return this; } public TransactionMode transactionMode() { return transactional ? TransactionMode.TRANSACTIONAL : TransactionMode.NON_TRANSACTIONAL; } @Override protected String parameters() { // cacheMode is self-explaining String[] names = parameterNames(); Object[] params = parameterValues(); assert names.length == params.length; boolean[] last = new boolean[params.length]; boolean none = true; for (int i = params.length - 1; i >= 0; --i) { last[i] = none; none &= params[i] == null; } if (none) { return null; } StringBuilder sb = new StringBuilder().append('['); for (int i = 0; i < params.length; ++i) { if (params[i] != null) { if (names[i] != null) { sb.append(names[i]).append('='); } sb.append(params[i]); if (!last[i]) sb.append(", "); } } return sb.append(']').toString(); } protected String[] parameterNames() { return new String[]{ null, "tx", "locking", "TO", "isolation" }; } protected Object[] parameterValues() { return new Object[]{ cacheMode, transactional, lockingMode, totalOrder, isolationLevel }; } protected static <T> T[] concat(T[] a1, T... a2) { T[] na = Arrays.copyOf(a1, a1.length + a2.length); System.arraycopy(a2, 0, na, a1.length, a2.length); return na; } /** * 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 <A, B> AdvancedCache<A, B> advancedCache(int i) { return this.<A,B>cache(i).getAdvancedCache(); } protected <A, B> AdvancedCache<A, B> advancedCache(int i, String cacheName) { return this.<A, B>cache(i, cacheName).getAdvancedCache(); } protected <K, V> List<Cache<K, V>> caches(String name) { return getCaches(name); } 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) { killMember(cacheIndex, null); } /** * Kills the cache manager with the given index and waits for the new cluster to form using the provided cache */ protected void killMember(int cacheIndex, String cacheName) { killMember(cacheIndex, cacheName, true); } protected void killMember(int cacheIndex, String cacheName, boolean awaitRehash) { List<Cache<Object, Object>> caches = caches(cacheName); caches.remove(cacheIndex); manager(cacheIndex).stop(); cacheManagers.remove(cacheIndex); if (awaitRehash && caches.size() > 0) { TestingUtil.blockUntilViewsReceived(60000, false, caches); TestingUtil.waitForNoRebalance(caches); } } /** * 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); } @SuppressWarnings("unchecked") protected <K> Supplier<K> supplyKeyForCache(int nodeIndex, String cacheName) { return () -> (K) getKeyForCache(nodeIndex, cacheName); } protected MagicKey getKeyForCache(Cache<?, ?> cache) { return new MagicKey(cache); } protected MagicKey getKeyForCache(Cache<?, ?> primary, Cache<?, ?>... backup) { return new MagicKey(primary, backup); } protected void assertNotLocked(final String cacheName, final Object key) { eventually(() -> { 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(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) { assertEventuallyNotLocked(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 <K, V> Cache<K, V> getLockOwner(Object key) { return getLockOwner(key, null); } protected <K, V> Cache<K, V> getLockOwner(Object key, String cacheName) { Configuration c = getCache(0, cacheName).getCacheConfiguration(); if (c.clustering().cacheMode().isInvalidation()) { return getCache(0, cacheName); //for replicated caches only the coordinator acquires lock } else if (!c.clustering().cacheMode().isClustered()) { throw new IllegalStateException("This is not a clustered cache!"); } else { Address address = getCache(0, cacheName).getAdvancedCache().getDistributionManager().getCacheTopology() .getDistribution(key).primary(); for (Cache<K, V> cache : this.<K, V>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 <K, V> Cache<K, V> getCache(int index, String name) { return name == null ? this.cache(index) : this.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() { assertNoTransactions(null); } protected void assertNoTransactions(final String cacheName) { eventually("There are pending transactions!", () -> { for (Cache<?, ?> cache : caches(cacheName)) { final TransactionTable transactionTable = TestingUtil.extractComponent(cache, TransactionTable.class); int localTxCount = transactionTable.getLocalTxCount(); int remoteTxCount = transactionTable.getRemoteTxCount(); if (localTxCount != 0 || remoteTxCount != 0) { log.tracef("Local tx=%s, remote tx=%s, for cache %s ", transactionTable.getLocalGlobalTransaction(), transactionTable.getRemoteGlobalTransaction(), address(cache)); return false; } } return true; }); } 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(() -> value == null ? null == cache(cacheIndex).get(key) : value.equals(cache(cacheIndex).get(key))); } protected abstract static class AnnotationFilter<A extends Annotation, AM, CM> { private final Class<A> annotationClazz; private final Function<A, AM[]> modesRetriever; private final BiPredicate<AM, CM> modeChecker; protected AnnotationFilter(Class<A> annotationClazz, Function<A, AM[]> modesRetriever, BiPredicate<AM, CM> modeChecker) { this.annotationClazz = annotationClazz; this.modesRetriever = modesRetriever; this.modeChecker = modeChecker; } public boolean test(CM mode, IMethodInstance method) { // If both method and class have the annotation, class annotation has priority. A clazzAnnotation = method.getInstance().getClass().getAnnotation(annotationClazz); A methodAnnotation = method.getMethod().getConstructorOrMethod().getMethod().getAnnotation(annotationClazz); if (methodAnnotation != null) { // If a method-level annotation contains current cache mode, run it, otherwise ignore that if (Stream.of(modesRetriever.apply(methodAnnotation)).anyMatch(m -> modeChecker.test(m, mode))) { return true; } } else if (clazzAnnotation != null) { return true; } else if (mode == null || !((MultipleCacheManagersTest) method.getInstance()).parametrizedInstance) { // There are no annotations on this method nor on this class, but due to an annotation // on different method there may be instances with non-default cache mode return true; } return false; } } public static class CacheModeFilter extends AnnotationFilter<InCacheMode, CacheMode, CacheMode> implements Predicate<IMethodInstance> { private final String cacheModeString = System.getProperty("test.infinispan.cacheMode"); public CacheModeFilter() { super(InCacheMode.class, InCacheMode::value, (m1, m2) -> m1 == m2); } @Override public boolean test(IMethodInstance method) { CacheMode cacheMode = ((MultipleCacheManagersTest) method.getInstance()).cacheMode; if (cacheModeString != null && cacheMode != null && !cacheMode.friendlyCacheModeString().equalsIgnoreCase(cacheModeString)) { return false; } return test(cacheMode, method); } } public static class TransactionalModeFilter extends AnnotationFilter<InTransactionMode, TransactionMode, Boolean> implements Predicate<IMethodInstance> { private final String txModeString = System.getProperty("test.infinispan.transactional"); public TransactionalModeFilter() { super(InTransactionMode.class, InTransactionMode::value, (m, b) -> m.isTransactional() == b); } @Override public boolean test(IMethodInstance method) { Boolean transactional = ((MultipleCacheManagersTest) method.getInstance()).transactional; if (txModeString != null && transactional != null && !transactional.toString().equalsIgnoreCase(txModeString)) { return false; } return test(transactional, method); } } protected static abstract class FilterByProperty<T> implements Predicate<IMethodInstance> { private final String property; // this could be done through abstract method but this way is more concise private final Function<MultipleCacheManagersTest, T> getMode; public FilterByProperty(String property, Function<MultipleCacheManagersTest, T> getMode) { this.property = System.getProperty(property); this.getMode = getMode; } @Override public boolean test(IMethodInstance method) { if (property == null) return true; T mode = getMode.apply((MultipleCacheManagersTest) method.getInstance()); return mode == null || mode.toString().equalsIgnoreCase(property); } } public static class TotalOrderFilter extends FilterByProperty<Boolean> { public TotalOrderFilter() { super("test.infinispan.totalOrder", test -> test.totalOrder); } } public static class LockingModeFilter extends FilterByProperty<LockingMode> { public LockingModeFilter() { super("test.infinispan.lockingMode", test -> test.lockingMode); } } public static class IsolationLevelFilter extends FilterByProperty<IsolationLevel> { public IsolationLevelFilter() { super("test.infinispan.isolationLevel", test -> test.isolationLevel); } } }