package org.infinispan.functional; import static org.infinispan.test.Exceptions.assertException; import static org.infinispan.test.Exceptions.assertExceptionNonStrict; import static org.infinispan.test.Exceptions.expectExceptionNonStrict; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.io.Serializable; import java.util.NoSuchElementException; import java.util.concurrent.CompletionException; import java.util.function.BiConsumer; import java.util.function.Function; import javax.transaction.RollbackException; import javax.transaction.xa.XAException; import org.infinispan.Cache; import org.infinispan.commons.CacheException; import org.infinispan.commons.api.functional.EntryView.ReadEntryView; import org.infinispan.commons.api.functional.EntryView.WriteEntryView; import org.infinispan.commons.api.functional.FunctionalMap; import org.infinispan.functional.impl.ReadOnlyMapImpl; import org.infinispan.remoting.RemoteException; import org.testng.annotations.Test; /** * @author Radim Vansa <rvansa@redhat.com> && Krzysztof Sobolewski <Krzysztof.Sobolewski@atende.pl> */ @Test(groups = "functional", testName = "functional.FunctionalInMemoryTest") public class FunctionalInMemoryTest extends AbstractFunctionalOpTest { public FunctionalInMemoryTest() { persistence = false; } @Test(dataProvider = "owningModeAndWriteMethod") public void testWriteLoad(boolean isOwner, WriteMethod method) { Object key = getKey(isOwner); method.action.eval(key, wo, rw, (Function<ReadEntryView<Object, String>, Void> & Serializable) view -> { assertFalse(view.find().isPresent()); return null; }, (BiConsumer<WriteEntryView<String>, Void> & Serializable) (view, nil) -> view.set("value"), getClass()); assertInvocations(Boolean.TRUE.equals(transactional) && !isOwner && !method.doesRead ? 3 : 2); caches(DIST).forEach(cache -> assertEquals(cache.get(key), "value", getAddress(cache).toString())); caches(DIST).forEach(cache -> { if (cache.getAdvancedCache().getDistributionManager().getLocality(key).isLocal()) { assertTrue(cache.getAdvancedCache().getDataContainer().containsKey(key), getAddress(cache).toString()); } else { assertFalse(cache.getAdvancedCache().getDataContainer().containsKey(key), getAddress(cache).toString()); } }); method.action.eval(key, wo, rw, (Function<ReadEntryView<Object, String>, Void> & Serializable) view -> { assertTrue(view.find().isPresent()); assertEquals(view.get(), "value"); return null; }, (BiConsumer<WriteEntryView<String>, Void> & Serializable) (view, nil) -> { }, getClass()); assertInvocations(Boolean.TRUE.equals(transactional) && !isOwner && !method.doesRead ? 6 : 4); } @Test(dataProvider = "writeMethods") public void testWriteLoadLocal(WriteMethod method) { Integer key = 1; method.action.eval(key, lwo, lrw, (Function<ReadEntryView<Integer, String>, Void> & Serializable) view -> { assertFalse(view.find().isPresent()); return null; }, (BiConsumer<WriteEntryView<String>, Void> & Serializable) (view, nil) -> view.set("value"), getClass()); assertInvocations(1); assertEquals(cacheManagers.get(0).getCache().get(key), "value"); method.action.eval(key, lwo, lrw, (Function<ReadEntryView<Integer, String>, Void> & Serializable) view -> { assertTrue(view.find().isPresent()); assertEquals(view.get(), "value"); return null; }, (BiConsumer<WriteEntryView<String>, Void> & Serializable) (view, nil) -> {}, getClass()); assertInvocations(2); } @Test(dataProvider = "owningModeAndWriteMethod") public void testExceptionPropagation(boolean isOwner, WriteMethod method) { Object key = getKey(isOwner); try { method.action.eval(key, wo, rw, (Function<ReadEntryView<Object, String>, Void> & Serializable) view -> null, (BiConsumer<WriteEntryView<String>, Void> & Serializable) (view, nil) -> { throw new TestException(); }, getClass()); fail("Should throw CompletionException:CacheException:[RemoteException:]*TestException"); } catch (CacheException | CompletionException e) { // catches RemoteExceptions, too Throwable t = e; if (Boolean.TRUE.equals(transactional) && t.getCause() instanceof RollbackException) { Throwable[] suppressed = t.getCause().getSuppressed(); if (suppressed != null && suppressed.length > 0) { t = suppressed[0]; assertEquals(XAException.class, t.getClass()); t = t.getCause(); } } assertException(CompletionException.class, t); t = t.getCause(); assertExceptionNonStrict(CacheException.class, t); while (t.getCause() instanceof RemoteException && t != t.getCause()) { t = t.getCause(); } assertException(TestException.class, t.getCause()); } } @Test(dataProvider = "owningModeAndReadWrites") public void testWriteOnMissingValue(boolean isOwner, WriteMethod method) { Object key = getKey(isOwner); try { method.action.eval(key, null, rw, (Function<ReadEntryView<Object, String>, Object> & Serializable) view -> view.get(), (BiConsumer<WriteEntryView<String>, Void> & Serializable) (view, nil) -> {}, getClass()); fail("Should throw CompletionException:CacheException:[RemoteException:]*NoSuchElementException"); } catch (CompletionException e) { // catches RemoteExceptions, too Throwable t = e; assertException(CompletionException.class, t); t = t.getCause(); assertExceptionNonStrict(CacheException.class, t); while (t.getCause() instanceof RemoteException && t != t.getCause()) { t = t.getCause(); } assertException(NoSuchElementException.class, t.getCause()); } } @Test(dataProvider = "owningModeAndReadMethod") public void testReadLoad(boolean isOwner, ReadMethod method) { Object key = getKey(isOwner); assertTrue((Boolean) method.action.eval(key, ro, (Function<ReadEntryView<Object, String>, Boolean> & Serializable) view -> { assertFalse(view.find().isPresent()); return true; })); // we can't add from read-only cache, so we put manually: cache(0, DIST).put(key, "value"); caches(DIST).forEach(cache -> assertEquals(cache.get(key), "value", getAddress(cache).toString())); caches(DIST).forEach(cache -> { if (cache.getAdvancedCache().getDistributionManager().getLocality(key).isLocal()) { assertTrue(cache.getAdvancedCache().getDataContainer().containsKey(key), getAddress(cache).toString()); } else { assertFalse(cache.getAdvancedCache().getDataContainer().containsKey(key), getAddress(cache).toString()); } }); assertEquals(method.action.eval(key, ro, (Function<ReadEntryView<Object, String>, String> & Serializable) view -> { assertTrue(view.find().isPresent()); assertEquals(view.get(), "value"); return "OK"; }), "OK"); } @Test(dataProvider = "readMethods") public void testReadLoadLocal(ReadMethod method) { Integer key = 1; assertTrue((Boolean) method.action.eval(key, lro, (Function<ReadEntryView<Object, String>, Boolean> & Serializable) view -> { assertFalse(view.find().isPresent()); return true; })); // we can't add from read-only cache, so we put manually: Cache<Integer, String> cache = cacheManagers.get(0).getCache(); cache.put(key, "value"); assertEquals(cache.get(key), "value"); assertEquals(method.action.eval(key, lro, (Function<ReadEntryView<Object, String>, String> & Serializable) view -> { assertTrue(view.find().isPresent()); assertEquals(view.get(), "value"); return "OK"; }), "OK"); } @Test(dataProvider = "owningModeAndReadMethod") public void testReadOnMissingValue(boolean isOwner, ReadMethod method) { testReadOnMissingValue(getKey(isOwner), ro, method); } @Test(dataProvider = "methods") public void testOnMissingValueLocal(ReadMethod method) { testReadOnMissingValue(0, ReadOnlyMapImpl.create(fmapL1), method); } private <K> void testReadOnMissingValue(K key, FunctionalMap.ReadOnlyMap<K, String> ro, ReadMethod method) { assertEquals(ro.eval(key, (Function<ReadEntryView<K, String>, Boolean> & Serializable) (view -> view.find().isPresent())).join(), Boolean.FALSE); expectExceptionNonStrict(CompletionException.class, CacheException.class, NoSuchElementException.class, () -> method.action.eval(key, ro, (Function<ReadEntryView<K, String>, Object> & Serializable) view -> view.get()) ); } private static class TestException extends RuntimeException { } }