Implement CordConfigEvent

Change-Id: I5d42eb4a345a641fe0b1271eecdfba4151c80248
diff --git a/src/main/java/org/opencord/cordconfig/CordConfigEvent.java b/src/main/java/org/opencord/cordconfig/CordConfigEvent.java
new file mode 100644
index 0000000..4181c7d
--- /dev/null
+++ b/src/main/java/org/opencord/cordconfig/CordConfigEvent.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.opencord.cordconfig;
+
+import org.onosproject.event.AbstractEvent;
+import org.joda.time.LocalDateTime;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Describes an CORD config event.
+ */
+public class CordConfigEvent extends AbstractEvent<CordConfigEvent.Type, Object> {
+    private final Object prevSubject;
+
+    public enum Type {
+        /**
+         * Indicates a new access agent has been added.
+         * Event subject should be AccessAgentData.
+         */
+        ACCESS_AGENT_ADDED,
+
+        /**
+         * Indicates an access agent has been updated.
+         * Event subject and prevSubject should be AccessAgentData.
+         */
+        ACCESS_AGENT_UPDATED,
+
+        /**
+         * Indicates an access agent has been removed.
+         * Event prevSubject should be AccessAgentData.
+         */
+        ACCESS_AGENT_REMOVED,
+
+        /**
+         * Indicates a new access device has been added.
+         * Event subject should be AccessDeviceData.
+         */
+        ACCESS_DEVICE_ADDED,
+
+        /**
+         * Indicates an access device has been updated.
+         * Event subject and prevSubject should be AccessDeviceData.
+         */
+        ACCESS_DEVICE_UPDATED,
+
+        /**
+         * Indicates an access device has been removed.
+         * Event prevSubject should be AccessDeviceData.
+         */
+        ACCESS_DEVICE_REMOVED,
+    }
+
+    /**
+     * Creates an CORD config event with type and subject.
+     *
+     * @param type event type
+     * @param subject subject CORD config
+     */
+    public CordConfigEvent(Type type, Object subject) {
+        this(type, subject, null);
+    }
+
+    /**
+     * Creates an CORD config event with type, subject and time of event.
+     *
+     * @param type event type
+     * @param subject subject CORD config
+     * @param time time of event
+     */
+    public CordConfigEvent(Type type, Object subject, long time) {
+        this(type, subject, null, time);
+    }
+
+    /**
+     * Creates an CORD config event with type, subject and previous subject.
+     *
+     * @param type event type
+     * @param subject subject CORD config
+     * @param prevSubject previous CORD config subject
+     */
+    public CordConfigEvent(Type type, Object subject, Object prevSubject) {
+        super(type, subject);
+        this.prevSubject = prevSubject;
+    }
+
+    /**
+     * Creates an CORD config event with type, subject, previous subject and time.
+     *
+     * @param type event type
+     * @param subject subject CORD config
+     * @param prevSubject previous CORD config subject
+     * @param time time of event
+     */
+    public CordConfigEvent(Type type, Object subject, Object prevSubject, long time) {
+        super(type, subject, time);
+        this.prevSubject = prevSubject;
+    }
+
+    /**
+     * Returns the previous CORD config subject.
+     *
+     * @return previous subject of CORD config or null if previous subject does not exist.
+     */
+    public Object prevSubject() {
+        return prevSubject;
+    }
+
+    @Override
+    public String toString() {
+        if (prevSubject == null) {
+            return super.toString();
+        }
+        return toStringHelper(this)
+                .add("time", new LocalDateTime(time()))
+                .add("type", type())
+                .add("subject", subject())
+                .add("prevSubject", prevSubject)
+                .toString();
+    }
+}
diff --git a/src/main/java/org/opencord/cordconfig/CordConfigListener.java b/src/main/java/org/opencord/cordconfig/CordConfigListener.java
new file mode 100644
index 0000000..795e2be
--- /dev/null
+++ b/src/main/java/org/opencord/cordconfig/CordConfigListener.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.opencord.cordconfig;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for CORD config events.
+ */
+public interface CordConfigListener extends EventListener<CordConfigEvent> {
+}
diff --git a/src/main/java/org/opencord/cordconfig/access/CordConfig.java b/src/main/java/org/opencord/cordconfig/CordConfigManager.java
similarity index 60%
rename from src/main/java/org/opencord/cordconfig/access/CordConfig.java
rename to src/main/java/org/opencord/cordconfig/CordConfigManager.java
index 3393e5f..eed8694 100644
--- a/src/main/java/org/opencord/cordconfig/access/CordConfig.java
+++ b/src/main/java/org/opencord/cordconfig/CordConfigManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.opencord.cordconfig.access;
+package org.opencord.cordconfig;
 
 import com.google.common.collect.ImmutableSet;
 import org.apache.felix.scr.annotations.Activate;
@@ -23,12 +23,19 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onosproject.event.ListenerRegistry;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
 import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.config.basics.SubjectFactories;
