sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From romb...@apache.org
Subject [sling-org-apache-sling-discovery-commons] 03/38: 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)
Date Tue, 07 Nov 2017 09:26:06 GMT
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 <stefanegli@apache.org>
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
"commits@sling.apache.org" <commits@sling.apache.org>.

Mime
View raw message