package org.infinispan.counter; import static java.lang.String.format; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.infinispan.counter.api.CounterEvent; import org.infinispan.counter.api.CounterListener; import org.infinispan.counter.api.CounterManager; import org.infinispan.counter.api.CounterState; import org.infinispan.counter.api.Handle; import org.infinispan.counter.impl.BaseCounterTest; import org.infinispan.counter.util.TestCounter; import org.testng.annotations.Test; /** * A notification test for the counters. * * @author Pedro Ruivo * @since 9.0 */ @Test(groups = "functional") public abstract class AbstractCounterNotificationTest extends BaseCounterTest { private static final int CLUSTER_SIZE = 4; public void testSimpleListener(Method method) throws Exception { final String counterName = method.getName(); final TestCounter[] counters = new TestCounter[CLUSTER_SIZE]; for (int i = 0; i < CLUSTER_SIZE; ++i) { counters[i] = createCounter(counterManager(i), counterName); } Handle<ListenerQueue> l = counters[0].addListener(new ListenerQueue()); for (TestCounter counter : counters) { counter.increment(); } ListenerQueue lq = l.getCounterListener(); printQueue(lq); lq.assertEvent(0, CounterState.VALID, 1, CounterState.VALID); lq.assertEvent(1, CounterState.VALID, 2, CounterState.VALID); lq.assertEvent(2, CounterState.VALID, 3, CounterState.VALID); lq.assertEvent(3, CounterState.VALID, 4, CounterState.VALID); assertEquals(4L, counters[0].getValue()); l.remove(); for (TestCounter counter : counters) { counter.increment(); } assertTrue(l.getCounterListener().queue.isEmpty()); } public void testMultipleListeners(Method method) throws ExecutionException, InterruptedException { final String counterName = method.getName(); final TestCounter[] counters = new TestCounter[CLUSTER_SIZE]; final List<Handle<ListenerQueue>> listeners = new ArrayList<>(CLUSTER_SIZE); for (int i = 0; i < CLUSTER_SIZE; ++i) { counters[i] = createCounter(counterManager(i), counterName); listeners.add(counters[i].addListener(new ListenerQueue())); } for (TestCounter counter : counters) { counter.increment(); } for (int i = 0; i < CLUSTER_SIZE; ++i) { ListenerQueue lq = listeners.get(i).getCounterListener(); printQueue(lq); lq.assertEvent(0, CounterState.VALID, 1, CounterState.VALID); lq.assertEvent(1, CounterState.VALID, 2, CounterState.VALID); lq.assertEvent(2, CounterState.VALID, 3, CounterState.VALID); lq.assertEvent(3, CounterState.VALID, 4, CounterState.VALID); assertEquals(4L, counters[i].getValue()); } } public void testExceptionInListener(Method method) throws ExecutionException, InterruptedException { final String counterName = method.getName(); final TestCounter[] counters = new TestCounter[CLUSTER_SIZE]; for (int i = 0; i < CLUSTER_SIZE; ++i) { counters[i] = createCounter(counterManager(i), counterName); } counters[0].addListener(event -> { throw new RuntimeException("expected 1"); }); final Handle<ListenerQueue> l1 = counters[0].addListener(new ListenerQueue()); counters[0].addListener(event -> { throw new RuntimeException("expected 2"); }); final Handle<ListenerQueue> l2 = counters[0].addListener(new ListenerQueue()); for (TestCounter counter : counters) { counter.increment(); } ListenerQueue lq = l1.getCounterListener(); printQueue(lq); lq.assertEvent(0, CounterState.VALID, 1, CounterState.VALID); lq.assertEvent(1, CounterState.VALID, 2, CounterState.VALID); lq.assertEvent(2, CounterState.VALID, 3, CounterState.VALID); lq.assertEvent(3, CounterState.VALID, 4, CounterState.VALID); lq = l2.getCounterListener(); printQueue(lq); lq.assertEvent(0, CounterState.VALID, 1, CounterState.VALID); lq.assertEvent(1, CounterState.VALID, 2, CounterState.VALID); lq.assertEvent(2, CounterState.VALID, 3, CounterState.VALID); lq.assertEvent(3, CounterState.VALID, 4, CounterState.VALID); assertEquals(4L, counters[0].getValue()); for (TestCounter counter : counters) { counter.decrement(); } lq = l1.getCounterListener(); printQueue(lq); lq.assertEvent(4, CounterState.VALID, 3, CounterState.VALID); lq.assertEvent(3, CounterState.VALID, 2, CounterState.VALID); lq.assertEvent(2, CounterState.VALID, 1, CounterState.VALID); lq.assertEvent(1, CounterState.VALID, 0, CounterState.VALID); lq = l2.getCounterListener(); printQueue(lq); lq.assertEvent(4, CounterState.VALID, 3, CounterState.VALID); lq.assertEvent(3, CounterState.VALID, 2, CounterState.VALID); lq.assertEvent(2, CounterState.VALID, 1, CounterState.VALID); lq.assertEvent(1, CounterState.VALID, 0, CounterState.VALID); } @Override protected int clusterSize() { return CLUSTER_SIZE; } protected abstract TestCounter createCounter(CounterManager counterManager, String counterName); protected void printQueue(ListenerQueue queue) { log.tracef("Queue is " + queue); } protected static class ListenerQueue implements CounterListener { final BlockingQueue<CounterEvent> queue; ListenerQueue() { queue = new LinkedBlockingQueue<>(); } @Override public void onUpdate(CounterEvent event) { queue.offer(event); } @Override public String toString() { return "ListenerQueue{" + "queue=" + queue + '}'; } void assertEvent(long oldValue, CounterState oldState, long newValue, CounterState newState) throws InterruptedException { CounterEvent event = queue.poll(30, TimeUnit.SECONDS); assertNotNull(event); assertEquals(format("Wrong old value for event: %s.", event), oldValue, event.getOldValue()); assertEquals(format("Wrong old state for event: %s.", event), oldState, event.getOldState()); assertEquals(format("Wrong new value for event: %s.", event), newValue, event.getNewValue()); assertEquals(format("Wrong new state for event: %s.", event), newState, event.getNewState()); } } }