/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.discovery.commons.providers.base; import static org.junit.Assert.assertEquals; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.UUID; import java.util.concurrent.locks.ReentrantLock; import org.apache.sling.discovery.TopologyEvent; import org.apache.sling.discovery.TopologyEvent.Type; import org.apache.sling.discovery.commons.providers.BaseTopologyView; import org.apache.sling.discovery.commons.providers.base.ViewStateManagerImpl; import org.apache.sling.discovery.commons.providers.spi.ClusterSyncService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClusterTest { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private List<ViewStateManagerImpl> mgrList; private Random defaultRandom; @Before public void setup() throws Exception { mgrList = new LinkedList<ViewStateManagerImpl>(); defaultRandom = new Random(1234123412); // I want randomness yes, but deterministic, for some methods at least } @After public void teardown() throws Exception { mgrList = null; defaultRandom= null; } private ViewStateManagerImpl newMgr() { ViewStateManagerImpl mgr = new ViewStateManagerImpl(new ReentrantLock(), new ClusterSyncService() { public void sync(BaseTopologyView view, Runnable callback) { callback.run(); } @Override public void cancelSync() { // nothing to cancel, we're auto-run } }); mgrList.add(mgr); return mgr; } private void waitForInflightEvents(ViewStateManagerImpl mgr) throws InterruptedException { if (mgr==null) { throw new IllegalArgumentException("mgr must not be null"); } if (mgr.getAsyncEventSender()==null) { logger.info("waitForInflightEvents: mgr not yet activated..."); return; } while(mgr.getAsyncEventSender().hasInFlightEvent()) { logger.info("waitForInflightEvents: waiting 10ms..."); Thread.sleep(10); } } private void assertCountEvents(ViewStateManagerImpl mgr, DummyListener l, TopologyEvent.Type... types) throws InterruptedException { waitForInflightEvents(mgr); assertEquals(types.length, l.countEvents()); Iterator<TopologyEvent> it = l.getEvents().iterator(); int i=0; while(it.hasNext() && (i<types.length)) { TopologyEvent expectedEvent = it.next(); Type gotType = types[i++]; assertEquals(expectedEvent.getType(), gotType); } if (it.hasNext()) { StringBuffer additionalTypes = new StringBuffer(); while(it.hasNext()) { additionalTypes.append(","); additionalTypes.append(it.next().getType()); } fail("got more events than expected : "+additionalTypes); } if (i<types.length) { StringBuffer additionalTypes = new StringBuffer(); while(i<types.length) { additionalTypes.append(","); additionalTypes.append(types[i++]); } fail("did not get all events, also expected : "+additionalTypes); } } private void fail(String string) { // TODO Auto-generated method stub } @Test public void testTwoNodes() throws Exception { final ViewStateManagerImpl mgr1 = newMgr(); final String slingId1 = UUID.randomUUID().toString(); final ViewStateManagerImpl mgr2 = newMgr(); final String slingId2 = UUID.randomUUID().toString(); // bind l1 DummyListener l1 = new DummyListener(); mgr1.bind(l1); assertCountEvents(mgr1, l1); // bind l2 DummyListener l2 = new DummyListener(); mgr2.bind(l2); assertCountEvents(mgr2, l2); // fiddle with l1 - without any events expected to be sent mgr1.handleChanging(); assertCountEvents(mgr1, l1); mgr1.handleActivated(); assertCountEvents(mgr1, l1); mgr1.handleChanging(); assertCountEvents(mgr1, l1); // fiddle with l2 - without any events expected to be sent mgr2.handleChanging(); assertCountEvents(mgr2, l2); mgr2.handleActivated(); assertCountEvents(mgr2, l2); mgr2.handleChanging(); assertCountEvents(mgr2, l2); // call handleNewView with not-current views first... BaseTopologyView vA1 = TestHelper.newView(false, slingId1, slingId1, slingId1, slingId2); mgr1.handleNewView(vA1); assertCountEvents(mgr1, l1); assertCountEvents(mgr2, l2); BaseTopologyView vB1 = TestHelper.newView(false, slingId1, slingId2, slingId1, slingId2); mgr2.handleNewView(vB1); assertCountEvents(mgr1, l1); assertCountEvents(mgr2, l2); // then call handleNewView with a current view - that should now sent the INIT BaseTopologyView vA2 = TestHelper.newView(true, slingId1, slingId1, slingId1, slingId2); mgr1.handleNewView(vA2); assertCountEvents(mgr1, l1, Type.TOPOLOGY_INIT); assertCountEvents(mgr2, l2); BaseTopologyView vB2 = TestHelper.newView(true, slingId1, slingId2, slingId1, slingId2); mgr2.handleNewView(vB2); assertCountEvents(mgr1, l1, Type.TOPOLOGY_INIT); assertCountEvents(mgr2, l2, Type.TOPOLOGY_INIT); // now let instance1 get decoupled from the cluster (pseudo-network-partitioning) BaseTopologyView vB3 = TestHelper.newView(true, slingId2, slingId2, slingId2); mgr2.handleNewView(vB3); assertCountEvents(mgr1, l1, Type.TOPOLOGY_INIT); assertCountEvents(mgr2, l2, Type.TOPOLOGY_INIT, Type.TOPOLOGY_CHANGING, Type.TOPOLOGY_CHANGED); // now let instance1 take note of this decoupling mgr1.handleChanging(); assertCountEvents(mgr1, l1, Type.TOPOLOGY_INIT, Type.TOPOLOGY_CHANGING); assertCountEvents(mgr2, l2, Type.TOPOLOGY_INIT, Type.TOPOLOGY_CHANGING, Type.TOPOLOGY_CHANGED); // and now let instance1 rejoin BaseTopologyView vA4 = TestHelper.newView(true, slingId2, slingId1, slingId1, slingId2); mgr1.handleNewView(vA4); assertCountEvents(mgr1, l1, Type.TOPOLOGY_INIT, Type.TOPOLOGY_CHANGING, Type.TOPOLOGY_CHANGED); assertCountEvents(mgr2, l2, Type.TOPOLOGY_INIT, Type.TOPOLOGY_CHANGING, Type.TOPOLOGY_CHANGED); BaseTopologyView vB4 = TestHelper.newView(true, slingId2, slingId2, slingId1, slingId2); mgr2.handleNewView(vA4); assertCountEvents(mgr1, l1, Type.TOPOLOGY_INIT, Type.TOPOLOGY_CHANGING, Type.TOPOLOGY_CHANGED); assertCountEvents(mgr2, l2, Type.TOPOLOGY_INIT, Type.TOPOLOGY_CHANGING, Type.TOPOLOGY_CHANGED, Type.TOPOLOGY_CHANGING, Type.TOPOLOGY_CHANGED); } }