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/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfSBController.java b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfSBController.java
index 772aaea..bb565f2 100644
--- a/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfSBController.java
+++ b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfSBController.java
@@ -31,29 +31,41 @@
      * callBackListener upon receiving notifications to notify the requester
      * about notifications.
      *
-     *
-     * @param device device to make the request to
-     * @param request url of the request
-     * @param mediaType format to retrieve the content in
+     * @param device           device to make the request to
+     * @param request          url of the request
+     * @param mediaType        format to retrieve the content in
      * @param callBackListener method to call when notifications arrives
      */
     void enableNotifications(DeviceId device, String request, String mediaType,
-                          RestConfNotificationEventListener callBackListener);
+                             RestconfNotificationEventListener callBackListener);
 
     /**
-     * Register a listener for notification events that occur to restconf
+     * Registers a listener for notification events that occur to restconf
      * devices.
      *
-     * @param deviceId the deviceId
+     * @param deviceId identifier of the device to which the listener is attached
      * @param listener the listener to notify
      */
     void addNotificationListener(DeviceId deviceId,
-                                 RestConfNotificationEventListener listener);
+                                 RestconfNotificationEventListener listener);
 
     /**
-     * Unregister the listener for the device.
+     * Unregisters the listener for the device.
      *
-     * @param deviceId the deviceId
+     * @param deviceId identifier of the device for which the listener
+     *                 is to be removed
+     * @param listener listener to be removed
      */
-    void removeNotificationListener(DeviceId deviceId);
+    void removeNotificationListener(DeviceId deviceId,
+                                    RestconfNotificationEventListener listener);
+
+    /**
+     * Returns true if a listener has been installed to listen to RESTCONF
+     * notifications sent from a particular device.
+     *
+     * @param deviceId identifier of the device from which the notifications
+     *                 are generated
+     * @return true if listener is installed; false otherwise
+     */
+    boolean isNotificationEnabled(DeviceId deviceId);
 }
diff --git a/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfNotificationEventListener.java b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestconfNotificationEventListener.java
similarity index 76%
rename from protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfNotificationEventListener.java
rename to protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestconfNotificationEventListener.java
index 7866587..230724b 100644
--- a/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfNotificationEventListener.java
+++ b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestconfNotificationEventListener.java
@@ -20,13 +20,13 @@
 /**
  * Notifies providers about incoming RESTCONF notification events.
  */
-public interface RestConfNotificationEventListener<T> {
+public interface RestconfNotificationEventListener<T> {
 
     /**
      * Handles the notification event.
      *
-     * @param deviceId        restconf device identifier
-     * @param eventJsonString the json string representation of the event
+     * @param deviceId restconf device identifier
+     * @param event    event payload
      */
-    void handleNotificationEvent(DeviceId deviceId, T eventJsonString);
+    void handleNotificationEvent(DeviceId deviceId, T event);
 }
diff --git a/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImpl.java b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImpl.java
index ce6335b..897f6c7 100644
--- a/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImpl.java
+++ b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImpl.java
@@ -15,16 +15,6 @@
  */
 package org.onosproject.protocol.restconf.ctl;
 
-import java.io.InputStream;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.GenericType;
-import javax.ws.rs.core.Response;
-
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -36,13 +26,24 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
 import org.onosproject.protocol.rest.RestSBDevice;
-import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
 import org.onosproject.protocol.restconf.RestConfSBController;
+import org.onosproject.protocol.restconf.RestconfNotificationEventListener;
 import org.onosproject.yms.ych.YangProtocolEncodingFormat;
 import org.onosproject.yms.ymsm.YmsService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 /**
  * The implementation of RestConfSBController.
  */
@@ -62,8 +63,8 @@
     private static final String RESOURCE_PATH_PREFIX = "/data/";
     private static final String NOTIFICATION_PATH_PREFIX = "/streams/";
 
-    private Map<DeviceId, RestConfNotificationEventListener>
-                                            restconfNotificationListenerMap = new ConcurrentHashMap<>();
+    private Map<DeviceId, Set<RestconfNotificationEventListener>>
+            restconfNotificationListenerMap = new ConcurrentHashMap<>();
     private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -168,8 +169,13 @@
 
     @Override
     public void enableNotifications(DeviceId device, String request,
-                                 String mediaType,
-                                 RestConfNotificationEventListener listener) {
+                                    String mediaType,
+                                    RestconfNotificationEventListener listener) {
+
+        if (isNotificationEnabled(device)) {
+            log.warn("enableNotifications: already enabled on device: {}", device);
+            return;
+        }
 
         request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
                 + request;
@@ -183,10 +189,9 @@
     }
 
     public void stopNotifications(DeviceId device) {
-
         runnableTable.get(device).terminate();
         runnableTable.remove(device);
-        removeNotificationListener(device);
+        restconfNotificationListenerMap.remove(device);
         log.debug("Stop sending notifications for device URI: " + device.uri().toString());
 
     }
@@ -203,9 +208,9 @@
         }
 
         /**
-         * @param request request
+         * @param request   request
          * @param mediaType media type
-         * @param device device identifier
+         * @param device    device identifier
          */
         public GetChunksRunnable(String request, String mediaType,
                                  DeviceId device) {
@@ -218,8 +223,8 @@
         public void run() {
             WebTarget wt = getWebTarget(device, request);
             Response clientResp = wt.request(mediaType).get();
-            RestConfNotificationEventListener listener = restconfNotificationListenerMap
-                    .get(device);
+            Set<RestconfNotificationEventListener> listeners =
+                    restconfNotificationListenerMap.get(device);
             final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
                     .readEntity(new GenericType<ChunkedInput<String>>() {
                     });
@@ -233,10 +238,12 @@
                 chunk = chunkedInput.read();
                 if (chunk != null) {
                     if (running) {
-                        listener.handleNotificationEvent(device, chunk);
+                        for (RestconfNotificationEventListener listener : listeners) {
+                            listener.handleNotificationEvent(device, chunk);
+                        }
                     } else {
                         log.trace("the requesting client is no more interested "
-                                + "to receive such notifications.");
+                                          + "to receive such notifications.");
                     }
                 } else {
                     log.trace("The received notification chunk is null. do not continue any more.");
@@ -256,15 +263,29 @@
 
     @Override
     public void addNotificationListener(DeviceId deviceId,
-                                        RestConfNotificationEventListener listener) {
-        if (!restconfNotificationListenerMap.containsKey(deviceId)) {
-            this.restconfNotificationListenerMap.put(deviceId, listener);
+                                        RestconfNotificationEventListener listener) {
+        Set<RestconfNotificationEventListener> listeners =
+                restconfNotificationListenerMap.get(deviceId);
+        if (listeners == null) {
+            listeners = new HashSet<>();
         }
+
+        listeners.add(listener);
+
+        this.restconfNotificationListenerMap.put(deviceId, listeners);
     }
 
     @Override
-    public void removeNotificationListener(DeviceId deviceId) {
-        this.restconfNotificationListenerMap.remove(deviceId);
+    public void removeNotificationListener(DeviceId deviceId,
+                                           RestconfNotificationEventListener listener) {
+        Set<RestconfNotificationEventListener> listeners =
+                restconfNotificationListenerMap.get(deviceId);
+        if (listeners != null) {
+            listeners.remove(listener);
+        }
     }
 
+    public boolean isNotificationEnabled(DeviceId deviceId) {
+        return runnableTable.containsKey(deviceId);
+    }
 }