This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.discovery.commons-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-discovery-commons.git
commit 594318990cdea7c1eda90c2fb9c7cedc59c2a465 Author: Stefan Egli <[email protected]> AuthorDate: Mon May 4 10:11:10 2015 +0000 SLING-4685 : adding initial version of ViewStateManager - a shared implementation of TopologyEventListener-handling and sending of events based on activate/deactivate/changing/newView triggers - intended for use by implementors of the discovery.api (not clients of it) git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/discovery/commons@1677574 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 5 + .../commons/providers/BaseTopologyView.java | 52 ++ .../commons/providers/ViewStateManager.java | 352 +++++++++++++ .../discovery/commons/providers/package-info.java | 29 ++ .../commons/providers/TestViewStateManager.java | 569 +++++++++++++++++++++ 5 files changed, 1007 insertions(+) diff --git a/pom.xml b/pom.xml index 7b74420..7a9f48e 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,11 @@ <artifactId>jsr305</artifactId> <version>2.0.0</version> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.6.1</version> + </dependency> <!-- Testing --> <dependency> <groupId>junit</groupId> diff --git a/src/main/java/org/apache/sling/discovery/commons/providers/BaseTopologyView.java b/src/main/java/org/apache/sling/discovery/commons/providers/BaseTopologyView.java new file mode 100644 index 0000000..d8daefe --- /dev/null +++ b/src/main/java/org/apache/sling/discovery/commons/providers/BaseTopologyView.java @@ -0,0 +1,52 @@ +/* + * 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; + +import org.apache.sling.discovery.TopologyView; + +/** + * Very simple abstract base class for the TopologyView which + * comes with the 'setNotCurrent()' method - that allows the + * ViewStateManager to mark a topologyView as no longer current + * - and the isCurrent() is handled accordingly. + */ +public abstract class BaseTopologyView implements TopologyView { + + /** Whether or not this topology is considered 'current' / ie currently valid **/ + private volatile boolean current = true; + + /** + * {@inheritDoc} + */ + public boolean isCurrent() { + return current; + } + + /** + * Marks this view as no longer current - this typically + * results in a TOPOLOGY_CHANGING event to be sent. + * <p> + * Note that once marked as not current, it can no longer + * be reverted to current==true + */ + public void setNotCurrent() { + current = false; + } + +} diff --git a/src/main/java/org/apache/sling/discovery/commons/providers/ViewStateManager.java b/src/main/java/org/apache/sling/discovery/commons/providers/ViewStateManager.java new file mode 100644 index 0000000..5820f5d --- /dev/null +++ b/src/main/java/org/apache/sling/discovery/commons/providers/ViewStateManager.java @@ -0,0 +1,352 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.sling.discovery.TopologyEvent; +import org.apache.sling.discovery.TopologyEvent.Type; +import org.apache.sling.discovery.TopologyEventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The ViewStateManager is at the core of managing TopologyEventListeners, + * the 'view state' (changing vs changed) and sending out the appropriate + * and according TopologyEvents to the registered listeners. + * <p> + * Note that this class is completely unsynchronized and the idea is that + * (since this class is only of interest to other implementors/providers of + * the discovery.api, not to users of the discovery.api however) that those + * other implementors take care of proper synchronization. Without synchronization + * this class is prone to threading issues! The most simple form of + * synchronization is to synchronize all of the public (non-static..) methods + * with the same monitor object. + */ +public class ViewStateManager { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * List of bound listeners that have already received their INIT event - others are in unInitializedEventListeners. + * @see ViewStateManager#unInitializedEventListeners + */ + private List<TopologyEventListener> eventListeners = new ArrayList<TopologyEventListener>(); + + /** + * List of bound listeners that have not yet received their TOPOLOGY_INIT event - + * once they are sent the TOPOLOGY_INIT event they are moved to eventListeners (and stay there). + * <p> + * This list becomes necessary for cases where the bind() happens before activate, or after activate but at a time + * when the topology is TOPOLOGY_CHANGING - at which point an TOPOLOGY_INIT event can not yet be sent. + * @see ViewStateManager#eventListeners + */ + private List<TopologyEventListener> unInitializedEventListeners = new ArrayList<TopologyEventListener>(); + + /** + * Set true when the bundle.activate() was called, false if not yet or when deactivate() is called. + * <p> + * This controls whether handleChanging() and handleNewView() should cause any events + * to be sent - which they do not if called before handleActivated() (or after handleDeactivated()) + * @see ViewStateManager#handleActivated() + * @see ViewStateManager#handleChanging() + * @see ViewStateManager#handleNewView(BaseTopologyView) + * @see ViewStateManager#handleDeactivated() + */ + private boolean activated; + + /** + * Represents the 'newView' passed to handleNewTopologyView at the most recent invocation. + * <p> + * This is used for: + * <ul> + * <li>sending with the TOPOLOGY_INIT event to newly bound listeners or at activate time</li> + * <li>sending as oldView (marked not current) with the TOPOLOGY_CHANGING event</li> + * <li>sending as oldView (marked not current in case handleChanging() was not invoked) with the TOPOLOGY_CHANGED event</li> + * </ul> + */ + private BaseTopologyView previousView; + + /** + * Set to true when handleChanging is called - set to false in handleNewView. + * When this goes true, a TOPOLOGY_CHANGING is sent. + * When this goes false, a TOPOLOGY_CHANGED is sent. + */ + private boolean isChanging; + + /** + * Binds the given eventListener, sending it an INIT event if applicable. + * <p> + * Note: no synchronization done in ViewStateManager, <b>must</b> be done externally + * @param eventListener the eventListener that is to bind + */ + public void bind(final TopologyEventListener eventListener) { + + logger.debug("bind: Binding TopologyEventListener {}", + eventListener); + + if (eventListeners.contains(eventListener) || unInitializedEventListeners.contains(eventListener)) { + logger.info("bind: TopologyEventListener already registered: "+eventListener); + return; + } + + if (activated) { + // check to see in which state we are + if (isChanging || (previousView==null)) { + // then we cannot send the TOPOLOGY_INIT at this point - need to delay this + unInitializedEventListeners.add(eventListener); + } else { + // otherwise we can send the TOPOLOGY_INIT now + sendEvent(eventListener, newInitEvent(previousView)); + eventListeners.add(eventListener); + } + } else { + unInitializedEventListeners.add(eventListener); + } + } + + /** + * Unbinds the given eventListener, returning whether or not it was bound at all. + * <p> + * Note: no synchronization done in ViewStateManager, <b>must</b> be done externally + * @param eventListener the eventListner that is to unbind + * @return whether or not the listener was added in the first place + */ + public boolean unbind(final TopologyEventListener eventListener) { + + logger.debug("unbind: Releasing TopologyEventListener {}", + eventListener); + + // even though a listener must always only ever exist in one of the two, + // the unbind we do - for safety-paranoia-reasons - remove them from both + final boolean a = eventListeners.remove(eventListener); + final boolean b = unInitializedEventListeners.remove(eventListener); + return a || b; + } + + /** Internal helper method that sends a given event to a list of listeners **/ + private void sendEvent(final List<TopologyEventListener> audience, final TopologyEvent event) { + if (logger.isDebugEnabled()) { + logger.debug("sendEvent: sending topologyEvent {}, to all ({}) listeners", event, audience.size()); + } + for (Iterator<TopologyEventListener> it = audience.iterator(); it.hasNext();) { + TopologyEventListener topologyEventListener = it.next(); + sendEvent(topologyEventListener, event); + } + if (logger.isDebugEnabled()) { + logger.debug("sendEvent: sent topologyEvent {}, to all ({}) listeners", event, audience.size()); + } + } + + /** Internal helper method that sends a given event to a particular listener **/ + private void sendEvent(final TopologyEventListener da, final TopologyEvent event) { + if (logger.isDebugEnabled()) { + logger.debug("sendEvent: sending topologyEvent {}, to {}", event, da); + } + try{ + da.handleTopologyEvent(event); + } catch(final Exception e) { + logger.warn("sendEvent: handler threw exception. handler: "+da+", exception: "+e, e); + } + } + + /** Note: no synchronization done in ViewStateManager, <b>must</b> be done externally **/ + public void handleActivated() { + logger.debug("handleActivated: activating the ViewStateManager"); + activated = true; + + if (previousView!=null && !isChanging) { + sendEvent(unInitializedEventListeners, newInitEvent(previousView)); + eventListeners.addAll(unInitializedEventListeners); + unInitializedEventListeners.clear(); + } + logger.debug("handleActivated: activated the ViewStateManager"); + } + + /** Simple factory method for creating a TOPOLOGY_INIT event with the given newView **/ + public static TopologyEvent newInitEvent(final BaseTopologyView newView) { + if (newView==null) { + throw new IllegalStateException("newView must not be null"); + } + if (!newView.isCurrent()) { + throw new IllegalStateException("newView must be current"); + } + return new TopologyEvent(Type.TOPOLOGY_INIT, null, newView); + } + + /** Simple factory method for creating a TOPOLOGY_CHANGING event with the given oldView **/ + public static TopologyEvent newChangingEvent(final BaseTopologyView oldView) { + if (oldView==null) { + throw new IllegalStateException("oldView must not be null"); + } + if (oldView.isCurrent()) { + throw new IllegalStateException("oldView must not be current"); + } + return new TopologyEvent(Type.TOPOLOGY_CHANGING, oldView, null); + } + + /** Simple factory method for creating a TOPOLOGY_CHANGED event with the given old and new views **/ + public static TopologyEvent newChangedEvent(final BaseTopologyView oldView, final BaseTopologyView newView) { + if (oldView==null) { + throw new IllegalStateException("oldView must not be null"); + } + if (oldView.isCurrent()) { + throw new IllegalStateException("oldView must not be current"); + } + if (newView==null) { + throw new IllegalStateException("newView must not be null"); + } + if (!newView.isCurrent()) { + throw new IllegalStateException("newView must be current"); + } + return new TopologyEvent(Type.TOPOLOGY_CHANGED, oldView, newView); + } + + /** + * Must be called when the corresponding service (typically a DiscoveryService implementation) + * is deactivated. + * <p> + * Will mark this manager as deactivated and flags the last available view as not current. + * <p> + * Note: no synchronization done in ViewStateManager, <b>must</b> be done externally + */ + public void handleDeactivated() { + logger.debug("handleDeactivated: deactivating the ViewStateManager"); + activated = false; + + if (previousView!=null) { + previousView.setNotCurrent(); + previousView = null; + } + isChanging = false; + + eventListeners.clear(); + unInitializedEventListeners.clear(); + logger.debug("handleDeactivated: deactivated the ViewStateManager"); + } + + /** Note: no synchronization done in ViewStateManager, <b>must</b> be done externally **/ + public void handleChanging() { + logger.debug("handleChanging: start"); + + if (isChanging) { + // if isChanging: then this is no news + // hence: return asap + logger.debug("handleChanging: was already changing - ignoring."); + return; + } + + // whether activated or not: set isChanging to true now + isChanging = true; + + if (!activated) { + // if not activated: we can only start sending events once activated + // hence returning here - after isChanging was set to true accordingly + + // note however, that if !activated, there should be no eventListeners yet + // all of them should be in unInitializedEventListeners at the moment + // waiting for activate() and handleNewTopologyView + logger.debug("handleChanging: not yet activated - ignoring."); + return; + } + + if (previousView==null) { + // then nothing further to do - this is a very early changing event + // before even the first view was available + logger.debug("handleChanging: no previousView set - ignoring."); + return; + } + + logger.debug("handleChanging: sending TOPOLOGY_CHANGING to initialized listeners"); + previousView.setNotCurrent(); + sendEvent(eventListeners, newChangingEvent(previousView)); + logger.debug("handleChanging: end"); + } + + /** Note: no synchronization done in ViewStateManager, <b>must</b> be done externally **/ + public void handleNewView(BaseTopologyView newView) { + if (logger.isDebugEnabled()) { + logger.debug("handleNewView: start, newView={}", newView); + } + if (!newView.isCurrent()) { + logger.error("handleNewView: newView must be current"); + throw new IllegalArgumentException("newView must be current"); + } + + if (!isChanging) { + // verify if there is actually a change between previousView and newView + // if there isn't, then there is not much point in sending a CHANGING/CHANGED tuple + // at all + if (previousView!=null && previousView.equals(newView)) { + // then nothing to send - the view has not changed, and we haven't + // sent the CHANGING event - so we should not do anything here + logger.debug("handleNewView: we were not in changing state and new view matches old, so - ignoring"); + return; + } + logger.debug("handleNewView: simulating a handleChanging as we were not in changing state"); + handleChanging(); + logger.debug("handleNewView: simulation of a handleChanging done"); + } + + // whether activated or not: set isChanging to false, first thing + isChanging = false; + + if (!activated) { + // then all we can do is to pass this on to previoueView + previousView = newView; + // other than that, we can't currently send any event, before activate + logger.debug("handleNewView: not yet activated - ignoring"); + return; + } + + if (previousView==null) { + // this is the first time handleNewTopologyView is called + + if (eventListeners.size()>0) { + logger.info("handleNewTopologyView: no previous view available even though listeners already got CHANGED event"); + } + + // otherwise this is the normal case where there are uninitialized event listeners waiting below + + } else { + logger.debug("handleNewView: sending TOPOLOGY_CHANGED to initialized listeners"); + previousView.setNotCurrent(); + sendEvent(eventListeners, newChangedEvent(previousView, newView)); + } + + if (unInitializedEventListeners.size()>0) { + // then there were bindTopologyEventListener calls coming in while + // we were in CHANGING state - so we must send those the INIT they were + // waiting for oh so long + if (logger.isDebugEnabled()) { + logger.debug("handleNewView: sending TOPOLOGY_INIT to uninitialized listeners ({})", + unInitializedEventListeners.size()); + } + sendEvent(unInitializedEventListeners, newInitEvent(newView)); + eventListeners.addAll(unInitializedEventListeners); + unInitializedEventListeners.clear(); + } + + previousView = newView; + logger.debug("handleNewView: end"); + } + +} diff --git a/src/main/java/org/apache/sling/discovery/commons/providers/package-info.java b/src/main/java/org/apache/sling/discovery/commons/providers/package-info.java new file mode 100644 index 0000000..c254e70 --- /dev/null +++ b/src/main/java/org/apache/sling/discovery/commons/providers/package-info.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * Provides commons utility for the Discovery API. + * + * @version 1.0.0 + */ +@Version("1.0.0") +package org.apache.sling.discovery.commons.providers; + +import aQute.bnd.annotation.Version; + diff --git a/src/test/java/org/apache/sling/discovery/commons/providers/TestViewStateManager.java b/src/test/java/org/apache/sling/discovery/commons/providers/TestViewStateManager.java new file mode 100644 index 0000000..3179438 --- /dev/null +++ b/src/test/java/org/apache/sling/discovery/commons/providers/TestViewStateManager.java @@ -0,0 +1,569 @@ +/* + * 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; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.apache.sling.discovery.ClusterView; +import org.apache.sling.discovery.InstanceDescription; +import org.apache.sling.discovery.InstanceFilter; +import org.apache.sling.discovery.TopologyEvent; +import org.apache.sling.discovery.TopologyEventListener; +import org.apache.sling.discovery.commons.providers.BaseTopologyView; +import org.apache.sling.discovery.commons.providers.ViewStateManager; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class TestViewStateManager { + + private class Listener implements TopologyEventListener { + + private List<TopologyEvent> events = new LinkedList<TopologyEvent>(); + private TopologyEvent lastEvent; + + public synchronized void handleTopologyEvent(TopologyEvent event) { + events.add(event); + lastEvent = event; + } + + public synchronized List<TopologyEvent> getEvents() { + return Collections.unmodifiableList(events); + } + + public synchronized int countEvents() { + return events.size(); + } + + public synchronized TopologyEvent getLastEvent() { + return lastEvent; + } + + public synchronized void clearEvents() { + events.clear(); + } + + public BaseTopologyView getLastView() { + if (lastEvent==null) { + return null; + } else { + switch(lastEvent.getType()) { + case TOPOLOGY_INIT: + case PROPERTIES_CHANGED: + case TOPOLOGY_CHANGED: { + return (BaseTopologyView) lastEvent.getNewView(); + } + case TOPOLOGY_CHANGING:{ + return (BaseTopologyView) lastEvent.getOldView(); + } + default: { + fail("no other types supported yet"); + } + } + } + return null; + } + + } + + private class View extends BaseTopologyView { + + private final BaseTopologyView clonedView; + + public View() { + clonedView = null; + } + + public View(BaseTopologyView clonedView) { + this.clonedView = clonedView; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof View)) { + return false; + } + final View other = (View) obj; + if (clonedView!=null) { + if (obj==clonedView) { + return true; + } + } else if (other.clonedView==this) { + return true; + } + return super.equals(obj); + } + + @Override + public int hashCode() { + if (clonedView!=null) { + return clonedView.hashCode(); + } + return super.hashCode(); + } + + public View addInstance() { + return this; + } + + public InstanceDescription getLocalInstance() { + throw new IllegalStateException("not yet implemented"); + } + + public Set<InstanceDescription> getInstances() { + throw new IllegalStateException("not yet implemented"); + } + + public Set<InstanceDescription> findInstances(InstanceFilter filter) { + throw new IllegalStateException("not yet implemented"); + } + + public Set<ClusterView> getClusterViews() { + throw new IllegalStateException("not yet implemented"); + } + } + + private ViewStateManager mgr; + + private Random defaultRandom; + + @Before + public void setup() throws Exception { + mgr = new ViewStateManager(); + defaultRandom = new Random(1234123412); // I want randomness yes, but deterministic, for some methods at least + } + + @After + public void teardown() throws Exception { + mgr = null; + defaultRandom= null; + } + + private void assertNoEvents(Listener listener) { + assertEquals(0, listener.countEvents()); + } + + private void assertEvents(Listener listener, TopologyEvent... events) { + assertEquals(events.length, listener.countEvents()); + for (int i = 0; i < events.length; i++) { + TopologyEvent e = events[i]; + assertEquals(e.getType(), listener.getEvents().get(i).getType()); + switch(e.getType()) { + case TOPOLOGY_INIT: { + assertNull(listener.getEvents().get(i).getOldView()); + assertEquals(e.getNewView(), listener.getEvents().get(i).getNewView()); + break; + } + case TOPOLOGY_CHANGING: { + assertEquals(e.getOldView(), listener.getEvents().get(i).getOldView()); + assertNull(listener.getEvents().get(i).getNewView()); + break; + } + case PROPERTIES_CHANGED: + case TOPOLOGY_CHANGED: { + assertEquals(e.getOldView(), listener.getEvents().get(i).getOldView()); + assertEquals(e.getNewView(), listener.getEvents().get(i).getNewView()); + break; + } + default: { + fail("no other type supported yet"); + } + } + } + listener.clearEvents(); + } + + /** does couple loops randomly calling handleChanging() (or not) and then handleNewView(). + * Note: random is passed to allow customizing and not hardcoding this method to a particular random **/ + private void randomEventLoop(final Random random, Listener... listeners) { + for(int i=0; i<100; i++) { + final boolean shouldCallChanging = random.nextBoolean(); + if (shouldCallChanging) { + // dont always do a changing + mgr.handleChanging(); + for(int j=0; j<listeners.length; j++) { + assertEvents(listeners[j], ViewStateManager.newChangingEvent(listeners[j].getLastView())); + } + } else { + for(int j=0; j<listeners.length; j++) { + assertNoEvents(listeners[j]); + } + } + final BaseTopologyView view = new View().addInstance(); + BaseTopologyView[] lastViews = new BaseTopologyView[listeners.length]; + for(int j=0; j<listeners.length; j++) { + lastViews[j] = listeners[j].getLastView(); + } + mgr.handleNewView(view); + if (!shouldCallChanging) { + // in that case I should still get a CHANGING - by contract + for(int j=0; j<listeners.length; j++) { + assertEvents(listeners[j], ViewStateManager.newChangingEvent(lastViews[j]), ViewStateManager.newChangedEvent(lastViews[j], view)); + } + } else { + for(int j=0; j<listeners.length; j++) { + assertEvents(listeners[j], ViewStateManager.newChangedEvent(lastViews[j], view)); + } + } + } + } + + @Test + public void testDuplicateListeners() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + mgr.bind(listener); // we should be generous and allow duplicate registration + assertTrue(mgr.unbind(listener)); + assertFalse(mgr.unbind(listener)); + + mgr.handleActivated(); + assertFalse(mgr.unbind(listener)); + mgr.bind(listener); + mgr.bind(listener); // we should be generous and allow duplicate registration + assertTrue(mgr.unbind(listener)); + assertFalse(mgr.unbind(listener)); + } + + @Test + public void testBindActivateChangingChanged() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleActivated(); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testBindChangingActivateChanged() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testBindChangingChangedActivate() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertNoEvents(listener); + mgr.handleActivated(); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testBindChangingChangedChangingActivate() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view2 = new View().addInstance(); + mgr.handleNewView(view2); + assertEvents(listener, ViewStateManager.newInitEvent(view2)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testBindChangedChangingActivate() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view2 = new View().addInstance(); + mgr.handleNewView(view2); + assertEvents(listener, ViewStateManager.newInitEvent(view2)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testActivateBindChangingChanged() throws Exception { + final Listener listener = new Listener(); + // first activate + mgr.handleActivated(); + assertNoEvents(listener); // paranoia + // then bind + mgr.bind(listener); + assertNoEvents(listener); // there was no changing or changed yet + mgr.handleChanging(); + assertNoEvents(listener); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testActivateChangingBindChanged() throws Exception { + final Listener listener = new Listener(); + // first activate + mgr.handleActivated(); + assertNoEvents(listener); // paranoia + mgr.handleChanging(); + assertNoEvents(listener); // no listener yet + // then bind + mgr.bind(listener); + assertNoEvents(listener); // no changed event yet + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testActivateChangingChangedBind() throws Exception { + final Listener listener = new Listener(); + // first activate + mgr.handleActivated(); + assertNoEvents(listener); // paranoia + mgr.handleChanging(); + assertNoEvents(listener); // no listener yet + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertNoEvents(listener); // no listener yet + // then bind + mgr.bind(listener); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testBindActivateBindChangingChanged() throws Exception { + final Listener listener1 = new Listener(); + final Listener listener2 = new Listener(); + + mgr.bind(listener1); + assertNoEvents(listener1); + mgr.handleActivated(); + assertNoEvents(listener1); + mgr.bind(listener2); + assertNoEvents(listener1); + assertNoEvents(listener2); + mgr.handleChanging(); + assertNoEvents(listener1); + assertNoEvents(listener2); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener1, ViewStateManager.newInitEvent(view)); + assertEvents(listener2, ViewStateManager.newInitEvent(view)); + + randomEventLoop(defaultRandom, listener1, listener2); + } + + @Test + public void testBindActivateChangingBindChanged() throws Exception { + final Listener listener1 = new Listener(); + final Listener listener2 = new Listener(); + + mgr.bind(listener1); + assertNoEvents(listener1); + mgr.handleActivated(); + assertNoEvents(listener1); + mgr.handleChanging(); + assertNoEvents(listener1); + mgr.bind(listener2); + assertNoEvents(listener1); + assertNoEvents(listener2); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener1, ViewStateManager.newInitEvent(view)); + assertEvents(listener2, ViewStateManager.newInitEvent(view)); + + randomEventLoop(defaultRandom, listener1, listener2); + } + + @Test + public void testActivateBindChangingDuplicateHandleNewView() throws Exception { + final Listener listener = new Listener(); + mgr.handleActivated(); + mgr.bind(listener); + mgr.handleChanging(); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + mgr.handleNewView(clone(view)); + assertNoEvents(listener); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testActivateBindChangingChangedBindDuplicateHandleNewView() throws Exception { + final Listener listener1 = new Listener(); + mgr.handleActivated(); + mgr.bind(listener1); + mgr.handleChanging(); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertEvents(listener1, ViewStateManager.newInitEvent(view)); + + final Listener listener2 = new Listener(); + mgr.bind(listener2); + mgr.handleNewView(clone(view)); + assertNoEvents(listener1); + assertEvents(listener2, ViewStateManager.newInitEvent(view)); + randomEventLoop(defaultRandom, listener1, listener2); + } + + @Test + public void testActivateChangedBindDuplicateHandleNewView() throws Exception { + final Listener listener = new Listener(); + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view = new View().addInstance(); + mgr.handleNewView(view); + assertNoEvents(listener); + mgr.bind(listener); + assertEvents(listener, ViewStateManager.newInitEvent(view)); + mgr.handleNewView(clone(view)); + assertNoEvents(listener); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testBindActivateChangedChanged() throws Exception { + final Listener listener = new Listener(); + mgr.handleActivated(); + assertNoEvents(listener); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + final BaseTopologyView view1 = new View().addInstance(); + mgr.handleNewView(view1); + assertEvents(listener, ViewStateManager.newInitEvent(view1)); + final BaseTopologyView view2 = new View().addInstance(); + mgr.handleNewView(view2); + assertEvents(listener, ViewStateManager.newChangingEvent(view1), ViewStateManager.newChangedEvent(view1, view2)); + randomEventLoop(defaultRandom, listener); + } + + @Test + public void testBindActivateChangedDeactivateChangingActivateChanged() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view1 = new View().addInstance(); + mgr.handleNewView(view1); + assertEvents(listener, ViewStateManager.newInitEvent(view1)); + mgr.handleDeactivated(); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + mgr.bind(listener); // need to bind again after deactivate + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view2 = new View().addInstance(); + mgr.handleNewView(view2); + assertEvents(listener, ViewStateManager.newInitEvent(view2)); + } + + @Test + public void testBindActivateChangedDeactivateChangedActivateChanged() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view1 = new View().addInstance(); + mgr.handleNewView(view1); + assertEvents(listener, ViewStateManager.newInitEvent(view1)); + mgr.handleDeactivated(); + assertNoEvents(listener); + final BaseTopologyView view2 = new View().addInstance(); + mgr.handleNewView(view2); + assertNoEvents(listener); + mgr.bind(listener); // need to bind again after deactivate + mgr.handleActivated(); + assertEvents(listener, ViewStateManager.newInitEvent(view2)); + final BaseTopologyView view3 = new View().addInstance(); + mgr.handleNewView(view3); + assertEvents(listener, ViewStateManager.newChangingEvent(view2), ViewStateManager.newChangedEvent(view2, view3)); + } + + @Test + public void testBindActivateChangedChangingDeactivateActivateChangingChanged() throws Exception { + final Listener listener = new Listener(); + mgr.bind(listener); + assertNoEvents(listener); + mgr.handleActivated(); + assertNoEvents(listener); + final BaseTopologyView view1 = new View().addInstance(); + mgr.handleNewView(view1); + assertEvents(listener, ViewStateManager.newInitEvent(view1)); + mgr.handleChanging(); + assertEvents(listener, ViewStateManager.newChangingEvent(view1)); + mgr.handleDeactivated(); + assertNoEvents(listener); + mgr.bind(listener); // need to bind again after deactivate + mgr.handleActivated(); + assertNoEvents(listener); + mgr.handleChanging(); + assertNoEvents(listener); + final BaseTopologyView view2 = new View().addInstance(); + mgr.handleNewView(view2); + assertEvents(listener, ViewStateManager.newInitEvent(view2)); + } + + private BaseTopologyView clone(final BaseTopologyView view) { + return new View(view); + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
