[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/BUCK b/protocols/restconf/server/BUCK
index 8b4f0d2..c1383b6 100644
--- a/protocols/restconf/server/BUCK
+++ b/protocols/restconf/server/BUCK
@@ -2,6 +2,7 @@
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
'//protocols/restconf/server/restconfmgr:onos-protocols-restconf-server-restconfmgr',
'//protocols/restconf/server/rpp:onos-protocols-restconf-server-rpp',
+ '//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
]
onos_app (
diff --git a/protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/Patch.java b/protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/Patch.java
new file mode 100644
index 0000000..5004944
--- /dev/null
+++ b/protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/Patch.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.api;
+
+import javax.ws.rs.HttpMethod;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the annotated method responds to HTTP PATCH requests.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("PATCH")
+public @interface Patch {
+}
\ No newline at end of file
diff --git a/protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/RestconfService.java b/protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/RestconfService.java
index 6fea21d..053356b 100644
--- a/protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/RestconfService.java
+++ b/protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/RestconfService.java
@@ -25,59 +25,82 @@
public interface RestconfService {
/**
* Processes a GET request against a data resource. The
- * target data resource is identified by its URI.
+ * target data resource is identified by its URI. If the
+ * GET operation cannot be fulfilled due to reasons such
+ * as the nonexistence of the target resource, then a
+ * RestconfException exception is raised. The proper
+ * HTTP error status code is enclosed in the exception, so
+ * that the caller may return it to the RESTCONF client to
+ * display.
*
* @param uri URI of the target data resource
* @return JSON representation of the data resource
- * @throws RestconfException if the GET operation cannot be fulfilled due
- * reasons such as the nonexistence of the target
- * resource. The proper HTTP error status code is
- * enclosed in the exception, so that the caller
- * may return it to the RESTCONF client
+ * @throws RestconfException if the GET operation cannot be fulfilled
*/
- ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException;
+ ObjectNode runGetOperationOnDataResource(String uri)
+ throws RestconfException;
/**
* Processes a POST request against a data resource. The location of
* the target resource is passed in as a URI. And the resource's
- * content is passed in as a JSON ObjectNode.
+ * content is passed in as a JSON ObjectNode. If the POST operation
+ * cannot be fulfilled due to reasons such as wrong input URIs or
+ * syntax errors in the JSON payloads, a RestconfException exception
+ * is raised. The proper HTTP error status code is enclosed in the
+ * exception.
*
* @param uri URI of the data resource to be created
* @param rootNode JSON representation of the data resource
- * @throws RestconfException if the POST operation cannot be fulfilled due
- * reasons such as wrong URI or syntax error
- * in JSON payload. The proper HTTP error status
- * code is enclosed in the exception
+ * @throws RestconfException if the POST operation cannot be fulfilled
*/
- void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException;
+ void runPostOperationOnDataResource(String uri, ObjectNode rootNode)
+ throws RestconfException;
/**
* Processes a PUT request against a data resource. The location of
* the target resource is passed in as a URI. And the resource's
- * content is passed in as a JSON ObjectNode.
+ * content is passed in as a JSON ObjectNode. If the PUT operation
+ * cannot be fulfilled due to reasons such as wrong input URIs or
+ * syntax errors in the JSON payloads, a RestconfException exception
+ * is raised. The proper HTTP error status code is enclosed in the
+ * exception.
*
* @param uri URI of the data resource to be created or updated
* @param rootNode JSON representation of the data resource
- * @throws RestconfException if the PUT operation cannot be fulfilled due
- * reasons such as wrong URI or syntax error
- * in JSON payload. The proper HTTP error status
- * code is enclosed in the exception
+ * @throws RestconfException if the PUT operation cannot be fulfilled
*/
- void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException;
+ void runPutOperationOnDataResource(String uri, ObjectNode rootNode)
+ throws RestconfException;
/**
* Processes the DELETE operation against a data resource. The target
- * data resource is identified by its URI.
+ * data resource is identified by its URI. If the DELETE operation
+ * cannot be fulfilled due reasons such as the nonexistence of the
+ * target resource, a RestconfException exception is raised. The
+ * proper HTTP error status code is enclosed in the exception.
*
* @param uri URI of the data resource to be deleted
- * @throws RestconfException if the DELETE operation cannot be fulfilled due
- * reasons such as the nonexistence of the target
- * resource. The proper HTTP error status code is
- * enclosed in the exception
+ * @throws RestconfException if the DELETE operation cannot be fulfilled
*/
void runDeleteOperationOnDataResource(String uri) throws RestconfException;
/**
+ * Processes a PATCH operation on a data resource. The target data
+ * resource is identified by its URI passed in by the caller.
+ * And the content of the data resource is passed in as a JSON ObjectNode.
+ * If the PATCH operation cannot be fulfilled due reasons such as
+ * the nonexistence of the target resource, a RestconfException
+ * exception is raised. The proper HTTP error status code is
+ * enclosed in the exception.
+ *
+ * @param uri URI of the data resource to be patched
+ * @param rootNode JSON representation of the data resource
+ * @throws RestconfException if the PATCH operation cannot be fulfilled
+ */
+ void runPatchOperationOnDataResource(String uri, ObjectNode rootNode)
+ throws RestconfException;
+
+ /**
* Retrieves the RESTCONF Root directory.
*
* @return the RESTCONF Root directory
@@ -90,13 +113,18 @@
* which is passed in from the caller. (The worker thread blocks if
* no events arrive.) The ChuckedOutput is a pipe to which this
* function acts as the writer and the caller the reader.
+ * <p>
+ * If the Event Stream cannot be subscribed due to reasons such as
+ * the nonexistence of the target stream or failure to allocate
+ * worker thread to handle the request, a RestconfException exception
+ * is raised. The proper HTTP error status code is enclosed in the
+ * exception, so that the caller may return it to the RESTCONF client
+ * to display.
*
* @param streamId ID of the RESTCONF stream to subscribe
* @param output A string data stream
- * @throws RestconfException if the Event Stream cannot be subscribed due to
- * reasons such as the nonexistence of the target
- * stream or unable to allocate any free worker
- * thread to handle the request
+ * @throws RestconfException if the Event Stream cannot be subscribed
*/
- void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException;
+ void subscribeEventStream(String streamId, ChunkedOutput<String> output)
+ throws RestconfException;
}
diff --git a/protocols/restconf/server/app/app.xml b/protocols/restconf/server/app/app.xml
index 1e8723e..28775b6 100644
--- a/protocols/restconf/server/app/app.xml
+++ b/protocols/restconf/server/app/app.xml
@@ -22,4 +22,5 @@
<artifact>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-restconf-server-utils/${project.version}</artifact>
</app>
diff --git a/protocols/restconf/server/app/features.xml b/protocols/restconf/server/app/features.xml
index b7bcce4..2ef4707 100644
--- a/protocols/restconf/server/app/features.xml
+++ b/protocols/restconf/server/app/features.xml
@@ -21,5 +21,6 @@
<bundle>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-restconf-server-utils/${project.version}</bundle>
</feature>
</features>
diff --git a/protocols/restconf/server/app/pom.xml b/protocols/restconf/server/app/pom.xml
index 0875b99..4f20be1 100644
--- a/protocols/restconf/server/app/pom.xml
+++ b/protocols/restconf/server/app/pom.xml
@@ -51,5 +51,10 @@
<artifactId>onos-restconf-server-rpp</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-restconf-server-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
diff --git a/protocols/restconf/server/pom.xml b/protocols/restconf/server/pom.xml
index 6ef2700..9874b45 100644
--- a/protocols/restconf/server/pom.xml
+++ b/protocols/restconf/server/pom.xml
@@ -34,6 +34,7 @@
<module>restconfmgr</module>
<module>rpp</module>
<module>app</module>
+ <module>utils</module>
</modules>
<description>RESTCONF Server Module</description>
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
diff --git a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
index f2608a3..35abdb0 100644
--- a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
+++ b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.glassfish.jersey.server.ChunkedOutput;
+import org.onosproject.protocol.restconf.server.api.Patch;
import org.onosproject.protocol.restconf.server.api.RestconfException;
import org.onosproject.protocol.restconf.server.api.RestconfService;
import org.onosproject.rest.AbstractWebResource;
@@ -39,8 +40,11 @@
import java.io.IOException;
import java.io.InputStream;
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static org.slf4j.LoggerFactory.getLogger;
+
/*
* This class is the main implementation of the RESTCONF Protocol
* Proxy module. Currently it only handles some basic operations
@@ -134,7 +138,8 @@
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
- public Response handlePostRequest(@PathParam("identifier") String uriString, InputStream stream) {
+ public Response handlePostRequest(@PathParam("identifier") String uriString,
+ InputStream stream) {
log.debug("handlePostRequest: {}", uriString);
@@ -145,14 +150,14 @@
return Response.created(uriInfo.getRequestUri()).build();
} catch (JsonProcessingException e) {
log.error("ERROR: handlePostRequest ", e);
- return Response.status(Response.Status.BAD_REQUEST).build();
+ return Response.status(BAD_REQUEST).build();
} catch (RestconfException e) {
log.error("ERROR: handlePostRequest: {}", e.getMessage());
log.debug("Exception in handlePostRequest:", e);
return e.getResponse();
} catch (IOException ex) {
log.error("ERROR: handlePostRequest ", ex);
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ return Response.status(INTERNAL_SERVER_ERROR).build();
}
}
@@ -174,7 +179,8 @@
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
- public Response handlePutRequest(@PathParam("identifier") String uriString, InputStream stream) {
+ public Response handlePutRequest(@PathParam("identifier") String uriString,
+ InputStream stream) {
log.debug("handlePutRequest: {}", uriString);
@@ -185,14 +191,14 @@
return Response.created(uriInfo.getRequestUri()).build();
} catch (JsonProcessingException e) {
log.error("ERROR: handlePutRequest ", e);
- return Response.status(Response.Status.BAD_REQUEST).build();
+ return Response.status(BAD_REQUEST).build();
} catch (RestconfException e) {
log.error("ERROR: handlePutRequest: {}", e.getMessage());
log.debug("Exception in handlePutRequest:", e);
return e.getResponse();
} catch (IOException ex) {
log.error("ERROR: handlePutRequest ", ex);
- return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ return Response.status(INTERNAL_SERVER_ERROR).build();
}
}
@@ -223,4 +229,41 @@
}
}
+ /**
+ * Handles a RESTCONF PATCH operation against a data resource.
+ * If the PATCH request succeeds, a "200 OK" status-line is returned if
+ * there is a message-body, and "204 No Content" is returned if no
+ * response message-body is sent.
+ *
+ * @param uriString URI of the data resource
+ * @param stream Input JSON object
+ * @return HTTP response
+ */
+ @Patch
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("data/{identifier : .+}")
+ public Response handlePatchRequest(@PathParam("identifier") String uriString,
+ InputStream stream) {
+
+ log.debug("handlePatchRequest: {}", uriString);
+
+ try {
+ ObjectNode rootNode = (ObjectNode) mapper().readTree(stream);
+
+ service.runPatchOperationOnDataResource(uriString, rootNode);
+ return Response.ok().build();
+ } catch (JsonProcessingException e) {
+ log.error("ERROR: handlePatchRequest ", e);
+ return Response.status(BAD_REQUEST).build();
+ } catch (RestconfException e) {
+ log.error("ERROR: handlePatchRequest: {}", e.getMessage());
+ log.debug("Exception in handlePatchRequest:", e);
+ return e.getResponse();
+ } catch (IOException ex) {
+ log.error("ERROR: handlePatchRequest ", ex);
+ return Response.status(INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
}
diff --git a/protocols/restconf/server/utils/BUCK b/protocols/restconf/server/utils/BUCK
new file mode 100644
index 0000000..d1410c0
--- /dev/null
+++ b/protocols/restconf/server/utils/BUCK
@@ -0,0 +1,8 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//apps/yms/api:onos-apps-yms-api',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+)
diff --git a/protocols/restconf/server/utils/pom.xml b/protocols/restconf/server/utils/pom.xml
new file mode 100644
index 0000000..943be73
--- /dev/null
+++ b/protocols/restconf/server/utils/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>onos-restconf-server</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.8.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>onos-restconf-server-utils</artifactId>
+ <packaging>bundle</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-yms-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/JsonParseException.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/JsonParseException.java
new file mode 100644
index 0000000..cd178c5
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/JsonParseException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.exceptions;
+
+/**
+ * Represents class of errors related to Json parse utils.
+ */
+public class JsonParseException extends RuntimeException {
+
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message describing the specific nature of the error
+ */
+ public JsonParseException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the specified message and the underlying
+ * cause.
+ *
+ * @param message the message describing the specific nature of the error
+ * @param cause the underlying cause of this error
+ */
+ public JsonParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/YdtParseException.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/YdtParseException.java
new file mode 100644
index 0000000..c2e121b
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/YdtParseException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.exceptions;
+
+/**
+ * Represents class of errors related to YDT parse utils.
+ */
+public class YdtParseException extends RuntimeException {
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message describing the specific nature of the error
+ */
+ public YdtParseException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the specified message and the underlying
+ * cause.
+ *
+ * @param message the message describing the specific nature of the error
+ * @param cause the underlying cause of this error
+ */
+ public YdtParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/package-info.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/package-info.java
new file mode 100644
index 0000000..508bca0
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/exceptions/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * Parse utils custom exceptions.
+ */
+package org.onosproject.protocol.restconf.server.utils.exceptions;
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonBuilder.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonBuilder.java
new file mode 100644
index 0000000..f56422f
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonBuilder.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.api;
+
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.util.Set;
+
+/**
+ * Abstraction of an entity which provides interfaces to build and obtain JSON
+ * data tree.
+ */
+public interface JsonBuilder {
+
+ /**
+ * Adds a to half (a left brace/bracket and the field name) of a JSON
+ * object/array to the JSON tree. This method is used by protocols which
+ * knows the nature (object/array) of node.
+ *
+ * @param fieldName name of child to be added
+ * @param nodeType the type of the child
+ */
+ void addNodeTopHalf(String fieldName, JsonNodeType nodeType);
+
+ /**
+ * Adds a child with value and a comma to the JSON tree.
+ * Protocols unaware of nature of node (single/multiple) will use it to add
+ * both single instance and multi instance node. Protocols aware of nature
+ * of node will use it for single instance value node addition.
+ *
+ * @param fieldName name of child to be added
+ * @param value the type of the child
+ */
+ void addNodeWithValueTopHalf(String fieldName, String value);
+
+ /**
+ * Adds a child with list of values to JSON data tree. This method is
+ * used by protocols which knows the nature (object/array) of node for
+ * ArrayNode addition.
+ *
+ * @param fieldName name of child to be added
+ * @param sets the value list of the child
+ */
+ void addNodeWithSetTopHalf(String fieldName, Set<String> sets);
+
+ /**
+ * Adds the bottom half(a right brace/bracket) of a JSON object/array to
+ * the JSON tree. for the text, a comma should be taken out.
+ *
+ * @param nodeType the type of the child
+ */
+ void addNodeBottomHalf(JsonNodeType nodeType);
+
+ /**
+ * Returns the JSON tree after build operations in the format of string.
+ *
+ * @return the final string JSON tree after build operations
+ */
+ String getTreeString();
+
+ /**
+ * Returns the JSON tree after build operations in the format of ObjectNode.
+ *
+ * @return the final ObjectNode JSON tree after build operations
+ */
+ ObjectNode getTreeNode();
+}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonListener.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonListener.java
new file mode 100644
index 0000000..7432dd3
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.api;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Abstraction of an entity which provide call back methods which are called
+ * by JSON walker while walking the JSON data tree. This interface needs to be
+ * implemented by protocol implementing listener's based call backs while JSON
+ * walk.
+ */
+public interface JsonListener {
+
+ /**
+ * Callback invoked during a node entry.
+ * All the related information about the node can be obtain from the JSON
+ * object.
+ *
+ * @param fieldName the field name of the JSON Node value
+ * @param node the JsonNode which is walked through
+ */
+ void enterJsonNode(String fieldName, JsonNode node);
+
+ /**
+ * Callback invoked during a node exit.
+ * All the related information about the node can be obtain from the JSON
+ * node.
+ *
+ * @param jsonNode JSON node which has been walked through
+ */
+ void exitJsonNode(JsonNode jsonNode);
+
+}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonWalker.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonWalker.java
new file mode 100644
index 0000000..59ce2ab
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/JsonWalker.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.api;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Abstraction of an entity which provides interfaces for Json walk.
+ * This interface serve as common tools for anyone who needs to parse
+ * the json node with depth-first algorithm.
+ */
+public interface JsonWalker {
+
+ /**
+ * Walks the JSON data tree. Protocols implements JSON listener service
+ * and walks JSON tree with input as implemented object. JSON walker
+ * provides call backs to implemented methods. For the original json
+ * node(come from NB), there is a field name which is something like the
+ * module name of a YANG model. If not, the fieldName can be null.
+ *
+ * @param jsonListener Json listener implemented by the user
+ * @param fieldName the original object node field
+ * @param node the json node which needs to be walk
+ */
+ void walk(JsonListener jsonListener, String fieldName, ObjectNode node);
+}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/package-info.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/package-info.java
new file mode 100644
index 0000000..51d28df
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/api/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * Provider json related process interface.
+ */
+package org.onosproject.protocol.restconf.server.utils.parser.api;
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonBuilder.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonBuilder.java
new file mode 100644
index 0000000..b5f2911
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonBuilder.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.json;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
+import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Represents implementation of interfaces to build and obtain JSON data tree.
+ */
+public class DefaultJsonBuilder implements JsonBuilder {
+
+ private static final String LEFT_BRACE = "{";
+ private static final String RIGHT_BRACE = "}";
+ private static final String LEFT_BRACKET = "[";
+ private static final String RIGHT_BRACKET = "]";
+ private static final String COMMA = ",";
+ private static final String COLON = ":";
+ private static final String QUOTE = "\"";
+ private static final String E_UNSUP_TYPE = "Unsupported node type %s " +
+ "field name is %s fieldName";
+
+ private Logger log = LoggerFactory.getLogger(getClass());
+
+ private StringBuilder treeString;
+
+ /**
+ * Creates a Default Json Builder with a specific root name.
+ *
+ * @param rootName the start string of the Json builder
+ */
+ public DefaultJsonBuilder(String rootName) {
+ checkNotNull(rootName);
+ treeString = new StringBuilder(rootName);
+ }
+
+ /**
+ * Creates a Default Json Builder with a default root name.
+ */
+ public DefaultJsonBuilder() {
+ treeString = new StringBuilder(LEFT_BRACE);
+ }
+
+ @Override
+ public void addNodeTopHalf(String fieldName, JsonNodeType nodeType) {
+
+ appendField(fieldName);
+
+ switch (nodeType) {
+ case OBJECT:
+ treeString.append(LEFT_BRACE);
+ break;
+ case ARRAY:
+ treeString.append(LEFT_BRACKET);
+ break;
+ default:
+ throw new JsonParseException(String.format(E_UNSUP_TYPE,
+ nodeType, fieldName));
+ }
+ }
+
+ @Override
+ public void addNodeWithValueTopHalf(String fieldName, String value) {
+ if (isNullOrEmpty(fieldName)) {
+ return;
+ }
+ appendField(fieldName);
+ if (value.isEmpty()) {
+ return;
+ }
+ treeString.append(QUOTE)
+ .append(value)
+ .append(QUOTE + COMMA);
+ }
+
+ @Override
+ public void addNodeWithSetTopHalf(String fieldName, Set<String> sets) {
+ if (isNullOrEmpty(fieldName)) {
+ return;
+ }
+ appendField(fieldName);
+ treeString.append(LEFT_BRACKET);
+ for (String el : sets) {
+ treeString.append(QUOTE)
+ .append(el)
+ .append(QUOTE + COMMA);
+ }
+ }
+
+ @Override
+ public void addNodeBottomHalf(JsonNodeType nodeType) {
+
+ switch (nodeType) {
+ case OBJECT:
+ removeCommaIfExist();
+ treeString.append(RIGHT_BRACE + COMMA);
+ break;
+
+ case ARRAY:
+ removeCommaIfExist();
+ treeString.append(RIGHT_BRACKET + COMMA);
+ break;
+
+ case BINARY:
+ case BOOLEAN:
+ case MISSING:
+ case NULL:
+ case NUMBER:
+ case POJO:
+ case STRING:
+ log.debug("Unimplemented node type {}", nodeType);
+ break;
+
+ default:
+ throw new JsonParseException("Unsupported json node type " +
+ nodeType);
+ }
+ }
+
+ @Override
+ public String getTreeString() {
+ removeCommaIfExist();
+ return treeString.append(RIGHT_BRACE).toString();
+ }
+
+ @Override
+ public ObjectNode getTreeNode() {
+ ObjectNode node = null;
+ try {
+ node = (ObjectNode) (new ObjectMapper()).readTree(getTreeString());
+ } catch (IOException e) {
+ log.error("Parse json string failed {}", e.getMessage());
+ }
+ return node;
+ }
+
+
+ private void appendField(String fieldName) {
+ if (!isNullOrEmpty(fieldName)) {
+ treeString.append(QUOTE)
+ .append(fieldName)
+ .append(QUOTE + COLON);
+ }
+ }
+
+ private void removeCommaIfExist() {
+ int lastIndex = treeString.length() - 1;
+ if (treeString.charAt(lastIndex) == COMMA.charAt(0)) {
+ treeString.deleteCharAt(lastIndex);
+ }
+ }
+}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonWalker.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonWalker.java
new file mode 100644
index 0000000..c1802ac
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonWalker.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.json;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.protocol.restconf.server.utils.parser.api.JsonWalker;
+import org.onosproject.protocol.restconf.server.utils.parser.api.JsonListener;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Represents implementation of JSON walk, which walks the JSON object node.
+ */
+public class DefaultJsonWalker implements JsonWalker {
+ @Override
+ public void walk(JsonListener jsonListener, String fieldName,
+ ObjectNode objectNode) {
+
+ //enter the object node, the original ObjectNode should have a module
+ //name as fieldName.
+ jsonListener.enterJsonNode(fieldName, objectNode);
+ //the node has no children, then exist and return.
+ if (!objectNode.isContainerNode()) {
+ jsonListener.exitJsonNode(objectNode);
+ return;
+ }
+
+ Iterator<Map.Entry<String, JsonNode>> fields = objectNode.fields();
+ while (fields.hasNext()) {
+ //get the children entry of the node
+ Map.Entry<String, JsonNode> currentChild = fields.next();
+ String key = currentChild.getKey();
+ JsonNode value = currentChild.getValue();
+ //if the entry's value has its own children, do a recursion.
+ //if the entry has no children, store the key and value.
+ //for we don't know the specific type of the entry's value, we
+ // should give it to a method which can handle JsonNode
+ if (value.isContainerNode()) {
+ walkJsonNode(jsonListener, key, value);
+ } else {
+ jsonListener.enterJsonNode(key, value);
+ jsonListener.exitJsonNode(value);
+ }
+ }
+ jsonListener.exitJsonNode(objectNode);
+ }
+
+ /**
+ * Walks the JSON data tree. This method is called when we don't know
+ * the exact type of a json node.
+ *
+ * @param jsonListener Json listener implemented by the user
+ * @param fieldName the original object node field
+ * @param rootNode the json node which needs to be walk
+ */
+ private void walkJsonNode(JsonListener jsonListener, String fieldName,
+ JsonNode rootNode) {
+ if (rootNode.isObject()) {
+ walk(jsonListener, fieldName, (ObjectNode) rootNode);
+ return;
+ }
+
+ if (rootNode.isArray()) {
+ walkArrayNode(jsonListener, fieldName, (ArrayNode) rootNode);
+ }
+ }
+
+ /**
+ * Walks the JSON data tree. This method is called when the user knows the
+ * json node type is ArrayNode.
+ *
+ * @param jsonListener Json listener implemented by the user
+ * @param fieldName the original object node field
+ * @param rootNode the json node which needs to be walk
+ */
+ private void walkArrayNode(JsonListener jsonListener, String fieldName,
+ ArrayNode rootNode) {
+ if (rootNode == null) {
+ return;
+ }
+ //enter the array node.
+ jsonListener.enterJsonNode(fieldName, rootNode);
+ Iterator<JsonNode> children = rootNode.elements();
+ while (children.hasNext()) {
+ JsonNode currentChild = children.next();
+ if (currentChild.isContainerNode()) {
+ walkJsonNode(jsonListener, null, currentChild);
+ }
+ }
+ jsonListener.exitJsonNode(rootNode);
+ }
+}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java
new file mode 100644
index 0000000..4a130f7
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.json;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
+import org.onosproject.protocol.restconf.server.utils.parser.api.JsonListener;
+import org.onosproject.yms.ydt.YdtBuilder;
+import org.onosproject.yms.ydt.YdtContext;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import static com.fasterxml.jackson.databind.node.JsonNodeType.ARRAY;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.yms.ydt.YdtType.MULTI_INSTANCE_NODE;
+import static org.onosproject.yms.ydt.YdtType.SINGLE_INSTANCE_NODE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Represents default implementation of codec JSON listener.
+ */
+public class JsonToYdtListener implements JsonListener {
+
+ private static final String INPUT_FIELD_NAME = "input";
+ private static final String COLON = ":";
+ private static final int INPUT_FIELD_LENGTH = 2;
+ private static final String E_UNSUP_TYPE = "Unsupported node type %s " +
+ "field name is %s fieldName";
+
+ private Logger log = getLogger(getClass());
+
+ private YdtBuilder ydtBuilder;
+ private String defaultMultiInsNodeName;
+ private YdtContext rpcModule;
+
+ /**
+ * Creates a listener for the process of a Json Object, the listener will
+ * try to transfer the JSON object to a Ydt builder.
+ *
+ * @param ydtBuilder the YDT builder to build a YDT object
+ */
+ public JsonToYdtListener(YdtBuilder ydtBuilder) {
+ this.ydtBuilder = ydtBuilder;
+ }
+
+ @Override
+ public void enterJsonNode(String fieldName, JsonNode node) {
+ if (isNullOrEmpty(fieldName)) {
+ if (!isNullOrEmpty(defaultMultiInsNodeName)) {
+ ydtBuilder.addChild(defaultMultiInsNodeName, null,
+ MULTI_INSTANCE_NODE);
+ }
+ return;
+ }
+ JsonNodeType nodeType = node.getNodeType();
+ switch (nodeType) {
+ case OBJECT:
+ processObjectNode(fieldName);
+ break;
+
+ case ARRAY:
+ processArrayNode(fieldName, node);
+ break;
+
+ //TODO for now, just process the following three node type
+ case STRING:
+ case NUMBER:
+ case BOOLEAN:
+ ydtBuilder.addLeaf(fieldName, null, node.asText());
+ break;
+
+ case BINARY:
+ case MISSING:
+ case NULL:
+ case POJO:
+ log.debug("Unimplemented node type {}", nodeType);
+ break;
+
+ default:
+ throw new JsonParseException(String.format(E_UNSUP_TYPE,
+ nodeType, fieldName));
+ }
+ }
+
+ @Override
+ public void exitJsonNode(JsonNode jsonNode) {
+ if (jsonNode.getNodeType() == ARRAY) {
+ return;
+ }
+ ydtBuilder.traverseToParent();
+ YdtContext curNode = ydtBuilder.getCurNode();
+ //if the current node is the RPC node, then should go to the father
+ //for we have enter the RPC node and Input node at the same time
+ //and the input is the only child of RPC node.
+
+ if (curNode == null) {
+ return;
+ }
+ String name = curNode.getName();
+ if (rpcModule != null && name.equals(rpcModule.getName())) {
+ ydtBuilder.traverseToParent();
+ }
+ }
+
+ private void processObjectNode(String fieldName) {
+ String[] segments = fieldName.split(COLON);
+ Boolean isLastInput = segments.length == INPUT_FIELD_LENGTH &&
+ segments[INPUT_FIELD_LENGTH - 1].equals(INPUT_FIELD_NAME);
+ int first = 0;
+ int second = 1;
+ if (isLastInput) {
+ ydtBuilder.addChild(segments[first], null, SINGLE_INSTANCE_NODE);
+ rpcModule = ydtBuilder.getCurNode();
+ ydtBuilder.addChild(segments[second], null, SINGLE_INSTANCE_NODE);
+ } else {
+ ydtBuilder.addChild(fieldName, null, SINGLE_INSTANCE_NODE);
+ }
+ }
+
+ private void processArrayNode(String fieldName, JsonNode node) {
+ ArrayNode arrayNode = (ArrayNode) node;
+ Set<String> sets = new HashSet<>();
+ Iterator<JsonNode> elements = arrayNode.elements();
+ boolean isLeafList = true;
+ while (elements.hasNext()) {
+ JsonNode element = elements.next();
+ JsonNodeType eleType = element.getNodeType();
+
+ if (eleType == JsonNodeType.STRING ||
+ eleType == JsonNodeType.NUMBER ||
+ eleType == JsonNodeType.BOOLEAN) {
+ sets.add(element.asText());
+ } else {
+ isLeafList = false;
+ }
+ }
+ if (isLeafList) {
+ //leaf-list
+ ydtBuilder.addLeaf(fieldName, null, sets);
+ } else {
+ this.defaultMultiInsNodeName = fieldName;
+ }
+ }
+}
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
new file mode 100644
index 0000000..fa8b133
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.json;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
+import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
+import org.onosproject.yms.ydt.YdtBuilder;
+import org.onosproject.yms.ydt.YdtContext;
+import org.onosproject.yms.ydt.YdtContextOperationType;
+import org.onosproject.yms.ydt.YdtListener;
+import org.onosproject.yms.ydt.YdtType;
+import org.onosproject.yms.ydt.YdtWalker;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
+
+/**
+ * Utils to complete the conversion between JSON and YDT(YANG DATA MODEL).
+ */
+public final class ParserUtils {
+
+ private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+ private static final Splitter COMMA_SPLITTER = Splitter.on(',');
+ private static final String EQUAL = "=";
+ private static final String COMMA = ",";
+ private static final String COLON = ":";
+ private static final String URI_ENCODING_CHAR_SET = "ISO-8859-1";
+ private static final String ERROR_LIST_MSG = "List/Leaf-list node should be " +
+ "in format \"nodeName=key\"or \"nodeName=instance-value\"";
+ private static final String ERROR_MODULE_MSG = "First node should be in " +
+ "format \"moduleName:nodeName\"";
+
+ // no instantiation
+ private ParserUtils() {
+ }
+
+ /**
+ * Converts URI identifier to YDT builder.
+ *
+ * @param id the uri identifier from web request
+ * @param builder the base ydt builder
+ * @param opType the ydt operation type for the uri
+ */
+ public static void convertUriToYdt(String id,
+ YdtBuilder builder,
+ YdtContextOperationType opType) {
+ checkNotNull(id, "uri identifier should not be null");
+ List<String> paths = urlPathArgsDecode(SLASH_SPLITTER.split(id));
+ if (!paths.isEmpty()) {
+ processPathSegments(paths, builder, opType);
+ }
+ }
+
+ /**
+ * Converts JSON objectNode to YDT builder. The objectNode can be any
+ * standard JSON node, node just for RESTconf payload.
+ *
+ * @param objectNode the objectNode from web request
+ * @param builder the base ydt builder
+ */
+ public static void convertJsonToYdt(ObjectNode objectNode,
+ YdtBuilder builder) {
+
+ JsonToYdtListener listener = new JsonToYdtListener(builder);
+ new DefaultJsonWalker().walk(listener, null, objectNode);
+ }
+
+ /**
+ * Converts a Ydt context tree to a JSON object.
+ *
+ * @param rootName the name of the YdtContext from which the YdtListener
+ * start to builder a Json Object
+ * @param context a abstract data model for YANG data
+ * @param walker abstraction of an entity which provides interfaces for
+ * YDT walk
+ * @return the JSON node corresponding the YANG data
+ */
+ public static ObjectNode convertYdtToJson(String rootName,
+ YdtContext context,
+ YdtWalker walker) {
+ JsonBuilder builder = new DefaultJsonBuilder();
+ YdtListener listener = new YdtToJsonListener(rootName, builder);
+ walker.walk(listener, context);
+ return builder.getTreeNode();
+ }
+
+ /**
+ * Converts a list of path segments to a YDT builder tree.
+ *
+ * @param paths the list of path segments split from URI
+ * @param builder the base YDT builder
+ * @param opType the YDT operation type for the Path segment
+ * @return the YDT builder with the tree info of paths
+ */
+ private static YdtBuilder processPathSegments(List<String> paths,
+ YdtBuilder builder,
+ YdtContextOperationType opType) {
+ if (paths.isEmpty()) {
+ return builder;
+ }
+ boolean isLastNode = paths.size() == 1;
+ YdtContextOperationType opTypeForThisNode = isLastNode ? opType : NONE;
+
+ String path = paths.iterator().next();
+ if (path.contains(COLON)) {
+ addModule(builder, path);
+ addNode(path, builder, opTypeForThisNode);
+ } else if (path.contains(EQUAL)) {
+ addListOrLeafList(path, builder, opTypeForThisNode);
+ } else {
+ addLeaf(path, builder, opTypeForThisNode);
+ }
+
+ if (isLastNode) {
+ return builder;
+ }
+ List<String> remainPaths = paths.subList(1, paths.size());
+ processPathSegments(remainPaths, builder, opType);
+
+ return builder;
+ }
+
+ private static YdtBuilder addModule(YdtBuilder builder, String path) {
+ String moduleName = getPreSegment(path, COLON);
+ if (moduleName == null) {
+ throw new JsonParseException(ERROR_MODULE_MSG);
+ }
+ builder.addChild(moduleName, null, YdtType.SINGLE_INSTANCE_NODE);
+ return builder;
+ }
+
+ private static YdtBuilder addNode(String path, YdtBuilder builder,
+ YdtContextOperationType opType) {
+ String nodeName = getLatterSegment(path, COLON);
+ builder.addChild(nodeName,
+ null,
+ YdtType.SINGLE_INSTANCE_NODE,
+ opType);
+ return builder;
+ }
+
+ private static YdtBuilder addListOrLeafList(String path,
+ YdtBuilder builder,
+ YdtContextOperationType opType) {
+ String nodeName = getPreSegment(path, EQUAL);
+ String keyStr = getLatterSegment(path, EQUAL);
+ if (keyStr == null) {
+ throw new JsonParseException(ERROR_LIST_MSG);
+ }
+ builder.setDefaultEditOperationType(opType);
+ if (keyStr.contains(COMMA)) {
+ List<String> keys = Lists.
+ newArrayList(COMMA_SPLITTER.split(keyStr));
+ builder.addMultiInstanceChild(nodeName, null, keys);
+ } else {
+ builder.addMultiInstanceChild(nodeName, null,
+ Lists.newArrayList(keyStr));
+ }
+ return builder;
+ }
+
+ private static YdtBuilder addLeaf(String path, YdtBuilder builder,
+ YdtContextOperationType opType) {
+ checkNotNull(path);
+ builder.addChild(path, null, opType);
+ return builder;
+ }
+
+ /**
+ * Returns the previous segment of a path which is separated by a split char.
+ * For example:
+ * <pre>
+ * "foo:bar", ":" --> "foo"
+ * </pre>
+ *
+ * @param path the original path string
+ * @param splitChar char used to split the path
+ * @return the previous segment of the path
+ */
+ private static String getPreSegment(String path, String splitChar) {
+ int idx = path.indexOf(splitChar);
+ if (idx == -1) {
+ return null;
+ }
+
+ if (path.indexOf(splitChar, idx + 1) != -1) {
+ return null;
+ }
+
+ return path.substring(0, idx);
+ }
+
+ /**
+ * Returns the latter segment of a path which is separated by a split char.
+ * For example:
+ * <pre>
+ * "foo:bar", ":" --> "bar"
+ * </pre>
+ *
+ * @param path the original path string
+ * @param splitChar char used to split the path
+ * @return the latter segment of the path
+ */
+ private static String getLatterSegment(String path, String splitChar) {
+ int idx = path.indexOf(splitChar);
+ if (idx == -1) {
+ return path;
+ }
+
+ if (path.indexOf(splitChar, idx + 1) != -1) {
+ return null;
+ }
+
+ return path.substring(idx + 1);
+ }
+
+ /**
+ * Converts a list of path from the original format to ISO-8859-1 code.
+ *
+ * @param paths the original paths
+ * @return list of decoded paths
+ */
+ public static List<String> urlPathArgsDecode(Iterable<String> paths) {
+ try {
+ List<String> decodedPathArgs = new ArrayList<>();
+ for (String pathArg : paths) {
+ String decode = URLDecoder.decode(pathArg,
+ URI_ENCODING_CHAR_SET);
+ decodedPathArgs.add(decode);
+ }
+ return decodedPathArgs;
+ } catch (UnsupportedEncodingException e) {
+ throw new JsonParseException("Invalid URL path arg '" +
+ paths + "': ", e);
+ }
+ }
+}
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
new file mode 100644
index 0000000..b0ce26b
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2016-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.protocol.restconf.server.utils.parser.json;
+
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import org.onosproject.protocol.restconf.server.utils.exceptions.YdtParseException;
+import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
+import org.onosproject.yms.ydt.YdtContext;
+import org.onosproject.yms.ydt.YdtListener;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Represents implementation of codec YDT listener.
+ */
+public class YdtToJsonListener implements YdtListener {
+
+ private static final String EMPTY = "";
+ private JsonBuilder jsonBuilder;
+ //the root name of the json
+ //the input YdtContext is usually a total tree of a YANG resource
+ //this property is used to mark the start of the request node.
+ private String rootName;
+ //the parse state
+ private boolean isBegin;
+
+ /**
+ * Creates a listener for the process of a Ydt, the listener will try to
+ * transfer the Ydt to a JSON Object.
+ *
+ * @param rootName the name of a specific YdtContext begin to process
+ * @param jsonBuilder the JSON builder to build a JSON object
+ */
+ public YdtToJsonListener(String rootName, JsonBuilder jsonBuilder) {
+ this.jsonBuilder = jsonBuilder;
+ this.rootName = rootName;
+ this.isBegin = isNullOrEmpty(rootName);
+ }
+
+ @Override
+ public void enterYdtNode(YdtContext ydtContext) {
+ String name = ydtContext.getName();
+
+ if (!isBegin && name.equals(rootName)) {
+ isBegin = true;
+ }
+ if (!isBegin) {
+ return;
+ }
+
+ switch (ydtContext.getYdtType()) {
+
+ case SINGLE_INSTANCE_NODE:
+ jsonBuilder.addNodeTopHalf(name, JsonNodeType.OBJECT);
+ break;
+ case MULTI_INSTANCE_NODE:
+ YdtContext preNode = ydtContext.getPreviousSibling();
+ if (preNode == null || !preNode.getName().equals(name)) {
+ jsonBuilder.addNodeTopHalf(name, JsonNodeType.ARRAY);
+ }
+ jsonBuilder.addNodeTopHalf(EMPTY, JsonNodeType.OBJECT);
+ break;
+ case SINGLE_INSTANCE_LEAF_VALUE_NODE:
+ jsonBuilder.addNodeWithValueTopHalf(name, ydtContext.getValue());
+ break;
+ case MULTI_INSTANCE_LEAF_VALUE_NODE:
+ jsonBuilder.addNodeWithSetTopHalf(name, ydtContext.getValueSet());
+ break;
+ default:
+ throw new YdtParseException("unknown Ydt type " +
+ ydtContext.getYdtType());
+ }
+
+ }
+
+ @Override
+ public void exitYdtNode(YdtContext ydtContext) {
+
+ if (!isBegin) {
+ return;
+ }
+
+ String curName = ydtContext.getName();
+ YdtContext nextNode = ydtContext.getNextSibling();
+ switch (ydtContext.getYdtType()) {
+
+ case SINGLE_INSTANCE_NODE:
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
+ break;
+ case MULTI_INSTANCE_NODE:
+ if (nextNode == null || !nextNode.getName().equals(curName)) {
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
+ } else {
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
+ }
+ break;
+ case SINGLE_INSTANCE_LEAF_VALUE_NODE:
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.STRING);
+ break;
+ case MULTI_INSTANCE_LEAF_VALUE_NODE:
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
+ break;
+ default:
+ throw new YdtParseException("Unknown Ydt type " +
+ ydtContext.getYdtType());
+ }
+ if (curName.equals(rootName) &&
+ (nextNode == null || !nextNode.getName().equals(rootName))) {
+ isBegin = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/package-info.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/package-info.java
new file mode 100644
index 0000000..3e3c49b
--- /dev/null
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016-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.
+ */
+
+/**
+ * Provider utilities to support the data format based encoding and decoding,
+ * used by YMSC to operate on different data format and YDT(YANG DATA TYPE).
+ */
+
+package org.onosproject.protocol.restconf.server.utils.parser.json;
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/test/java/org/onosproject/restconf/utils/parser/json/DefaultJsonWalkerTest.java b/protocols/restconf/server/utils/src/test/java/org/onosproject/restconf/utils/parser/json/DefaultJsonWalkerTest.java
new file mode 100644
index 0000000..c82f0f8
--- /dev/null
+++ b/protocols/restconf/server/utils/src/test/java/org/onosproject/restconf/utils/parser/json/DefaultJsonWalkerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2016-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.restconf.utils.parser.json;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.protocol.restconf.server.utils.parser.api.JsonListener;
+import org.onosproject.protocol.restconf.server.utils.parser.json.DefaultJsonWalker;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class DefaultJsonWalkerTest {
+
+ private static final String[] EXP_TEXT_ARRAY = {
+ "Enter: type is OBJECT name is null",
+ "Enter: type is STRING name is name",
+ "Exit: type is STRING",
+ "Enter: type is STRING name is surname",
+ "Exit: type is STRING",
+ "Exit: type is OBJECT"
+ };
+
+ private static final String[] EXP_NODE_LIST_ARRAY = {
+ "Enter: type is OBJECT name is null",
+ "Enter: type is STRING name is surname",
+ "Exit: type is STRING",
+ "Enter: type is ARRAY name is networklist",
+ "Exit: type is ARRAY",
+ "Exit: type is OBJECT"
+ };
+
+ private static final String[] EXP_NODE_WITH_ARRAY = {
+ "Enter: type is OBJECT name is null",
+ "Enter: type is STRING name is surname",
+ "Exit: type is STRING",
+ "Enter: type is ARRAY name is networklist",
+ "Enter: type is OBJECT name is null",
+ "Enter: type is STRING name is network-id",
+ "Exit: type is STRING",
+ "Enter: type is STRING name is server-provided",
+ "Exit: type is STRING",
+ "Exit: type is OBJECT",
+ "Enter: type is OBJECT name is null",
+ "Enter: type is STRING name is network-id",
+ "Exit: type is STRING",
+ "Enter: type is STRING name is server-provided",
+ "Exit: type is STRING",
+ "Exit: type is OBJECT",
+ "Enter: type is OBJECT name is null",
+ "Enter: type is STRING name is network-id",
+ "Exit: type is STRING",
+ "Enter: type is STRING name is server-provided",
+ "Exit: type is STRING",
+ "Exit: type is OBJECT",
+ "Exit: type is ARRAY",
+ "Exit: type is OBJECT"
+ };
+ private static final String WRONG_CONTENT_MSG = "Wrong content in array";
+
+ private final List<String> logger = new ArrayList<>();
+
+ DefaultJsonWalker defaultJsonWalker = new DefaultJsonWalker();
+ InternalJsonListener jsonListener = new InternalJsonListener();
+
+ ObjectNode arrayNode;
+ ObjectNode textNode;
+ ObjectNode listNode;
+
+ @Before
+ public void setup() throws Exception {
+ textNode = loadJsonFile("/textNode.json");
+ listNode = loadJsonFile("/listNode.json");
+ arrayNode = loadJsonFile("/arrayNode.json");
+ }
+
+ private ObjectNode loadJsonFile(String path) throws Exception {
+ InputStream jsonStream = getClass().getResourceAsStream(path);
+ ObjectMapper mapper = new ObjectMapper();
+ return (ObjectNode) mapper.readTree(jsonStream);
+ }
+
+ private void assertWalkResult(String[] expectArray, List<String> logger) {
+ for (int i = 0; i < expectArray.length; i++) {
+ assertThat(WRONG_CONTENT_MSG,
+ true,
+ is(logger.get(i).contentEquals(expectArray[i])));
+ }
+ }
+
+ @Test
+ public void testWalkTextNode() throws Exception {
+
+ defaultJsonWalker.walk(jsonListener, null, textNode);
+ assertWalkResult(EXP_TEXT_ARRAY, logger);
+ }
+
+ @Test
+ public void testWalkNodeWithList() throws Exception {
+ defaultJsonWalker.walk(jsonListener, null, listNode);
+ assertWalkResult(EXP_NODE_LIST_ARRAY, logger);
+ }
+
+ @Test
+ public void testWalkNodeWithArray() throws Exception {
+ defaultJsonWalker.walk(jsonListener, null, arrayNode);
+ assertWalkResult(EXP_NODE_WITH_ARRAY, logger);
+ }
+
+ private class InternalJsonListener implements JsonListener {
+
+ @Override
+ public void enterJsonNode(String fieldName, JsonNode node) {
+ logger.add("Enter: type is " + node.getNodeType() + " name is " +
+ fieldName);
+ }
+
+ @Override
+ public void exitJsonNode(JsonNode node) {
+ logger.add("Exit: type is " + node.getNodeType());
+ }
+ }
+}
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/test/resources/arrayNode.json b/protocols/restconf/server/utils/src/test/resources/arrayNode.json
new file mode 100644
index 0000000..2035e82
--- /dev/null
+++ b/protocols/restconf/server/utils/src/test/resources/arrayNode.json
@@ -0,0 +1,18 @@
+{
+ "surname": "Bangalore",
+ "networklist":[
+ {
+ "network-id": "520",
+ "server-provided": "123"
+ },
+ {
+ "network-id": "521",
+ "server-provided": "124"
+ },
+ {
+ "network-id": "523",
+ "server-provided": "125"
+ }
+
+ ]
+}
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/test/resources/listNode.json b/protocols/restconf/server/utils/src/test/resources/listNode.json
new file mode 100644
index 0000000..c7902f5
--- /dev/null
+++ b/protocols/restconf/server/utils/src/test/resources/listNode.json
@@ -0,0 +1,8 @@
+{
+ "surname": "Bangalore",
+ "networklist":[
+ "0.79",
+ "1.04",
+ "3.14"
+ ]
+}
\ No newline at end of file
diff --git a/protocols/restconf/server/utils/src/test/resources/textNode.json b/protocols/restconf/server/utils/src/test/resources/textNode.json
new file mode 100644
index 0000000..8894cfb
--- /dev/null
+++ b/protocols/restconf/server/utils/src/test/resources/textNode.json
@@ -0,0 +1,4 @@
+{
+ "name": "Huawei",
+ "surname": "Bangalore"
+}
\ No newline at end of file