Add gNMI device state subscriber

Change-Id: I20cb5e130f4e416bf8678aab2e5268faf24ad06b
diff --git a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiClient.java b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiClient.java
index 5beb238..6e65dd3 100644
--- a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiClient.java
+++ b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiClient.java
@@ -22,6 +22,7 @@
 import gnmi.Gnmi.GetResponse;
 import gnmi.Gnmi.SetRequest;
 import gnmi.Gnmi.SetResponse;
+import gnmi.Gnmi.SubscribeRequest;
 import org.onosproject.grpc.api.GrpcClient;
 
 import java.util.concurrent.CompletableFuture;
@@ -56,12 +57,23 @@
     CompletableFuture<SetResponse> set(SetRequest request);
 
     /**
-     * Check weather the gNMI service is available or not by sending a
-     * dummy get request message.
+     * Subscribes to a given specific gNMI path.
+     *
+     * @param request the subscribe request
+     * @return true if subscribe successfully; false otherwise
+     */
+    boolean subscribe(SubscribeRequest request);
+
+    /**
+     * Terminates the subscription channel of this device.
+     */
+    void terminateSubscriptionChannel();
+
+    /**
+     * Check weather the gNMI service is available or not by sending a dummy get
+     * request message.
      *
      * @return true if gNMI service available; false otherwise
      */
     CompletableFuture<Boolean> isServiceAvailable();
-
-    // TODO: Support gNMI subscription
 }
diff --git a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiController.java b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiController.java
index f4964ed..b0e0071 100644
--- a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiController.java
+++ b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiController.java
@@ -17,6 +17,7 @@
 package org.onosproject.gnmi.api;
 
 import com.google.common.annotations.Beta;
+import org.onosproject.event.ListenerService;
 import org.onosproject.grpc.api.GrpcClientController;
 
 /**
@@ -24,5 +25,6 @@
  */
 @Beta
 public interface GnmiController
-        extends GrpcClientController<GnmiClientKey, GnmiClient> {
+        extends GrpcClientController<GnmiClientKey, GnmiClient>,
+        ListenerService<GnmiEvent, GnmiEventListener> {
 }
diff --git a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiEvent.java b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiEvent.java
index 5129926..84031d0 100644
--- a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiEvent.java
+++ b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiEvent.java
@@ -32,15 +32,10 @@
         /**
          * Update.
          */
-        UPDATE,
-
-        /**
-         * Sync response.
-         */
-        SYNC_RESPONSE
+        UPDATE
     }
 
-    protected GnmiEvent(Type type, GnmiEventSubject subject) {
+    public GnmiEvent(Type type, GnmiEventSubject subject) {
         super(type, subject);
     }
 }
diff --git a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiUpdate.java b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiUpdate.java
new file mode 100644
index 0000000..eeb7649
--- /dev/null
+++ b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiUpdate.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.gnmi.api;
+
+import com.google.common.base.MoreObjects;
+import gnmi.Gnmi.Notification;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Event class for gNMI update.
+ */
+public class GnmiUpdate implements GnmiEventSubject {
+    private DeviceId deviceId;
+    private Notification update;
+    private boolean syncResponse;
+
+    /**
+     * Default constructor.
+     *
+     * @param deviceId the device id for this event
+     * @param update the update for this event
+     * @param syncResponse indicate target has sent all values associated with
+     *                     the subscription at least once.
+     */
+    public GnmiUpdate(DeviceId deviceId, Notification update, boolean syncResponse) {
+        this.deviceId = deviceId;
+        this.update = update;
+        this.syncResponse = syncResponse;
+    }
+
+    /**
+     * Gets the update data.
+     *
+     * @return the update data
+     */
+    public Notification update() {
+        return update;
+    }
+
+    /**
+     * indicate target has sent all values associated with the subscription at
+     * least once.
+     *
+     * @return true if all value from target has sent
+     */
+    public boolean syncResponse() {
+        return syncResponse;
+    }
+
+    @Override
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("deviceId", deviceId)
+                .add("syncResponse", syncResponse)
+                .add("update", update)
+                .toString();
+    }
+}
diff --git a/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiUtils.java b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiUtils.java
new file mode 100644
index 0000000..6a71a19
--- /dev/null
+++ b/protocols/gnmi/api/src/main/java/org/onosproject/gnmi/api/GnmiUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.gnmi.api;
+
+import gnmi.Gnmi.Path;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Utilities for gNMI protocol.
+ */
+public final class GnmiUtils {
+
+    private GnmiUtils() {
+        // Hide default constructor
+    }
+
+    /**
+     * Convert gNMI path to human readable string.
+     *
+     * @param path the gNMI path
+     * @return readable string of the path
+     */
+    public static String pathToString(Path path) {
+        StringBuilder pathStringBuilder = new StringBuilder();
+
+        path.getElemList().forEach(elem -> {
+            pathStringBuilder.append("/").append(elem.getName());
+            if (elem.getKeyCount() > 0) {
+                pathStringBuilder.append("[");
+                List<String> keys = elem.getKeyMap().entrySet().stream()
+                        .map(entry -> entry.getKey() + "=" + entry.getValue())
+                        .collect(Collectors.toList());
+                pathStringBuilder.append(String.join(", ", keys));
+                pathStringBuilder.append("]");
+            }
+        });
+        return pathStringBuilder.toString();
+    }
+}