mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-12-16 23:01:28 +01:00
[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
This commit is contained in:
parent
e20926ebaa
commit
c58d4be8a5
@ -2,6 +2,7 @@ BUNDLES = [
|
|||||||
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
|
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
|
||||||
'//protocols/restconf/server/restconfmgr:onos-protocols-restconf-server-restconfmgr',
|
'//protocols/restconf/server/restconfmgr:onos-protocols-restconf-server-restconfmgr',
|
||||||
'//protocols/restconf/server/rpp:onos-protocols-restconf-server-rpp',
|
'//protocols/restconf/server/rpp:onos-protocols-restconf-server-rpp',
|
||||||
|
'//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
|
||||||
]
|
]
|
||||||
|
|
||||||
onos_app (
|
onos_app (
|
||||||
|
|||||||
@ -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 {
|
||||||
|
}
|
||||||
@ -25,58 +25,81 @@ import org.glassfish.jersey.server.ChunkedOutput;
|
|||||||
public interface RestconfService {
|
public interface RestconfService {
|
||||||
/**
|
/**
|
||||||
* Processes a GET request against a data resource. The
|
* 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
|
* @param uri URI of the target data resource
|
||||||
* @return JSON representation of the data resource
|
* @return JSON representation of the data resource
|
||||||
* @throws RestconfException if the GET operation cannot be fulfilled due
|
* @throws RestconfException if the GET operation cannot be fulfilled
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException;
|
ObjectNode runGetOperationOnDataResource(String uri)
|
||||||
|
throws RestconfException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a POST request against a data resource. The location of
|
* Processes a POST request against a data resource. The location of
|
||||||
* the target resource is passed in as a URI. And the resource's
|
* 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 uri URI of the data resource to be created
|
||||||
* @param rootNode JSON representation of the data resource
|
* @param rootNode JSON representation of the data resource
|
||||||
* @throws RestconfException if the POST operation cannot be fulfilled due
|
* @throws RestconfException if the POST operation cannot be fulfilled
|
||||||
* reasons such as wrong URI or syntax error
|
|
||||||
* in JSON payload. The proper HTTP error status
|
|
||||||
* code is enclosed in the exception
|
|
||||||
*/
|
*/
|
||||||
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
|
* Processes a PUT request against a data resource. The location of
|
||||||
* the target resource is passed in as a URI. And the resource's
|
* 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 uri URI of the data resource to be created or updated
|
||||||
* @param rootNode JSON representation of the data resource
|
* @param rootNode JSON representation of the data resource
|
||||||
* @throws RestconfException if the PUT operation cannot be fulfilled due
|
* @throws RestconfException if the PUT operation cannot be fulfilled
|
||||||
* reasons such as wrong URI or syntax error
|
|
||||||
* in JSON payload. The proper HTTP error status
|
|
||||||
* code is enclosed in the exception
|
|
||||||
*/
|
*/
|
||||||
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
|
* 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
|
* @param uri URI of the data resource to be deleted
|
||||||
* @throws RestconfException if the DELETE operation cannot be fulfilled due
|
* @throws RestconfException if the DELETE operation cannot be fulfilled
|
||||||
* reasons such as the nonexistence of the target
|
|
||||||
* resource. The proper HTTP error status code is
|
|
||||||
* enclosed in the exception
|
|
||||||
*/
|
*/
|
||||||
void runDeleteOperationOnDataResource(String uri) throws RestconfException;
|
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.
|
* Retrieves the RESTCONF Root directory.
|
||||||
*
|
*
|
||||||
@ -90,13 +113,18 @@ public interface RestconfService {
|
|||||||
* which is passed in from the caller. (The worker thread blocks if
|
* which is passed in from the caller. (The worker thread blocks if
|
||||||
* no events arrive.) The ChuckedOutput is a pipe to which this
|
* no events arrive.) The ChuckedOutput is a pipe to which this
|
||||||
* function acts as the writer and the caller the reader.
|
* 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 streamId ID of the RESTCONF stream to subscribe
|
||||||
* @param output A string data stream
|
* @param output A string data stream
|
||||||
* @throws RestconfException if the Event Stream cannot be subscribed due to
|
* @throws RestconfException if the Event Stream cannot be subscribed
|
||||||
* reasons such as the nonexistence of the target
|
|
||||||
* stream or unable to allocate any free worker
|
|
||||||
* thread to handle the request
|
|
||||||
*/
|
*/
|
||||||
void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException;
|
void subscribeEventStream(String streamId, ChunkedOutput<String> output)
|
||||||
|
throws RestconfException;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,4 +22,5 @@
|
|||||||
<artifact>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</artifact>
|
<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-restconfmanager/${project.version}</artifact>
|
||||||
<artifact>mvn:${project.groupId}/onos-restconf-server-rpp/${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>
|
</app>
|
||||||
|
|||||||
@ -21,5 +21,6 @@
|
|||||||
<bundle>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</bundle>
|
<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-restconfmanager/${project.version}</bundle>
|
||||||
<bundle>mvn:${project.groupId}/onos-restconf-server-rpp/${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>
|
</feature>
|
||||||
</features>
|
</features>
|
||||||
|
|||||||
@ -51,5 +51,10 @@
|
|||||||
<artifactId>onos-restconf-server-rpp</artifactId>
|
<artifactId>onos-restconf-server-rpp</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.onosproject</groupId>
|
||||||
|
<artifactId>onos-restconf-server-utils</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
<module>restconfmgr</module>
|
<module>restconfmgr</module>
|
||||||
<module>rpp</module>
|
<module>rpp</module>
|
||||||
<module>app</module>
|
<module>app</module>
|
||||||
|
<module>utils</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<description>RESTCONF Server Module</description>
|
<description>RESTCONF Server Module</description>
|
||||||
|
|||||||
@ -6,6 +6,8 @@ COMPILE_DEPS = [
|
|||||||
'//utils/rest:onlab-rest',
|
'//utils/rest:onlab-rest',
|
||||||
'//core/store/serializers:onos-core-serializers',
|
'//core/store/serializers:onos-core-serializers',
|
||||||
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
|
'//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 (
|
osgi_jar_with_tests (
|
||||||
|
|||||||
@ -58,6 +58,16 @@
|
|||||||
<groupId>org.apache.felix</groupId>
|
<groupId>org.apache.felix</groupId>
|
||||||
<artifactId>org.apache.felix.scr.annotations</artifactId>
|
<artifactId>org.apache.felix.scr.annotations</artifactId>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|||||||
@ -15,20 +15,30 @@
|
|||||||
*/
|
*/
|
||||||
package org.onosproject.protocol.restconf.server.restconfmanager;
|
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.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import org.apache.felix.scr.annotations.Activate;
|
import org.apache.felix.scr.annotations.Activate;
|
||||||
import org.apache.felix.scr.annotations.Component;
|
import org.apache.felix.scr.annotations.Component;
|
||||||
import org.apache.felix.scr.annotations.Deactivate;
|
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.apache.felix.scr.annotations.Service;
|
||||||
import org.glassfish.jersey.server.ChunkedOutput;
|
import org.glassfish.jersey.server.ChunkedOutput;
|
||||||
import org.onosproject.event.ListenerTracker;
|
import org.onosproject.event.ListenerTracker;
|
||||||
import org.onosproject.protocol.restconf.server.api.RestconfException;
|
import org.onosproject.protocol.restconf.server.api.RestconfException;
|
||||||
import org.onosproject.protocol.restconf.server.api.RestconfService;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -37,21 +47,37 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
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
|
* Skeletal ONOS RESTCONF Server application. The RESTCONF Manager
|
||||||
* implements the main logic of the RESTCONF Server.
|
* implements the main logic of the RESTCONF Server.
|
||||||
*
|
*
|
||||||
* The design of the RESTCONF subsystem contains 2 major bundles:
|
* The design of the RESTCONF subsystem contains 2 major bundles:
|
||||||
*
|
*
|
||||||
* 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a JAX-RS application.
|
* 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a
|
||||||
* It acts as the frond-end of the the RESTCONF server. It handles
|
* JAX-RS application. It acts as the frond-end of the RESTCONF server.
|
||||||
* HTTP requests that are sent to the RESTCONF Root Path. It then calls the RESTCONF Manager
|
* It intercepts/handles HTTP requests that are sent to the RESTCONF
|
||||||
* to process the requests.
|
* 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.
|
* 2. RESTCONF Manager. This bundle module is the back-end of the server.
|
||||||
* It calls the YMS (YANG Management System) to operate on the YANG data objects.
|
* 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 @@ public class RestconfManager implements RestconfService {
|
|||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
//TODO: YMS service
|
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
||||||
//@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
|
protected YmsService ymsService;
|
||||||
//protected YmsService ymsService;
|
|
||||||
|
|
||||||
private ListenerTracker listeners;
|
private ListenerTracker listeners;
|
||||||
|
|
||||||
private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
|
private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
|
||||||
new ConcurrentHashMap<String, BlockingQueue<ObjectNode>>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private ExecutorService workerThreadPool;
|
private ExecutorService workerThreadPool;
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
protected void activate() {
|
protected void activate() {
|
||||||
workerThreadPool = Executors.newFixedThreadPool(maxNumOfWorkerThreads,
|
workerThreadPool = Executors
|
||||||
new ThreadFactoryBuilder()
|
.newFixedThreadPool(maxNumOfWorkerThreads,
|
||||||
.setNameFormat("restconf-worker")
|
new ThreadFactoryBuilder()
|
||||||
.build());
|
.setNameFormat("restconf-worker")
|
||||||
|
.build());
|
||||||
listeners = new ListenerTracker();
|
listeners = new ListenerTracker();
|
||||||
//TODO: YMS notification
|
//TODO: YMS notification
|
||||||
//listeners.addListener(ymsService, new InternalYangNotificationListener());
|
|
||||||
log.info("Started");
|
log.info("Started");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,34 +129,117 @@ public class RestconfManager implements RestconfService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException {
|
public ObjectNode runGetOperationOnDataResource(String uri)
|
||||||
//TODO: YMS integration
|
throws RestconfException {
|
||||||
return null;
|
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
|
@Override
|
||||||
public void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
|
public void runPostOperationOnDataResource(String uri, ObjectNode rootNode)
|
||||||
//TODO: YMS integration
|
throws RestconfException {
|
||||||
|
YmsOperationExecutionStatus status =
|
||||||
|
invokeYmsOp(uri, rootNode, CREATE);
|
||||||
|
|
||||||
|
if (status != EXECUTION_SUCCESS) {
|
||||||
|
throw new RestconfException("YMS post operation failed.",
|
||||||
|
INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
|
public void runPutOperationOnDataResource(String uri, ObjectNode rootNode)
|
||||||
//TODO: YMS integration
|
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
|
@Override
|
||||||
public void runDeleteOperationOnDataResource(String uri) throws RestconfException {
|
public void runDeleteOperationOnDataResource(String uri)
|
||||||
//TODO: YMS integration
|
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
|
@Override
|
||||||
public String getRestconfRootPath() {
|
public String getRestconfRootPath() {
|
||||||
return this.RESTCONF_ROOT;
|
return RESTCONF_ROOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,16 +251,21 @@ public class RestconfManager implements RestconfService {
|
|||||||
* @throws RestconfException if the worker thread fails to create
|
* @throws RestconfException if the worker thread fails to create
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException {
|
public void subscribeEventStream(String streamId,
|
||||||
BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<ObjectNode>();
|
ChunkedOutput<String> output)
|
||||||
|
throws RestconfException {
|
||||||
|
BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<>();
|
||||||
if (workerThreadPool instanceof ThreadPoolExecutor) {
|
if (workerThreadPool instanceof ThreadPoolExecutor) {
|
||||||
if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >= maxNumOfWorkerThreads) {
|
if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >=
|
||||||
throw new RestconfException("no more work threads left to handle event subscription",
|
maxNumOfWorkerThreads) {
|
||||||
Response.Status.INTERNAL_SERVER_ERROR);
|
throw new RestconfException("no more work threads left to " +
|
||||||
|
"handle event subscription",
|
||||||
|
INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new RestconfException("Server ERROR: workerThreadPool NOT instanceof ThreadPoolExecutor",
|
throw new RestconfException("Server ERROR: workerThreadPool NOT " +
|
||||||
Response.Status.INTERNAL_SERVER_ERROR);
|
"instanceof ThreadPoolExecutor",
|
||||||
|
INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,10 +282,11 @@ public class RestconfManager implements RestconfService {
|
|||||||
pool.shutdown(); // Disable new tasks from being submitted
|
pool.shutdown(); // Disable new tasks from being submitted
|
||||||
try {
|
try {
|
||||||
// Wait a while for existing tasks to terminate
|
// 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
|
pool.shutdownNow(); // Cancel currently executing tasks
|
||||||
// Wait a while for tasks to respond to being cancelled
|
// 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");
|
log.error("Pool did not terminate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +304,8 @@ public class RestconfManager implements RestconfService {
|
|||||||
private final ChunkedOutput<String> output;
|
private final ChunkedOutput<String> output;
|
||||||
private final BlockingQueue<ObjectNode> bqueue;
|
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.queueId = Thread.currentThread().getName();
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.bqueue = q;
|
this.bqueue = q;
|
||||||
@ -212,7 +327,8 @@ public class RestconfManager implements RestconfService {
|
|||||||
*/
|
*/
|
||||||
eventQueueList.remove(this.queueId);
|
eventQueueList.remove(this.queueId);
|
||||||
} catch (InterruptedException e) {
|
} 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);
|
log.debug("EventConsumer Exception:", e);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
@ -226,6 +342,10 @@ public class RestconfManager implements RestconfService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
* 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
|
* queues are created by the event consumer threads and are removed when the
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package org.onosproject.protocol.restconf.server.rpp;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import org.glassfish.jersey.server.ChunkedOutput;
|
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.RestconfException;
|
||||||
import org.onosproject.protocol.restconf.server.api.RestconfService;
|
import org.onosproject.protocol.restconf.server.api.RestconfService;
|
||||||
import org.onosproject.rest.AbstractWebResource;
|
import org.onosproject.rest.AbstractWebResource;
|
||||||
@ -39,8 +40,11 @@ import javax.ws.rs.core.UriInfo;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
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;
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This class is the main implementation of the RESTCONF Protocol
|
* This class is the main implementation of the RESTCONF Protocol
|
||||||
* Proxy module. Currently it only handles some basic operations
|
* Proxy module. Currently it only handles some basic operations
|
||||||
@ -134,7 +138,8 @@ public class RestconfWebResource extends AbstractWebResource {
|
|||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Path("data/{identifier : .+}")
|
@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);
|
log.debug("handlePostRequest: {}", uriString);
|
||||||
|
|
||||||
@ -145,14 +150,14 @@ public class RestconfWebResource extends AbstractWebResource {
|
|||||||
return Response.created(uriInfo.getRequestUri()).build();
|
return Response.created(uriInfo.getRequestUri()).build();
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
log.error("ERROR: handlePostRequest ", e);
|
log.error("ERROR: handlePostRequest ", e);
|
||||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
return Response.status(BAD_REQUEST).build();
|
||||||
} catch (RestconfException e) {
|
} catch (RestconfException e) {
|
||||||
log.error("ERROR: handlePostRequest: {}", e.getMessage());
|
log.error("ERROR: handlePostRequest: {}", e.getMessage());
|
||||||
log.debug("Exception in handlePostRequest:", e);
|
log.debug("Exception in handlePostRequest:", e);
|
||||||
return e.getResponse();
|
return e.getResponse();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
log.error("ERROR: handlePostRequest ", 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 @@ public class RestconfWebResource extends AbstractWebResource {
|
|||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Path("data/{identifier : .+}")
|
@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);
|
log.debug("handlePutRequest: {}", uriString);
|
||||||
|
|
||||||
@ -185,14 +191,14 @@ public class RestconfWebResource extends AbstractWebResource {
|
|||||||
return Response.created(uriInfo.getRequestUri()).build();
|
return Response.created(uriInfo.getRequestUri()).build();
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
log.error("ERROR: handlePutRequest ", e);
|
log.error("ERROR: handlePutRequest ", e);
|
||||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
return Response.status(BAD_REQUEST).build();
|
||||||
} catch (RestconfException e) {
|
} catch (RestconfException e) {
|
||||||
log.error("ERROR: handlePutRequest: {}", e.getMessage());
|
log.error("ERROR: handlePutRequest: {}", e.getMessage());
|
||||||
log.debug("Exception in handlePutRequest:", e);
|
log.debug("Exception in handlePutRequest:", e);
|
||||||
return e.getResponse();
|
return e.getResponse();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
log.error("ERROR: handlePutRequest ", 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 @@ public class RestconfWebResource extends AbstractWebResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
protocols/restconf/server/utils/BUCK
Normal file
8
protocols/restconf/server/utils/BUCK
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
COMPILE_DEPS = [
|
||||||
|
'//lib:CORE_DEPS',
|
||||||
|
'//apps/yms/api:onos-apps-yms-api',
|
||||||
|
]
|
||||||
|
|
||||||
|
osgi_jar_with_tests (
|
||||||
|
deps = COMPILE_DEPS,
|
||||||
|
)
|
||||||
27
protocols/restconf/server/utils/pom.xml
Normal file
27
protocols/restconf/server/utils/pom.xml
Normal file
@ -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>
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
@ -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();
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
@ -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;
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"surname": "Bangalore",
|
||||||
|
"networklist":[
|
||||||
|
"0.79",
|
||||||
|
"1.04",
|
||||||
|
"3.14"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Huawei",
|
||||||
|
"surname": "Bangalore"
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user