+import org.opencord.cordconfig.access.AccessAgentConfig;
+import org.opencord.cordconfig.access.AccessAgentData;
+import org.opencord.cordconfig.access.AccessDeviceConfig;
+import org.opencord.cordconfig.access.AccessDeviceData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.Map;
 import java.util.Optional;
@@ -42,7 +49,9 @@
  */
 @Service
 @Component(immediate = true)
-public class CordConfig implements CordConfigService {
+public class CordConfigManager extends ListenerRegistry<CordConfigEvent, CordConfigListener>
+        implements CordConfigService {
+    private static Logger log = LoggerFactory.getLogger(CordConfigManager.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigRegistry networkConfig;
@@ -112,10 +121,21 @@
     private void addAccessDevice(AccessDeviceConfig config) {
         AccessDeviceData accessDevice = config.getAccessDevice();
         accessDevices.put(accessDevice.deviceId(), accessDevice);
+        process(new CordConfigEvent(CordConfigEvent.Type.ACCESS_DEVICE_ADDED, accessDevice));
     }
 
-    private void removeAccessDeviceConfig(DeviceId subject) {
-        accessDevices.remove(subject);
+    private void updateAccessDevice(AccessDeviceConfig config, AccessDeviceConfig prevConfig) {
+        AccessDeviceData prevAccessDevice = prevConfig.getAccessDevice();
+        accessDevices.remove(prevConfig.subject());
+        AccessDeviceData accessDevice = config.getAccessDevice();
+        accessDevices.put(accessDevice.deviceId(), accessDevice);
+        process(new CordConfigEvent(CordConfigEvent.Type.ACCESS_DEVICE_UPDATED, accessDevice, prevAccessDevice));
+    }
+
+    private void removeAccessDevice(AccessDeviceConfig prevConfig) {
+        AccessDeviceData prevAccessDevice = prevConfig.getAccessDevice();
+        accessDevices.remove(prevConfig.subject());
+        process(new CordConfigEvent(CordConfigEvent.Type.ACCESS_DEVICE_REMOVED, prevAccessDevice));
     }
 
     private void addAccessAgentConfig(DeviceId subject) {
@@ -129,10 +149,21 @@
     private void addAccessAgent(AccessAgentConfig config) {
         AccessAgentData accessAgent = config.getAgent();
         accessAgents.put(accessAgent.deviceId(), accessAgent);
+        process(new CordConfigEvent(CordConfigEvent.Type.ACCESS_AGENT_ADDED, accessAgent, null));
     }
 
-    private void removeAccessAgentConfig(DeviceId subject) {
-        accessAgents.remove(subject);
+    private void updateAccessAgent(AccessAgentConfig config, AccessAgentConfig prevConfig) {
+        AccessAgentData prevAccessAgent = prevConfig.getAgent();
+        accessAgents.remove(prevConfig.subject());
+        AccessAgentData accessAgent = config.getAgent();
+        accessAgents.put(accessAgent.deviceId(), accessAgent);
+        process(new CordConfigEvent(CordConfigEvent.Type.ACCESS_AGENT_UPDATED, accessAgent, prevAccessAgent));
+    }
+
+    private void removeAccessAgent(AccessAgentConfig prevConfig) {
+        AccessAgentData prevAccessAgent = prevConfig.getAgent();
+        accessAgents.remove(prevConfig.subject());
+        process(new CordConfigEvent(CordConfigEvent.Type.ACCESS_AGENT_REMOVED, null, prevAccessAgent));
     }
 
     @Override
@@ -160,26 +191,38 @@
     private class InternalNetworkConfigListener implements NetworkConfigListener {
         @Override
         public void event(NetworkConfigEvent event) {
-            switch (event.type()) {
-            case CONFIG_ADDED:
-            case CONFIG_UPDATED:
-                if (event.configClass().equals(ACCESS_DEVICE_CONFIG_CLASS)) {
-                    addAccessDeviceConfig((DeviceId) event.subject());
-                } else if (event.configClass().equals(ACCESS_AGENT_CONFIG_CLASS)) {
-                    addAccessAgentConfig((DeviceId) event.subject());
+            if (event.configClass().equals(ACCESS_DEVICE_CONFIG_CLASS)) {
+                AccessDeviceConfig config = (AccessDeviceConfig) event.config().orElse(null);
+                AccessDeviceConfig prevConfig = (AccessDeviceConfig) event.prevConfig().orElse(null);
+                switch (event.type()) {
+                    case CONFIG_ADDED:
+                        addAccessDevice(config);
+                        break;
+                    case CONFIG_UPDATED:
+                        updateAccessDevice(config, prevConfig);
+                        break;
+                    case CONFIG_REMOVED:
+                        removeAccessDevice(prevConfig);
+                        break;
+                    default:
+                        break;
                 }
-                break;
-            case CONFIG_REMOVED:
-                if (event.configClass().equals(ACCESS_DEVICE_CONFIG_CLASS)) {
-                    removeAccessDeviceConfig((DeviceId) event.subject());
-                } else if (event.configClass().equals(ACCESS_AGENT_CONFIG_CLASS)) {
-                    removeAccessAgentConfig((DeviceId) event.subject());
+            } else if (event.configClass().equals(ACCESS_AGENT_CONFIG_CLASS)) {
+                AccessAgentConfig config = (AccessAgentConfig) event.config().orElse(null);
+                AccessAgentConfig prevConfig = (AccessAgentConfig) event.prevConfig().orElse(null);
+                switch (event.type()) {
+                    case CONFIG_ADDED:
+                        addAccessAgent(config);
+                        break;
+                    case CONFIG_UPDATED:
+                        updateAccessAgent(config, prevConfig);
+                        break;
+                    case CONFIG_REMOVED:
+                        removeAccessAgent(prevConfig);
+                        break;
+                    default:
+                        break;
                 }
-                break;
-            case CONFIG_REGISTERED:
-            case CONFIG_UNREGISTERED:
-            default:
-                break;
             }
         }
     }
diff --git a/src/main/java/org/opencord/cordconfig/access/CordConfigService.java b/src/main/java/org/opencord/cordconfig/CordConfigService.java
similarity index 83%
rename from src/main/java/org/opencord/cordconfig/access/CordConfigService.java
rename to src/main/java/org/opencord/cordconfig/CordConfigService.java
index fc2ed66..5d17c85 100644
--- a/src/main/java/org/opencord/cordconfig/access/CordConfigService.java
+++ b/src/main/java/org/opencord/cordconfig/CordConfigService.java
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-package org.opencord.cordconfig.access;
+package org.opencord.cordconfig;
 
+import org.onosproject.event.ListenerService;
 import org.onosproject.net.DeviceId;
+import org.opencord.cordconfig.access.AccessAgentData;
+import org.opencord.cordconfig.access.AccessDeviceData;
 
 import java.util.Optional;
 import java.util.Set;
@@ -24,7 +27,8 @@
 /**
  * Provides access to the common CORD configuration.
  */
-public interface CordConfigService {
+public interface CordConfigService
+        extends ListenerService<CordConfigEvent, CordConfigListener> {
 
     /**
      * Retrieves the set of all access devices in the system.
diff --git a/src/main/java/org/opencord/cordconfig/access/AccessAgentData.java b/src/main/java/org/opencord/cordconfig/access/AccessAgentData.java
index 66ec357..6ac33d2 100644
--- a/src/main/java/org/opencord/cordconfig/access/AccessAgentData.java
+++ b/src/main/java/org/opencord/cordconfig/access/AccessAgentData.java
@@ -16,6 +16,7 @@
 
 package org.opencord.cordconfig.access;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableMap;
 import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.packet.MacAddress;
@@ -123,4 +124,14 @@
 
         return Optional.of(sortedOltChips.get(index).getKey());
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("oltMacInfo", oltMacInfo)
+                .add("agentMac", agentMac)
+                .add("vtnLocation", vtnLocation)
+                .add("deviceId", deviceId)
+                .toString();
+    }
 }
diff --git a/src/main/java/org/opencord/cordconfig/access/AccessDeviceData.java b/src/main/java/org/opencord/cordconfig/access/AccessDeviceData.java
index a5b7895..40326ec 100644
--- a/src/main/java/org/opencord/cordconfig/access/AccessDeviceData.java
+++ b/src/main/java/org/opencord/cordconfig/access/AccessDeviceData.java
@@ -16,6 +16,7 @@
 
 package org.opencord.cordconfig.access;
 
+import com.google.common.base.MoreObjects;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -88,4 +89,14 @@
     public Optional<VlanId> defaultVlan() {
         return defaultVlan;
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("deviceId", deviceId)
+                .add("uplink", uplink)
+                .add("vlan", vlan)
+                .add("defaultVlan", defaultVlan)
+                .toString();
+    }
 }
diff --git a/src/main/java/org/opencord/cordconfig/access/package-info.java b/src/main/java/org/opencord/cordconfig/access/package-info.java
index 9539f11..4c0405a 100644
--- a/src/main/java/org/opencord/cordconfig/access/package-info.java
+++ b/src/main/java/org/opencord/cordconfig/access/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * Meta Application for hosting common cord configuration classes.
+ * CORD access configuration classes.
  */
 package org.opencord.cordconfig.access;
\ No newline at end of file
diff --git a/src/main/java/org/opencord/cordconfig/package-info.java b/src/main/java/org/opencord/cordconfig/package-info.java
new file mode 100644
index 0000000..3a73a88
--- /dev/null
+++ b/src/main/java/org/opencord/cordconfig/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.
+ */
+
+/**
+ * Meta Application for hosting common cord configuration classes.
+ */
+package org.opencord.cordconfig;
\ No newline at end of file