[ONOS-5580] Bug fix for JSON encoding of augmented node names

The submission contains:

1. Fix of ONOS-5580
2. Enable RESTCONF server to register and receive notifications from YMS

Change-Id: I8d27a7ee59679de22b7f49b370b35e48909958a4
diff --git a/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java b/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java
index 5237f50..95be9d1 100644
--- a/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java
+++ b/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java
@@ -36,10 +36,14 @@
 import org.onosproject.yms.ydt.YmsOperationExecutionStatus;
 import org.onosproject.yms.ydt.YmsOperationType;
 import org.onosproject.yms.ymsm.YmsService;
+import org.onosproject.yms.ynh.YangNotificationEvent;
+import org.onosproject.yms.ynh.YangNotificationListener;
+import org.onosproject.yms.ynh.YangNotificationService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.Map.Entry;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -48,21 +52,23 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 
-
-import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REQUEST;
-import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
-import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertJsonToYdt;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertUriToYdt;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertYdtToJson;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.getJsonNameFromYdtNode;
 import static org.onosproject.yms.ydt.YdtContextOperationType.CREATE;
 import static org.onosproject.yms.ydt.YdtContextOperationType.DELETE;
-import static org.onosproject.yms.ydt.YdtContextOperationType.REPLACE;
 import static org.onosproject.yms.ydt.YdtContextOperationType.MERGE;
+import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
+import static org.onosproject.yms.ydt.YdtContextOperationType.REPLACE;
 import static org.onosproject.yms.ydt.YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yms.ydt.YmsOperationExecutionStatus.EXECUTION_EXCEPTION;
 import static org.onosproject.yms.ydt.YmsOperationExecutionStatus.EXECUTION_SUCCESS;
-import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertYdtToJson;
-import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertUriToYdt;
-import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertJsonToYdt;
-import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
+import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REQUEST;
+
 /*
  * Skeletal ONOS RESTCONF Server application. The RESTCONF Manager
  * implements the main logic of the RESTCONF Server.
@@ -102,6 +108,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected YmsService ymsService;
 
+    protected YangNotificationService ymsNotificationService;
+
     private ListenerTracker listeners;
 
     private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
@@ -116,8 +124,9 @@
                                     new ThreadFactoryBuilder()
                                             .setNameFormat("restconf-worker")
                                             .build());
+        ymsNotificationService = ymsService.getYangNotificationService();
         listeners = new ListenerTracker();
-        //TODO: YMS notification
+        listeners.addListener(ymsNotificationService, new InternalYangNotificationListener());
         log.info("Started");
     }
 
@@ -145,7 +154,8 @@
         YdtContext rootNode = ydtResponse.getRootNode();
         YdtContext curNode = ydtBuilder.getCurNode();
 
-        ObjectNode result = convertYdtToJson(curNode.getName(), rootNode,
+        ObjectNode result = convertYdtToJson(getJsonNameFromYdtNode(curNode),
+                                             rootNode,
                                              ymsService.getYdtWalker());
         //if the query URI contain a key, something like list=key
         //here should only get get child with the specific key
@@ -153,17 +163,17 @@
         if (child != null &&
                 child.getYdtType() == SINGLE_INSTANCE_LEAF_VALUE_NODE) {
 
-            ArrayNode jsonNode = (ArrayNode) result.get(curNode.getName());
+            ArrayNode jsonNode = (ArrayNode) result.get(getJsonNameFromYdtNode(curNode));
             for (JsonNode next : jsonNode) {
-                if (next.findValue(child.getName())
+                if (next.findValue(getJsonNameFromYdtNode(child))
                         .asText().equals(child.getValue())) {
                     return (ObjectNode) next;
                 }
             }
             throw new RestconfException(String.format("No content for %s = %s",
-                                                      child.getName(),
+                                                      getJsonNameFromYdtNode(child),
                                                       child.getValue()),
-                                        INTERNAL_SERVER_ERROR);
+                                                      INTERNAL_SERVER_ERROR);
         }
         return result;
     }
@@ -180,9 +190,16 @@
         //convert the payload json body to ydt
         convertJsonToYdt(rootNode, ydtBuilder);
 
-        return ymsService
-                .executeOperation(ydtBuilder)
-                .getYmsOperationResult();
+        YmsOperationExecutionStatus status = EXECUTION_EXCEPTION;
+
+        try {
+            status = ymsService.executeOperation(ydtBuilder).getYmsOperationResult();
+        } catch (Exception e) {
+            log.error("YMS operation failed: {}", e.getMessage());
+            log.debug("Exception in invokeYmsOp: ", e);
+        }
+
+        return status;
     }
 
     @Override
@@ -339,7 +356,6 @@
                 }
             }
         }
-
     }
 
     private YdtBuilder getYdtBuilder(YmsOperationType ymsOperationType) {
@@ -351,8 +367,7 @@
      * queues are created by the event consumer threads and are removed when the
      * threads terminate.
      */
