[ONOS-5012] implement RESTconf server

 - fix javadoc longer than 80 char limit
 - fix javadoc that missing @params
 - chain calls to StringBuilder.append()
 - combine constant strings in place

Change-Id: Ie2ef4fd4c19e955ad2d5a5584f5017a842abb790
diff --git a/protocols/restconf/server/restconfmgr/BUCK b/protocols/restconf/server/restconfmgr/BUCK
index 100f922..26d8076 100644
--- a/protocols/restconf/server/restconfmgr/BUCK
+++ b/protocols/restconf/server/restconfmgr/BUCK
@@ -6,6 +6,8 @@
     '//utils/rest:onlab-rest',
     '//core/store/serializers:onos-core-serializers',
     '//protocols/restconf/server/api:onos-protocols-restconf-server-api',
+    '//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
+    '//apps/yms/api:onos-apps-yms-api',
 ]
 
 osgi_jar_with_tests (
diff --git a/protocols/restconf/server/restconfmgr/pom.xml b/protocols/restconf/server/restconfmgr/pom.xml
index 4c8b8c5..3fb62e3 100644
--- a/protocols/restconf/server/restconfmgr/pom.xml
+++ b/protocols/restconf/server/restconfmgr/pom.xml
@@ -58,6 +58,16 @@
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-restconf-server-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-yms-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
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 ee8506f..5237f50 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
@@ -15,20 +15,30 @@
  */
 package org.onosproject.protocol.restconf.server.restconfmanager;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.glassfish.jersey.server.ChunkedOutput;
 import org.onosproject.event.ListenerTracker;
 import org.onosproject.protocol.restconf.server.api.RestconfException;
 import org.onosproject.protocol.restconf.server.api.RestconfService;
+import org.onosproject.yms.ydt.YdtBuilder;
+import org.onosproject.yms.ydt.YdtContext;
+import org.onosproject.yms.ydt.YdtContextOperationType;
+import org.onosproject.yms.ydt.YdtResponse;
+import org.onosproject.yms.ydt.YmsOperationExecutionStatus;
+import org.onosproject.yms.ydt.YmsOperationType;
+import org.onosproject.yms.ymsm.YmsService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
@@ -37,21 +47,37 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 
+
+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 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.YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE;
+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;
 /*
  * Skeletal ONOS RESTCONF Server application. The RESTCONF Manager
  * implements the main logic of the RESTCONF Server.
  *
  * The design of the RESTCONF subsystem contains 2 major bundles:
  *
- * 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a JAX-RS application.
- * It acts as the frond-end of the the RESTCONF server. It handles
- * HTTP requests that are sent to the RESTCONF Root Path. It then calls the RESTCONF Manager
- * to process the requests.
+ * 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a
+ *    JAX-RS application. It acts as the frond-end of the RESTCONF server.
+ *    It intercepts/handles HTTP requests that are sent to the RESTCONF
+ *    Root Path. It then calls the RESTCONF Manager to process the requests.
  *
- * 2. RESTCONF Manager. This is the back-end. It provides the main logic of the RESTCONF server.
- * It calls the YMS (YANG Management System) to operate on the YANG data objects.
+ * 2. RESTCONF Manager. This bundle module is the back-end of the server.
+ *    It provides the main logic of the RESTCONF server. It interacts with
+ *    the YMS (YANG Management System) to run operations on the YANG data
+ *    objects (i.e., data resources).
  */
 
 /**
@@ -73,26 +99,25 @@
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    //TODO: YMS service
-    //@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    //protected YmsService ymsService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected YmsService ymsService;
 
     private ListenerTracker listeners;
 
     private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
-            new ConcurrentHashMap<String, BlockingQueue<ObjectNode>>();
+            new ConcurrentHashMap<>();
 
     private ExecutorService workerThreadPool;
 
     @Activate
     protected void activate() {
-        workerThreadPool = Executors.newFixedThreadPool(maxNumOfWorkerThreads,
-                                                        new ThreadFactoryBuilder()
-                                                                .setNameFormat("restconf-worker")
-                                                                .build());
+        workerThreadPool = Executors
+                .newFixedThreadPool(maxNumOfWorkerThreads,
+                                    new ThreadFactoryBuilder()
+                                            .setNameFormat("restconf-worker")
+                                            .build());
         listeners = new ListenerTracker();
         //TODO: YMS notification
-        //listeners.addListener(ymsService, new InternalYangNotificationListener());
         log.info("Started");
     }
 
@@ -104,34 +129,117 @@
     }
 
     @Override
-    public ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException {
-        //TODO: YMS integration
-        return null;
+    public ObjectNode runGetOperationOnDataResource(String uri)
+            throws RestconfException {
+        YdtBuilder ydtBuilder = getYdtBuilder(QUERY_REQUEST);
+        //Convert the URI to ydtBuilder
+        convertUriToYdt(uri, ydtBuilder, NONE);
+        YdtResponse ydtResponse = ymsService.executeOperation(ydtBuilder);
+        YmsOperationExecutionStatus status = ydtResponse
+                .getYmsOperationResult();
+        if (status != EXECUTION_SUCCESS) {
+            throw new RestconfException("YMS GET operation failed",
+                                        INTERNAL_SERVER_ERROR);
+        }
+
+        YdtContext rootNode = ydtResponse.getRootNode();
+        YdtContext curNode = ydtBuilder.getCurNode();
+
+        ObjectNode result = convertYdtToJson(curNode.getName(), rootNode,
+                                             ymsService.getYdtWalker());
+        //if the query URI contain a key, something like list=key
+        //here should only get get child with the specific key
+        YdtContext child = curNode.getFirstChild();
+        if (child != null &&
+                child.getYdtType() == SINGLE_INSTANCE_LEAF_VALUE_NODE) {
+
+            ArrayNode jsonNode = (ArrayNode) result.get(curNode.getName());
+            for (JsonNode next : jsonNode) {
+                if (next.findValue(child.getName())
+                        .asText().equals(child.getValue())) {
+                    return (ObjectNode) next;
+                }
+            }
+            throw new RestconfException(String.format("No content for %s = %s",
+                                                      child.getName(),
+                                                      child.getValue()),
+                                        INTERNAL_SERVER_ERROR);
+        }
+        return result;
+    }
+
+    private YmsOperationExecutionStatus
+    invokeYmsOp(String uri, ObjectNode rootNode,
+                YdtContextOperationType opType) {
+        YdtBuilder ydtBuilder = getYdtBuilder(EDIT_CONFIG_REQUEST);
+        //Convert the URI to ydtBuilder
+        convertUriToYdt(uri, ydtBuilder, opType);
+
+        //set default operation type for the payload node
+        ydtBuilder.setDefaultEditOperationType(opType);
+        //convert the payload json body to ydt
+        convertJsonToYdt(rootNode, ydtBuilder);
+
+        return ymsService
+                .executeOperation(ydtBuilder)
+                .getYmsOperationResult();
     }
 
     @Override
-    public void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
-        //TODO: YMS integration
+    public void runPostOperationOnDataResource(String uri, ObjectNode rootNode)
+            throws RestconfException {
+        YmsOperationExecutionStatus status =
+                invokeYmsOp(uri, rootNode, CREATE);
+
+        if (status != EXECUTION_SUCCESS) {
+            throw new RestconfException("YMS post operation failed.",
+                                        INTERNAL_SERVER_ERROR);
+        }
     }
 
     @Override
-    public void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
-        //TODO: YMS integration
+    public void runPutOperationOnDataResource(String uri, ObjectNode rootNode)
+            throws RestconfException {
+        YmsOperationExecutionStatus status =
+                invokeYmsOp(uri, rootNode, REPLACE);
+
+        if (status != EXECUTION_SUCCESS) {
+            throw new RestconfException("YMS put operation failed.",
+                                        INTERNAL_SERVER_ERROR);
+        }
     }
 
-    /**
-     * Process the delete operation on a data resource.
-     *
-     * @param uri URI of the data resource to be deleted.
-     */
     @Override
-    public void runDeleteOperationOnDataResource(String uri) throws RestconfException {
-        //TODO: YMS integration
+    public void runDeleteOperationOnDataResource(String uri)
+            throws RestconfException {
+        //Get a root ydtBuilder
+        YdtBuilder ydtBuilder = getYdtBuilder(EDIT_CONFIG_REQUEST);
+        //Convert the URI to ydtBuilder
+        convertUriToYdt(uri, ydtBuilder, DELETE);
+        //Execute the delete operation
+        YmsOperationExecutionStatus status = ymsService
+                .executeOperation(ydtBuilder)
+                .getYmsOperationResult();
+        if (status != EXECUTION_SUCCESS) {
+            throw new RestconfException("YMS delete operation failed.",
+                                        INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    @Override
+    public void runPatchOperationOnDataResource(String uri, ObjectNode rootNode)
+            throws RestconfException {
+        YmsOperationExecutionStatus status = invokeYmsOp(uri, rootNode, MERGE);
+
+        if (status != EXECUTION_SUCCESS) {
+            throw new RestconfException("YMS patch operation failed.",
+                                        INTERNAL_SERVER_ERROR);
+        }
     }
 
     @Override
     public String getRestconfRootPath() {
-        return this.RESTCONF_ROOT;
+        return RESTCONF_ROOT;
     }
 
     /**
@@ -143,16 +251,21 @@
      * @throws RestconfException if the worker thread fails to create
      */
     @Override
-    public void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException {
-        BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<ObjectNode>();
+    public void subscribeEventStream(String streamId,
+                                     ChunkedOutput<String> output)
+            throws RestconfException {
+        BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<>();
         if (workerThreadPool instanceof ThreadPoolExecutor) {
-            if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >= maxNumOfWorkerThreads) {
-                throw new RestconfException("no more work threads left to handle event subscription",
-                                            Response.Status.INTERNAL_SERVER_ERROR);
+            if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >=
+                    maxNumOfWorkerThreads) {
+                throw new RestconfException("no more work threads left to " +
+                                                    "handle event subscription",
+                                            INTERNAL_SERVER_ERROR);
             }
         } else {
-            throw new RestconfException("Server ERROR: workerThreadPool NOT instanceof ThreadPoolExecutor",
-                                        Response.Status.INTERNAL_SERVER_ERROR);
+            throw new RestconfException("Server ERROR: workerThreadPool NOT " +
+                                                "instanceof ThreadPoolExecutor",
+                                        INTERNAL_SERVER_ERROR);
 
         }
 
@@ -169,10 +282,11 @@
         pool.shutdown(); // Disable new tasks from being submitted
         try {
             // Wait a while for existing tasks to terminate
-            if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) {
+            if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, SECONDS)) {
                 pool.shutdownNow(); // Cancel currently executing tasks
                 // Wait a while for tasks to respond to being cancelled
-                if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) {
+                if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT,
+                                           SECONDS)) {
                     log.error("Pool did not terminate");
                 }
             }
@@ -190,7 +304,8 @@
         private final ChunkedOutput<String> output;
         private final BlockingQueue<ObjectNode> bqueue;
 
-        public EventConsumer(ChunkedOutput<String> output, BlockingQueue<ObjectNode> q) {
+        public EventConsumer(ChunkedOutput<String> output,
+                             BlockingQueue<ObjectNode> q) {
             this.queueId = Thread.currentThread().getName();
             this.output = output;
             this.bqueue = q;
@@ -212,7 +327,8 @@
                  */
                 eventQueueList.remove(this.queueId);
             } catch (InterruptedException e) {
-                log.error("ERROR: EventConsumer: bqueue.take() has been interrupted.");
+                log.error("ERROR: EventConsumer: bqueue.take() " +
+                                  "has been interrupted.");
                 log.debug("EventConsumer Exception:", e);
             } finally {
                 try {
@@ -226,6 +342,10 @@
 
     }
 
+    private YdtBuilder getYdtBuilder(YmsOperationType ymsOperationType) {
+        return ymsService.getYdtBuilder(RESTCONF_ROOT, null, ymsOperationType);
+    }
+
     /**
      * The listener class acts as the event producer for the event queues. The
      * queues are created by the event consumer threads and are removed when the