Pushed Switch Mastership Events into the Topology event channel.
Needed for ONOS-1729
For now those events are not used.

Also, added new TopologyElement attribute type: TYPE_ALL_LAYERS
which applies to the Mastership Events.

NOTE: Currently, those events are intercepted within the
Floodlight Controller.java class. The interception point might be moved
once it becomes clear whether the event origin should be the mastership
election mechanism or the "role change request" accepted by the switch.
In addition, the interception point needs to be moved/ported as appropriate
once we move to the newer 1.3 OpenFlow driver implementation and eventloop.

Change-Id: Iff06ed5aee867c428a8378e31f9d51dbe3e6b978
diff --git a/src/main/java/net/onrc/onos/core/topology/MastershipEvent.java b/src/main/java/net/onrc/onos/core/topology/MastershipEvent.java
new file mode 100644
index 0000000..01323ba
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/MastershipEvent.java
@@ -0,0 +1,145 @@
+package net.onrc.onos.core.topology;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import net.floodlightcontroller.core.IFloodlightProviderService.Role;
+import net.onrc.onos.core.topology.web.serializers.MastershipEventSerializer;
+import net.onrc.onos.core.util.Dpid;
+
+import org.apache.commons.lang.Validate;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * Self-contained Switch Mastership event Object.
+ * <p/>
+ * TODO: Rename to match what it is. (Switch/Port/Link/Host)Snapshot?
+ * FIXME: Current implementation directly use this object as
+ *        Replication message, but should be sending update operation info.
+ */
+@JsonSerialize(using = MastershipEventSerializer.class)
+public class MastershipEvent extends TopologyElement<MastershipEvent> {
+
+    private final Dpid dpid;
+    private final String onosInstanceId;
+    private final Role role;
+
+    /**
+     * Default constructor for Serializer to use.
+     */
+    @Deprecated
+    protected MastershipEvent() {
+        dpid = null;
+        onosInstanceId = null;
+        role = Role.SLAVE;              // Default role is SLAVE
+    }
+
+    /**
+     * Creates the Switch Mastership object.
+     *
+     * @param dpid the Switch DPID
+     * @param onosInstanceId the ONOS Instance ID
+     * @param role the ONOS instance role for the switch.
+     */
+    public MastershipEvent(Dpid dpid, String onosInstanceId, Role role) {
+        Validate.notNull(dpid);
+        Validate.notNull(onosInstanceId);
+
+        this.dpid = dpid;
+        this.onosInstanceId = onosInstanceId;
+        this.role = role;
+    }
+
+    /**
+     * Creates an unfrozen copy of given Object.
+     *
+     * @param original to make copy of.
+     */
+    public MastershipEvent(MastershipEvent original) {
+        super(original);
+        this.dpid = original.dpid;
+        this.onosInstanceId = original.onosInstanceId;
+        this.role = original.role;
+    }
+
+    /**
+     * Gets the Switch DPID.
+     *
+     * @return the Switch DPID.
+     */
+    public Dpid getDpid() {
+        return dpid;
+    }
+
+    /**
+     * Gets the ONOS Instance ID.
+     *
+     * @return the ONOS Instance ID.
+     */
+    public String getOnosInstanceId() {
+        return onosInstanceId;
+    }
+
+    /**
+     * Gets the ONOS Controller Role for the Switch.
+     *
+     * @return the ONOS Controller Role for the Switch.
+     */
+    public Role getRole() {
+        return role;
+    }
+
+    @Override
+    public String toString() {
+        return "[MastershipEvent " + getDpid() + "@" + getOnosInstanceId() +
+            "->" + getRole() + "]";
+    }
+
+    public byte[] getID() {
+        String keyStr = "M" + getDpid() + "@" + getOnosInstanceId();
+        return keyStr.getBytes(StandardCharsets.UTF_8);
+    }
+
+    public ByteBuffer getIDasByteBuffer() {
+        ByteBuffer buf = ByteBuffer.wrap(getID());
+        return buf;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(dpid, onosInstanceId);
+    }
+
+    /**
+     * Compares two MastershipEvent objects.
+     * MastershipEvent objects are equal if they have same DPID and same
+     * ONOS Instance ID.
+     *
+     * @param obj another object to compare to this
+     * @returns true if equal, false otherwise false.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        // compare attributes
+        if (!super.equals(obj)) {
+            return false;
+        }
+
+        MastershipEvent other = (MastershipEvent) obj;
+        return dpid.equals(other.dpid) &&
+            onosInstanceId.equals(other.onosInstanceId);
+    }
+}
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyDiscoveryInterface.java b/src/main/java/net/onrc/onos/core/topology/TopologyDiscoveryInterface.java
index d9c5c38..289ed05 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyDiscoveryInterface.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyDiscoveryInterface.java
@@ -54,14 +54,28 @@
     /**
      * Host discovered event.
      *
-     * @param hostEvent the device event.
+     * @param hostEvent the host event.
      */
     public void putHostDiscoveryEvent(HostEvent hostEvent);
 
     /**
      * Host removed event.
      *
-     * @param hostEvent the device event.
+     * @param hostEvent the host event.
      */
     public void removeHostDiscoveryEvent(HostEvent hostEvent);
+
+    /**
+     * Switch Mastership updated event.
+     *
+     * @param mastershipEvent the mastership event.
+     */
+    public void putSwitchMastershipEvent(MastershipEvent mastershipEvent);
+
+    /**
+     * Switch Mastership removed event.
+     *
+     * @param mastershipEvent the mastership event.
+     */
+    public void removeSwitchMastershipEvent(MastershipEvent mastershipEvent);
 }
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyElement.java b/src/main/java/net/onrc/onos/core/topology/TopologyElement.java
index f70e7f5..bb32958 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyElement.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyElement.java
@@ -26,13 +26,20 @@
      */
     public static final String TYPE = "type";
     /**
-     * Attribute "type" value representing that the object belongs to Packet layer.
+     * Attribute "type" value representing that the object belongs to Packet
+     * layer.
      */
     public static final String TYPE_PACKET_LAYER = "packet";
     /**
-     * Attribute "type" value representing that the object belongs to Optical layer.
+     * Attribute "type" value representing that the object belongs to Optical
+     * layer.
      */
     public static final String TYPE_OPTICAL_LAYER = "optical";