-    //TODO: YMS notification
-    /*private class InternalYangNotificationListener implements YangNotificationListener {
+    private class InternalYangNotificationListener implements YangNotificationListener {
 
         @Override
         public void event(YangNotificationEvent event) {
@@ -362,28 +377,33 @@
             }
 
             if (eventQueueList.isEmpty()) {
-                *//*
+                /*
                  * There is no consumer waiting to consume, so don't have to
                  * produce this event.
-                 *//*
+                 */
                 return;
             }
 
             try {
-                *//*
+                /*
                  * Put the event to every queue out there. Each queue is
                  * corresponding to an event stream session. The queue is
                  * removed when the session terminates.
-                 *//*
+                 */
                 for (Entry<String, BlockingQueue<ObjectNode>> entry : eventQueueList
                         .entrySet()) {
-                    entry.getValue().put(event.subject().getData());
+                    YdtContext ydtNode = event.subject().getNotificationRootContext();
+                    ObjectNode jsonNode = convertYdtToJson(getJsonNameFromYdtNode(ydtNode),
+                                                           ydtNode,
+                                                           ymsService.getYdtWalker());
+                    entry.getValue().put(jsonNode);
                 }
             } catch (InterruptedException e) {
-                Log.error("ERROR", e);
-                throw new RestconfException("queue", Status.INTERNAL_SERVER_ERROR);
+                log.error("Failed to put event in queue: {}", e.getMessage());
+                log.debug("Exception trace in InternalYangNotificationListener: ", e);
+                throw new RestconfException("Failed to put event in queue",
+                                            INTERNAL_SERVER_ERROR);
             }
         }
-
-    }*/
+    }
 }
diff --git a/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml b/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml
index c738da9..21cc8a5 100644
--- a/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml
+++ b/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml
@@ -28,6 +28,7 @@
             <param-value>org.onosproject.protocol.restconf.server.rpp.RestconfProtocolProxy</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
+        <async-supported>true</async-supported>
     </servlet>
 
     <servlet-mapping>
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
index 5ede187..2bf7f74 100755
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
@@ -335,4 +335,62 @@
         String name = getLatterSegment(field, COLON);
         return new NormalizedYangNode(namespace, name);
     }
+
+
+    /**
+     * Extracts the node name from a YDT node and encodes it in JSON format.
+     * A JSON encoded node name has the following format:
+     * <p>
+     * module_name ":" node_name
+     * <p>
+     * where module_name is name of the YANG module in which the data
+     * resource is defined, and node_name is the name of the data resource.
+     * <p>
+     * If the YDT node is null or its node name field is null, then the function
+     * returns null. If the node name field is not null but module name field is,
+     * then the function returns only the node name.
+     *
+     * @param ydtContext YDT node of the target data resource
+     * @return JSON encoded name of the target data resource
+     */
+    public static String getJsonNameFromYdtNode(YdtContext ydtContext) {
+        if (ydtContext == null) {
+            return null;
+        }
+
+        String nodeName = ydtContext.getName();
+        if (nodeName == null) {
+            return null;
+        }
+
+        /*
+         * The namespace field in YDT node is a string which contains a list
+         * of identifiers separated by colon (:). e.g.,
+         *
+         * {identifier ":" identifier}+
+         *
+         * The last identifier in the string is the YANG module name.
+         */
+        String moduleName = getModuleNameFromNamespace(ydtContext.getNamespace());
+        if (moduleName == null) {
+            return nodeName;
+        } else {
+            return moduleName + COLON + nodeName;
+        }
+    }
+
+    private static String getModuleNameFromNamespace(String namespace) {
+        if (namespace == null) {
+            return null;
+        }
+
+        String moduleName = null;
+
+        if (namespace.contains(COLON)) {
+            String[] tokens = namespace.split(COLON);
+            moduleName = tokens[tokens.length - 1];
+        }
+
+        return moduleName;
+    }
 }
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
index 4ffd6f1..eeee124 100644
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
@@ -23,6 +23,7 @@
 import org.onosproject.yms.ydt.YdtListener;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.getJsonNameFromYdtNode;
 
 /**
  * Represents implementation of codec YDT listener.
@@ -53,7 +54,7 @@
 
     @Override
     public void enterYdtNode(YdtContext ydtContext) {
-        String name = ydtContext.getName();
+        String name = getJsonNameFromYdtNode(ydtContext);
 
         if (!isBegin && name != null && name.equals(rootName)) {
             isBegin = true;
@@ -69,7 +70,7 @@
                 break;
             case MULTI_INSTANCE_NODE:
                 YdtContext preNode = ydtContext.getPreviousSibling();
-                if (preNode == null || !preNode.getName().equals(name)) {
+                if (preNode == null || !getJsonNameFromYdtNode(preNode).equals(name)) {
                     jsonBuilder.addNodeTopHalf(name, JsonNodeType.ARRAY);
                 }
                 jsonBuilder.addNodeTopHalf(EMPTY, JsonNodeType.OBJECT);
@@ -94,7 +95,7 @@
             return;
         }
 
-        String curName = ydtContext.getName();
+        String curName = getJsonNameFromYdtNode(ydtContext);
         YdtContext nextNode = ydtContext.getNextSibling();
         switch (ydtContext.getYdtType()) {
 
@@ -102,7 +103,7 @@
                 jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
                 break;
             case MULTI_INSTANCE_NODE:
-                if (nextNode == null || !nextNode.getName().equals(curName)) {
+                if (nextNode == null || !getJsonNameFromYdtNode(nextNode).equals(curName)) {
                     jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
                     jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
                 } else {
@@ -120,8 +121,8 @@
                                                     ydtContext.getYdtType());
         }
         if (curName.equals(rootName) &&
-                (nextNode == null || !nextNode.getName().equals(rootName))) {
+                (nextNode == null || !getJsonNameFromYdtNode(nextNode).equals(rootName))) {
             isBegin = false;
         }
     }
-}
\ No newline at end of file
+}