RESTCONF notification changes

* Added a callback function mechanism to the RESTCONF notification
  listener. Applications can register their callbacks to a listener.
  This is to allow multiple SB providers share one generic listener.
  while still being able to process app-specific events.
* Created a default RESTCONF notification listener implementation.
* Refactored TE provider code to allow TE-topology and TE-tunnel share one
  RESTCONF event listener

Change-Id: I26dc4972683fcda3eefacde131353312809aa95e
diff --git a/providers/ietfte/utils/BUCK b/providers/ietfte/utils/BUCK
index 7817d31..0964039 100644
--- a/providers/ietfte/utils/BUCK
+++ b/providers/ietfte/utils/BUCK
@@ -2,6 +2,7 @@
     '//lib:CORE_DEPS',
     '//apps/yms/api:onos-apps-yms-api',
     '//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
+    '//protocols/restconf/client/api:onos-protocols-restconf-client-api',
 ]
 
 TEST_DEPS = [
diff --git a/providers/ietfte/utils/pom.xml b/providers/ietfte/utils/pom.xml
index 35c727e..3174a8b 100644
--- a/providers/ietfte/utils/pom.xml
+++ b/providers/ietfte/utils/pom.xml
@@ -37,10 +37,13 @@
         </dependency>
         <dependency>
             <groupId>org.onosproject</groupId>
+            <artifactId>onos-restconf-client-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
             <artifactId>onos-restconf-server-utils</artifactId>
             <version>${project.version}</version>
         </dependency>
     </dependencies>
-
-
-</project>
\ No newline at end of file
+</project>
diff --git a/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/RestconfNotificationEventProcessor.java b/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/RestconfNotificationEventProcessor.java
new file mode 100644
index 0000000..7f6ea2e
--- /dev/null
+++ b/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/RestconfNotificationEventProcessor.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2017-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.onosproject.provider.te.utils;
+
+/**
+ * Abstraction of the RESTCONF notification callback function. The callback
+ * is invoked by the notification listener when it receives an event.
+ */
+public interface RestconfNotificationEventProcessor<T> {
+    void processEventPayload(T event);
+}
diff --git a/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/TeTopologyRestconfEventListener.java b/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/TeTopologyRestconfEventListener.java
new file mode 100644
index 0000000..668fec6
--- /dev/null
+++ b/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/TeTopologyRestconfEventListener.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2017-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.onosproject.provider.te.utils;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.protocol.restconf.RestconfNotificationEventListener;
+import org.slf4j.Logger;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the RESTCONF notification event
+ * listener for TE Topology.
+ */
+public class TeTopologyRestconfEventListener implements
+        RestconfNotificationEventListener<String> {
+    private static final String TE_TOPOLOGY_NOTIFICATION_PREFIX =
+            "{\"ietf-te-topology:ietf-te-topology\":";
+    private static final String TE_LINK_EVENT_PREFIX =
+            "{\"ietf-te-topology:te-link-event\":";
+    private static final String TE_NODE_EVENT_PREFIX =
+            "{\"ietf-te-topology:te-node-event\":";
+
+    private final Logger log = getLogger(getClass());
+
+    private Map<TeTopologyRestconfEventType, RestconfNotificationEventProcessor>
+            eventCallbackFunctionMap = new ConcurrentHashMap<>();
+
+    @Override
+    public void handleNotificationEvent(DeviceId deviceId,
+                                        String eventJsonString) {
+        log.debug("New notification: {} for device: {}",
+                  eventJsonString, deviceId.toString());
+
+        if (!eventJsonString.startsWith(TE_TOPOLOGY_NOTIFICATION_PREFIX)) {
+            // This is not a TE topology event.
+            return;
+        }
+
+        String teEventString = removePrefixTagFromJson(eventJsonString,
+                                                       TE_TOPOLOGY_NOTIFICATION_PREFIX);
+
+        TeTopologyRestconfEventType eventType = getEventType(teEventString);
+
+        if (eventType == TeTopologyRestconfEventType.TE_UNKNOWN_EVENT) {
+            log.error("handleNotificationEvent: unknown event: {}", eventJsonString);
+            return;
+        }
+
+        RestconfNotificationEventProcessor eventProcessor =
+                eventCallbackFunctionMap.get(eventType);
+
+        if (eventProcessor != null) {
+            eventProcessor.processEventPayload(teEventString);
+        } else {
+            log.info("Event callback not installed for event type: {}", eventType);
+        }
+    }
+
+    /**
+     * Registers an notification event callback function which is called by
+     * the listener when it receives an event.
+     *
+     * @param eventType      notification event type corresponding to the
+     *                       callback function
+     * @param eventProcessor callback function
+     */
+    public void addCallbackFunction(TeTopologyRestconfEventType eventType,
+                                    RestconfNotificationEventProcessor eventProcessor) {
+        if (eventCallbackFunctionMap.containsKey(eventType)) {
+            removeCallbackFunction(eventType);
+        }
+
+        eventCallbackFunctionMap.put(eventType, eventProcessor);
+    }
+
+    /**
+     * Removes the callback function associated with the given event type.
+     *
+     * @param eventType notification event type
+     */
+    public void removeCallbackFunction(TeTopologyRestconfEventType eventType) {
+        eventCallbackFunctionMap.remove(eventType);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        TeTopologyRestconfEventListener that = (TeTopologyRestconfEventListener) o;
+
+        return eventCallbackFunctionMap != null ?
+                eventCallbackFunctionMap.equals(that.eventCallbackFunctionMap) :
+                that.eventCallbackFunctionMap == null;
+    }
+
+    @Override
+    public int hashCode() {
+        return eventCallbackFunctionMap != null ? eventCallbackFunctionMap.hashCode() : 0;
+    }
+
+    private String removePrefixTagFromJson(String jsonString, String prefixTag) {
+        if (jsonString.startsWith(prefixTag)) {
+            return jsonString.substring(prefixTag.length(), jsonString.length() - 1);
+        }
+        return jsonString;
+    }
+
+    private TeTopologyRestconfEventType getEventType(String teEventString) {
+        if (teEventString.startsWith(TE_LINK_EVENT_PREFIX)) {
+            return TeTopologyRestconfEventType.TE_TOPOLOGY_LINK_NOTIFICATION;
+        }
+
+        if (teEventString.startsWith(TE_NODE_EVENT_PREFIX)) {
+            return TeTopologyRestconfEventType.TE_TOPOLOGY_NODE_NOTIFICATION;
+        }
+
+        return TeTopologyRestconfEventType.TE_UNKNOWN_EVENT;
+    }
+}
+
diff --git a/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/TeTopologyRestconfEventType.java b/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/TeTopologyRestconfEventType.java
new file mode 100644
index 0000000..6806023
--- /dev/null
+++ b/providers/ietfte/utils/src/main/java/org/onosproject/provider/te/utils/TeTopologyRestconfEventType.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-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.onosproject.provider.te.utils;
+
+/**
+ * Types of TE Topology RESTCONF notification events.
+ */
+public enum TeTopologyRestconfEventType {
+    /**
+     * TE topology link event type.
+     */
+    TE_TOPOLOGY_LINK_NOTIFICATION,
+
+    /**
+     * TE topology node event type.
+     */
+    TE_TOPOLOGY_NODE_NOTIFICATION,
+
+    /**
+     * Unknown or unsupported event type.
+     */
+    TE_UNKNOWN_EVENT,
+}