+    /**
+     * Attribute "type" value representing that the object belongs to all
+     * layers.
+     */
+    public static final String TYPE_ALL_LAYERS = "AllLayers";
 
     public static final String ELEMENT_CONFIG_STATE = "ConfigState";
 
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java b/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java
index 81e261d..cdae99c 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java
@@ -7,14 +7,15 @@
  * Self-contained Topology event Object
  * <p/>
  * TODO: For now the topology event contains one of the following events:
- * Switch, Port, Link, Host. In the future it will contain multiple events
- * in a single transaction.
+ * Switch, Port, Link, Host, Switch Mastership. In the future it will contain
+ * multiple events in a single transaction.
  */
 public class TopologyEvent {
-    SwitchEvent switchEvent = null;        // Set for Switch event
-    PortEvent portEvent = null;            // Set for Port event
-    LinkEvent linkEvent = null;            // Set for Link event
-    HostEvent hostEvent = null;        // Set for Host event
+    SwitchEvent switchEvent = null;             // Set for Switch event
+    PortEvent portEvent = null;                 // Set for Port event
+    LinkEvent linkEvent = null;                 // Set for Link event
+    HostEvent hostEvent = null;                 // Set for Host event
+    MastershipEvent mastershipEvent = null;     // Set for Mastership event
 
     /**
      * Default constructor.
@@ -59,6 +60,15 @@
     }
 
     /**
+     * Constructor for given Switch Mastership event.
+     *
+     * @param mastershipEvent the Switch Mastership event to use.
+     */
+    TopologyEvent(MastershipEvent mastershipEvent) {
+        this.mastershipEvent = mastershipEvent;
+    }
+
+    /**
      * Check if all events contained are equal.
      *
      * @param obj TopologyEvent to compare against
@@ -81,12 +91,14 @@
         return Objects.equals(switchEvent, other.switchEvent) &&
                 Objects.equals(portEvent, other.portEvent) &&
                 Objects.equals(linkEvent, other.linkEvent) &&
-                Objects.equals(hostEvent, other.hostEvent);
+                Objects.equals(hostEvent, other.hostEvent) &&
+                Objects.equals(mastershipEvent, other.mastershipEvent);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(switchEvent, portEvent, linkEvent, hostEvent);
+        return Objects.hash(switchEvent, portEvent, linkEvent, hostEvent,
+                            mastershipEvent);
     }
 
     /**
@@ -108,6 +120,9 @@
         if (hostEvent != null) {
             return hostEvent.toString();
         }
+        if (mastershipEvent != null) {
+            return mastershipEvent.toString();
+        }
         return "[Empty TopologyEvent]";
     }
 
@@ -129,6 +144,9 @@
         if (hostEvent != null) {
             return hostEvent.getID();
         }
+        if (mastershipEvent != null) {
+            return mastershipEvent.getID();
+        }
         throw new IllegalStateException("Invalid TopologyEvent ID");
     }
 }
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
index b12bc0f..e733c68 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
@@ -263,6 +263,10 @@
             Map<ByteBuffer, LinkEvent> removedLinkEvents = new HashMap<>();
             Map<ByteBuffer, HostEvent> addedHostEvents = new HashMap<>();
             Map<ByteBuffer, HostEvent> removedHostEvents = new HashMap<>();
+            Map<ByteBuffer, MastershipEvent> addedMastershipEvents =
+                new HashMap<>();
+            Map<ByteBuffer, MastershipEvent> removedMastershipEvents =
+                new HashMap<>();
 
             //
             // Classify and suppress matching events
@@ -273,6 +277,7 @@
                 PortEvent portEvent = topologyEvent.portEvent;
                 LinkEvent linkEvent = topologyEvent.linkEvent;
                 HostEvent hostEvent = topologyEvent.hostEvent;
+                MastershipEvent mastershipEvent = topologyEvent.mastershipEvent;
 
                 //
                 // Extract the events
@@ -306,6 +311,11 @@
                             removedHostEvents.remove(id);
                             reorderedAddedHostEvents.remove(id);
                         }
+                        if (mastershipEvent != null) {
+                            ByteBuffer id = mastershipEvent.getIDasByteBuffer();
+                            addedMastershipEvents.put(id, mastershipEvent);
+                            removedMastershipEvents.remove(id);
+                        }
                         break;
                     case ENTRY_REMOVE:
                         log.debug("Topology event ENTRY_REMOVE: {}", topologyEvent);
@@ -333,6 +343,11 @@
                             removedHostEvents.put(id, hostEvent);
                             reorderedAddedHostEvents.remove(id);
                         }
+                        if (mastershipEvent != null) {
+                            ByteBuffer id = mastershipEvent.getIDasByteBuffer();
+                            addedMastershipEvents.remove(id);
+                            removedMastershipEvents.put(id, mastershipEvent);
+                        }
                         break;
                     default:
                         log.error("Unknown topology event {}",
@@ -350,8 +365,15 @@
                 // Apply the classified events.
                 //
                 // Apply the "add" events in the proper order:
-                //   switch, port, link, host
+                //   mastership, switch, port, link, host
                 //
+                // TODO: Currently, the Mastership events are not used,
+                // so their processing ordering is not important (undefined).
+                //
+                for (MastershipEvent mastershipEvent :
+                         addedMastershipEvents.values()) {
+                    processAddedMastershipEvent(mastershipEvent);
+                }
                 for (SwitchEvent switchEvent : addedSwitchEvents.values()) {
                     addSwitch(switchEvent);
                 }
@@ -366,7 +388,7 @@
                 }
                 //
                 // Apply the "remove" events in the reverse order:
-                //   host, link, port, switch
+                //   host, link, port, switch, mastership
                 //
                 for (HostEvent hostEvent : removedHostEvents.values()) {
                     removeHost(hostEvent);
@@ -380,6 +402,10 @@
                 for (SwitchEvent switchEvent : removedSwitchEvents.values()) {
                     removeSwitch(switchEvent);
                 }
+                for (MastershipEvent mastershipEvent :
+                         removedMastershipEvents.values()) {
+                    processRemovedMastershipEvent(mastershipEvent);
+                }
 
                 //
                 // Apply reordered events
@@ -888,6 +914,29 @@
     //
 
     /**
+     * Mastership updated event.
+     *
+     * @param mastershipEvent the mastership event.
+     */
+    @Override
+    public void putSwitchMastershipEvent(MastershipEvent mastershipEvent) {
+        // Send out notification
+        TopologyEvent topologyEvent = new TopologyEvent(mastershipEvent);
+        eventChannel.addEntry(topologyEvent.getID(), topologyEvent);
+    }
+
+    /**
+     * Mastership removed event.
+     *
+     * @param mastershipEvent the mastership event.
+     */
+    @Override
+    public void removeSwitchMastershipEvent(MastershipEvent mastershipEvent) {
+        // Send out notification
+        eventChannel.removeEntry(mastershipEvent.getID());
+    }
+
+    /**
      * Adds a switch to the topology replica.
      *
      * @param switchEvent the SwitchEvent with the switch to add.
@@ -1272,6 +1321,30 @@
     }
 
     /**
+     * Processes added Switch Mastership event.
+     *
+     * @param mastershipEvent the MastershipEvent to process.
+     */
+    @GuardedBy("topology.writeLock")
+    private void processAddedMastershipEvent(MastershipEvent mastershipEvent) {
+        log.debug("Processing added Mastership event {}",
+                  mastershipEvent);
+        // TODO: Not implemented/used yet.
+    }
+
+    /**
+     * Processes removed Switch Mastership event.
+     *
+     * @param mastershipEvent the MastershipEvent to process.
+     */
+    @GuardedBy("topology.writeLock")
+    private void processRemovedMastershipEvent(MastershipEvent mastershipEvent) {
+        log.debug("Processing removed Mastership event {}",
+                  mastershipEvent);
+        // TODO: Not implemented/used yet.
+    }
+
+    /**
      * Read the whole topology from the database.
      *
      * @return a collection of EventEntry-encapsulated Topology Events for
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java b/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
index f11c06d..3b6a5e9 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
@@ -7,6 +7,7 @@
 import java.util.concurrent.TimeUnit;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IFloodlightProviderService.Role;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
@@ -14,6 +15,7 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.onrc.onos.api.registry.ILocalSwitchMastershipListener;
 import net.onrc.onos.core.hostmanager.Host;
 import net.onrc.onos.core.hostmanager.IHostListener;
 import net.onrc.onos.core.hostmanager.IHostService;
@@ -42,7 +44,8 @@
         IOFSwitchPortListener,
         ILinkDiscoveryListener,
         IFloodlightModule,
-        IHostListener {
+        IHostListener,
+        ILocalSwitchMastershipListener {
     private static final Logger log =
             LoggerFactory.getLogger(TopologyPublisher.class);
 
@@ -331,6 +334,8 @@
         hostService = context.getServiceImpl(IHostService.class);
 
         topologyService = context.getServiceImpl(ITopologyService.class);
+
+        floodlightProvider.addLocalSwitchMastershipListener(this);
     }
 
     @Override
@@ -387,4 +392,35 @@
         event.freeze();
         topologyDiscoveryInterface.removeHostDiscoveryEvent(event);
     }
+
+    @Override
+    public void controllerRoleChanged(Dpid dpid, Role role) {
+        log.debug("Local switch controller mastership role changed: dpid = {} role = {}", dpid, role);
+        MastershipEvent mastershipEvent =
+            new MastershipEvent(dpid, registryService.getControllerId(), role);
+        // FIXME should be merging, with existing attrs, etc..
+        // TODO define attr name as constant somewhere.
+        // TODO populate appropriate attributes.
+        mastershipEvent.createStringAttribute(TopologyElement.TYPE,
+                                              TopologyElement.TYPE_ALL_LAYERS);
+        mastershipEvent.freeze();
+        topologyDiscoveryInterface.putSwitchMastershipEvent(mastershipEvent);
+    }
+
+    @Override
+    public void switchDisconnected(Dpid dpid) {
+        log.debug("Local switch disconnected: dpid = {} role = {}", dpid);
+
+        Role role = Role.SLAVE;         // TODO: Should be Role.UNKNOWN
+
+        MastershipEvent mastershipEvent =
+            new MastershipEvent(dpid, registryService.getControllerId(), role);
+        // FIXME should be merging, with existing attrs, etc..
+        // TODO define attr name as constant somewhere.
+        // TODO populate appropriate attributes.
+        mastershipEvent.createStringAttribute(TopologyElement.TYPE,
+                                              TopologyElement.TYPE_ALL_LAYERS);
+        mastershipEvent.freeze();
+        topologyDiscoveryInterface.removeSwitchMastershipEvent(mastershipEvent);
+    }
 }
diff --git a/src/main/java/net/onrc/onos/core/topology/web/serializers/MastershipEventSerializer.java b/src/main/java/net/onrc/onos/core/topology/web/serializers/MastershipEventSerializer.java
new file mode 100644
index 0000000..65198f0
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/web/serializers/MastershipEventSerializer.java
@@ -0,0 +1,55 @@
+package net.onrc.onos.core.topology.web.serializers;
+
+import net.onrc.onos.core.topology.MastershipEvent;
+import net.onrc.onos.core.topology.TopologyElement;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+
+import java.io.IOException;
+
+/**
+ * JSON serializer for MastershipEvents.
+ */
+public class MastershipEventSerializer extends SerializerBase<MastershipEvent> {
+
+    /**
+     * Public constructor - just calls its super class constructor.
+     */
+    public MastershipEventSerializer() {
+        super(MastershipEvent.class);
+    }
+
+    /**
+     * Serializes a MastershipEvent object.
+     *
+     * @param mastershipEvent MastershipEvent to serialize
+     * @param jsonGenerator generator to add the serialized object to
+     * @param serializerProvider not used
+     * @throws IOException if the JSON serialization fails
+     */
+    @Override
+    public void serialize(final MastershipEvent mastershipEvent,
+                          final JsonGenerator jsonGenerator,
+                          final SerializerProvider serializerProvider)
+            throws IOException {
+
+        //
+        // TODO: For now, the JSON format of the serialized output should
+        // be same as the JSON format of the corresponding class Mastership
+        // (if such class exists).
+        // In the future, we will use a single serializer.
+        //
+
+        jsonGenerator.writeStartObject();
+        jsonGenerator.writeStringField(TopologyElement.TYPE, mastershipEvent.getType());
+        jsonGenerator.writeStringField("dpid",
+                                       mastershipEvent.getDpid().toString());
+        jsonGenerator.writeStringField("onosInstanceId",
+                                       mastershipEvent.getOnosInstanceId());
+        jsonGenerator.writeStringField("role",
+                                       mastershipEvent.getRole().name());
+        jsonGenerator.writeEndObject();
+    }
+}