chengfan
Committed by Brian O'Connor

[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
Showing 30 changed files with 1680 additions and 78 deletions
...@@ -2,6 +2,7 @@ BUNDLES = [ ...@@ -2,6 +2,7 @@ BUNDLES = [
2 '//protocols/restconf/server/api:onos-protocols-restconf-server-api', 2 '//protocols/restconf/server/api:onos-protocols-restconf-server-api',
3 '//protocols/restconf/server/restconfmgr:onos-protocols-restconf-server-restconfmgr', 3 '//protocols/restconf/server/restconfmgr:onos-protocols-restconf-server-restconfmgr',
4 '//protocols/restconf/server/rpp:onos-protocols-restconf-server-rpp', 4 '//protocols/restconf/server/rpp:onos-protocols-restconf-server-rpp',
5 + '//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
5 ] 6 ]
6 7
7 onos_app ( 8 onos_app (
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.protocol.restconf.server.api;
18 +
19 +import javax.ws.rs.HttpMethod;
20 +import java.lang.annotation.ElementType;
21 +import java.lang.annotation.Retention;
22 +import java.lang.annotation.RetentionPolicy;
23 +import java.lang.annotation.Target;
24 +
25 +/**
26 + * Indicates that the annotated method responds to HTTP PATCH requests.
27 + */
28 +@Target({ElementType.METHOD})
29 +@Retention(RetentionPolicy.RUNTIME)
30 +@HttpMethod("PATCH")
31 +public @interface Patch {
32 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -25,59 +25,82 @@ import org.glassfish.jersey.server.ChunkedOutput; ...@@ -25,59 +25,82 @@ import org.glassfish.jersey.server.ChunkedOutput;
25 public interface RestconfService { 25 public interface RestconfService {
26 /** 26 /**
27 * Processes a GET request against a data resource. The 27 * Processes a GET request against a data resource. The
28 - * target data resource is identified by its URI. 28 + * target data resource is identified by its URI. If the
29 + * GET operation cannot be fulfilled due to reasons such
30 + * as the nonexistence of the target resource, then a
31 + * RestconfException exception is raised. The proper
32 + * HTTP error status code is enclosed in the exception, so
33 + * that the caller may return it to the RESTCONF client to
34 + * display.
29 * 35 *
30 * @param uri URI of the target data resource 36 * @param uri URI of the target data resource
31 * @return JSON representation of the data resource 37 * @return JSON representation of the data resource
32 - * @throws RestconfException if the GET operation cannot be fulfilled due 38 + * @throws RestconfException if the GET operation cannot be fulfilled
33 - * reasons such as the nonexistence of the target
34 - * resource. The proper HTTP error status code is
35 - * enclosed in the exception, so that the caller
36 - * may return it to the RESTCONF client
37 */ 39 */
38 - ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException; 40 + ObjectNode runGetOperationOnDataResource(String uri)
41 + throws RestconfException;
39 42
40 /** 43 /**
41 * Processes a POST request against a data resource. The location of 44 * Processes a POST request against a data resource. The location of
42 * the target resource is passed in as a URI. And the resource's 45 * the target resource is passed in as a URI. And the resource's
43 - * content is passed in as a JSON ObjectNode. 46 + * content is passed in as a JSON ObjectNode. If the POST operation
47 + * cannot be fulfilled due to reasons such as wrong input URIs or
48 + * syntax errors in the JSON payloads, a RestconfException exception
49 + * is raised. The proper HTTP error status code is enclosed in the
50 + * exception.
44 * 51 *
45 * @param uri URI of the data resource to be created 52 * @param uri URI of the data resource to be created
46 * @param rootNode JSON representation of the data resource 53 * @param rootNode JSON representation of the data resource
47 - * @throws RestconfException if the POST operation cannot be fulfilled due 54 + * @throws RestconfException if the POST operation cannot be fulfilled
48 - * reasons such as wrong URI or syntax error
49 - * in JSON payload. The proper HTTP error status
50 - * code is enclosed in the exception
51 */ 55 */
52 - void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException; 56 + void runPostOperationOnDataResource(String uri, ObjectNode rootNode)
57 + throws RestconfException;
53 58
54 /** 59 /**
55 * Processes a PUT request against a data resource. The location of 60 * Processes a PUT request against a data resource. The location of
56 * the target resource is passed in as a URI. And the resource's 61 * the target resource is passed in as a URI. And the resource's
57 - * content is passed in as a JSON ObjectNode. 62 + * content is passed in as a JSON ObjectNode. If the PUT operation
63 + * cannot be fulfilled due to reasons such as wrong input URIs or
64 + * syntax errors in the JSON payloads, a RestconfException exception
65 + * is raised. The proper HTTP error status code is enclosed in the
66 + * exception.
58 * 67 *
59 * @param uri URI of the data resource to be created or updated 68 * @param uri URI of the data resource to be created or updated
60 * @param rootNode JSON representation of the data resource 69 * @param rootNode JSON representation of the data resource
61 - * @throws RestconfException if the PUT operation cannot be fulfilled due 70 + * @throws RestconfException if the PUT operation cannot be fulfilled
62 - * reasons such as wrong URI or syntax error
63 - * in JSON payload. The proper HTTP error status
64 - * code is enclosed in the exception
65 */ 71 */
66 - void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException; 72 + void runPutOperationOnDataResource(String uri, ObjectNode rootNode)
73 + throws RestconfException;
67 74
68 /** 75 /**
69 * Processes the DELETE operation against a data resource. The target 76 * Processes the DELETE operation against a data resource. The target
70 - * data resource is identified by its URI. 77 + * data resource is identified by its URI. If the DELETE operation
78 + * cannot be fulfilled due reasons such as the nonexistence of the
79 + * target resource, a RestconfException exception is raised. The
80 + * proper HTTP error status code is enclosed in the exception.
71 * 81 *
72 * @param uri URI of the data resource to be deleted 82 * @param uri URI of the data resource to be deleted
73 - * @throws RestconfException if the DELETE operation cannot be fulfilled due 83 + * @throws RestconfException if the DELETE operation cannot be fulfilled
74 - * reasons such as the nonexistence of the target
75 - * resource. The proper HTTP error status code is
76 - * enclosed in the exception
77 */ 84 */
78 void runDeleteOperationOnDataResource(String uri) throws RestconfException; 85 void runDeleteOperationOnDataResource(String uri) throws RestconfException;
79 86
80 /** 87 /**
88 + * Processes a PATCH operation on a data resource. The target data
89 + * resource is identified by its URI passed in by the caller.
90 + * And the content of the data resource is passed in as a JSON ObjectNode.
91 + * If the PATCH operation cannot be fulfilled due reasons such as
92 + * the nonexistence of the target resource, a RestconfException
93 + * exception is raised. The proper HTTP error status code is
94 + * enclosed in the exception.
95 + *
96 + * @param uri URI of the data resource to be patched
97 + * @param rootNode JSON representation of the data resource
98 + * @throws RestconfException if the PATCH operation cannot be fulfilled
99 + */
100 + void runPatchOperationOnDataResource(String uri, ObjectNode rootNode)
101 + throws RestconfException;
102 +
103 + /**
81 * Retrieves the RESTCONF Root directory. 104 * Retrieves the RESTCONF Root directory.
82 * 105 *
83 * @return the RESTCONF Root directory 106 * @return the RESTCONF Root directory
...@@ -90,13 +113,18 @@ public interface RestconfService { ...@@ -90,13 +113,18 @@ public interface RestconfService {
90 * which is passed in from the caller. (The worker thread blocks if 113 * which is passed in from the caller. (The worker thread blocks if
91 * no events arrive.) The ChuckedOutput is a pipe to which this 114 * no events arrive.) The ChuckedOutput is a pipe to which this
92 * function acts as the writer and the caller the reader. 115 * function acts as the writer and the caller the reader.
116 + * <p>
117 + * If the Event Stream cannot be subscribed due to reasons such as
118 + * the nonexistence of the target stream or failure to allocate
119 + * worker thread to handle the request, a RestconfException exception
120 + * is raised. The proper HTTP error status code is enclosed in the
121 + * exception, so that the caller may return it to the RESTCONF client
122 + * to display.
93 * 123 *
94 * @param streamId ID of the RESTCONF stream to subscribe 124 * @param streamId ID of the RESTCONF stream to subscribe
95 * @param output A string data stream 125 * @param output A string data stream
96 - * @throws RestconfException if the Event Stream cannot be subscribed due to 126 + * @throws RestconfException if the Event Stream cannot be subscribed
97 - * reasons such as the nonexistence of the target
98 - * stream or unable to allocate any free worker
99 - * thread to handle the request
100 */ 127 */
101 - void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException; 128 + void subscribeEventStream(String streamId, ChunkedOutput<String> output)
129 + throws RestconfException;
102 } 130 }
......
...@@ -22,4 +22,5 @@ ...@@ -22,4 +22,5 @@
22 <artifact>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</artifact> 22 <artifact>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</artifact>
23 <artifact>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</artifact> 23 <artifact>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</artifact>
24 <artifact>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</artifact> 24 <artifact>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</artifact>
25 + <artifact>mvn:${project.groupId}/onos-restconf-server-utils/${project.version}</artifact>
25 </app> 26 </app>
......
...@@ -21,5 +21,6 @@ ...@@ -21,5 +21,6 @@
21 <bundle>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</bundle> 21 <bundle>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</bundle>
22 <bundle>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</bundle> 22 <bundle>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</bundle>
23 <bundle>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</bundle> 23 <bundle>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</bundle>
24 + <bundle>mvn:${project.groupId}/onos-restconf-server-utils/${project.version}</bundle>
24 </feature> 25 </feature>
25 </features> 26 </features>
......
...@@ -51,5 +51,10 @@ ...@@ -51,5 +51,10 @@
51 <artifactId>onos-restconf-server-rpp</artifactId> 51 <artifactId>onos-restconf-server-rpp</artifactId>
52 <version>${project.version}</version> 52 <version>${project.version}</version>
53 </dependency> 53 </dependency>
54 + <dependency>
55 + <groupId>org.onosproject</groupId>
56 + <artifactId>onos-restconf-server-utils</artifactId>
57 + <version>${project.version}</version>
58 + </dependency>
54 </dependencies> 59 </dependencies>
55 </project> 60 </project>
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
34 <module>restconfmgr</module> 34 <module>restconfmgr</module>
35 <module>rpp</module> 35 <module>rpp</module>
36 <module>app</module> 36 <module>app</module>
37 + <module>utils</module>
37 </modules> 38 </modules>
38 39
39 <description>RESTCONF Server Module</description> 40 <description>RESTCONF Server Module</description>
......
...@@ -6,6 +6,8 @@ COMPILE_DEPS = [ ...@@ -6,6 +6,8 @@ COMPILE_DEPS = [
6 '//utils/rest:onlab-rest', 6 '//utils/rest:onlab-rest',
7 '//core/store/serializers:onos-core-serializers', 7 '//core/store/serializers:onos-core-serializers',
8 '//protocols/restconf/server/api:onos-protocols-restconf-server-api', 8 '//protocols/restconf/server/api:onos-protocols-restconf-server-api',
9 + '//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
10 + '//apps/yms/api:onos-apps-yms-api',
9 ] 11 ]
10 12
11 osgi_jar_with_tests ( 13 osgi_jar_with_tests (
......
...@@ -58,6 +58,16 @@ ...@@ -58,6 +58,16 @@
58 <groupId>org.apache.felix</groupId> 58 <groupId>org.apache.felix</groupId>
59 <artifactId>org.apache.felix.scr.annotations</artifactId> 59 <artifactId>org.apache.felix.scr.annotations</artifactId>
60 </dependency> 60 </dependency>
61 + <dependency>
62 + <groupId>org.onosproject</groupId>
63 + <artifactId>onos-restconf-server-utils</artifactId>
64 + <version>${project.version}</version>
65 + </dependency>
66 + <dependency>
67 + <groupId>org.onosproject</groupId>
68 + <artifactId>onos-app-yms-api</artifactId>
69 + <version>${project.version}</version>
70 + </dependency>
61 </dependencies> 71 </dependencies>
62 <build> 72 <build>
63 <plugins> 73 <plugins>
......
...@@ -15,20 +15,30 @@ ...@@ -15,20 +15,30 @@
15 */ 15 */
16 package org.onosproject.protocol.restconf.server.restconfmanager; 16 package org.onosproject.protocol.restconf.server.restconfmanager;
17 17
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.node.ArrayNode;
18 import com.fasterxml.jackson.databind.node.ObjectNode; 20 import com.fasterxml.jackson.databind.node.ObjectNode;
19 import com.google.common.util.concurrent.ThreadFactoryBuilder; 21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
20 import org.apache.felix.scr.annotations.Activate; 22 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component; 23 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate; 24 import org.apache.felix.scr.annotations.Deactivate;
25 +import org.apache.felix.scr.annotations.Reference;
26 +import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.apache.felix.scr.annotations.Service; 27 import org.apache.felix.scr.annotations.Service;
24 import org.glassfish.jersey.server.ChunkedOutput; 28 import org.glassfish.jersey.server.ChunkedOutput;
25 import org.onosproject.event.ListenerTracker; 29 import org.onosproject.event.ListenerTracker;
26 import org.onosproject.protocol.restconf.server.api.RestconfException; 30 import org.onosproject.protocol.restconf.server.api.RestconfException;
27 import org.onosproject.protocol.restconf.server.api.RestconfService; 31 import org.onosproject.protocol.restconf.server.api.RestconfService;
32 +import org.onosproject.yms.ydt.YdtBuilder;
33 +import org.onosproject.yms.ydt.YdtContext;
34 +import org.onosproject.yms.ydt.YdtContextOperationType;
35 +import org.onosproject.yms.ydt.YdtResponse;
36 +import org.onosproject.yms.ydt.YmsOperationExecutionStatus;
37 +import org.onosproject.yms.ydt.YmsOperationType;
38 +import org.onosproject.yms.ymsm.YmsService;
28 import org.slf4j.Logger; 39 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory; 40 import org.slf4j.LoggerFactory;
30 41
31 -import javax.ws.rs.core.Response;
32 import java.io.IOException; 42 import java.io.IOException;
33 import java.util.concurrent.BlockingQueue; 43 import java.util.concurrent.BlockingQueue;
34 import java.util.concurrent.ConcurrentHashMap; 44 import java.util.concurrent.ConcurrentHashMap;
...@@ -37,21 +47,37 @@ import java.util.concurrent.ExecutorService; ...@@ -37,21 +47,37 @@ import java.util.concurrent.ExecutorService;
37 import java.util.concurrent.Executors; 47 import java.util.concurrent.Executors;
38 import java.util.concurrent.LinkedBlockingQueue; 48 import java.util.concurrent.LinkedBlockingQueue;
39 import java.util.concurrent.ThreadPoolExecutor; 49 import java.util.concurrent.ThreadPoolExecutor;
40 -import java.util.concurrent.TimeUnit;
41 50
51 +
52 +import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REQUEST;
53 +import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
54 +import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
55 +import static org.onosproject.yms.ydt.YdtContextOperationType.CREATE;
56 +import static org.onosproject.yms.ydt.YdtContextOperationType.DELETE;
57 +import static org.onosproject.yms.ydt.YdtContextOperationType.REPLACE;
58 +import static org.onosproject.yms.ydt.YdtContextOperationType.MERGE;
59 +import static org.onosproject.yms.ydt.YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE;
60 +import static org.onosproject.yms.ydt.YmsOperationExecutionStatus.EXECUTION_SUCCESS;
61 +import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertYdtToJson;
62 +import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertUriToYdt;
63 +import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertJsonToYdt;
64 +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
65 +import static java.util.concurrent.TimeUnit.SECONDS;
42 /* 66 /*
43 * Skeletal ONOS RESTCONF Server application. The RESTCONF Manager 67 * Skeletal ONOS RESTCONF Server application. The RESTCONF Manager
44 * implements the main logic of the RESTCONF Server. 68 * implements the main logic of the RESTCONF Server.
45 * 69 *
46 * The design of the RESTCONF subsystem contains 2 major bundles: 70 * The design of the RESTCONF subsystem contains 2 major bundles:
47 * 71 *
48 - * 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a JAX-RS application. 72 + * 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a
49 - * It acts as the frond-end of the the RESTCONF server. It handles 73 + * JAX-RS application. It acts as the frond-end of the RESTCONF server.
50 - * HTTP requests that are sent to the RESTCONF Root Path. It then calls the RESTCONF Manager 74 + * It intercepts/handles HTTP requests that are sent to the RESTCONF
51 - * to process the requests. 75 + * Root Path. It then calls the RESTCONF Manager to process the requests.
52 * 76 *
53 - * 2. RESTCONF Manager. This is the back-end. It provides the main logic of the RESTCONF server. 77 + * 2. RESTCONF Manager. This bundle module is the back-end of the server.
54 - * It calls the YMS (YANG Management System) to operate on the YANG data objects. 78 + * It provides the main logic of the RESTCONF server. It interacts with
79 + * the YMS (YANG Management System) to run operations on the YANG data
80 + * objects (i.e., data resources).
55 */ 81 */
56 82
57 /** 83 /**
...@@ -73,26 +99,25 @@ public class RestconfManager implements RestconfService { ...@@ -73,26 +99,25 @@ public class RestconfManager implements RestconfService {
73 99
74 private final Logger log = LoggerFactory.getLogger(getClass()); 100 private final Logger log = LoggerFactory.getLogger(getClass());
75 101
76 - //TODO: YMS service 102 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 - //@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 103 + protected YmsService ymsService;
78 - //protected YmsService ymsService;
79 104
80 private ListenerTracker listeners; 105 private ListenerTracker listeners;
81 106
82 private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList = 107 private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
83 - new ConcurrentHashMap<String, BlockingQueue<ObjectNode>>(); 108 + new ConcurrentHashMap<>();
84 109
85 private ExecutorService workerThreadPool; 110 private ExecutorService workerThreadPool;
86 111
87 @Activate 112 @Activate
88 protected void activate() { 113 protected void activate() {
89 - workerThreadPool = Executors.newFixedThreadPool(maxNumOfWorkerThreads, 114 + workerThreadPool = Executors
90 - new ThreadFactoryBuilder() 115 + .newFixedThreadPool(maxNumOfWorkerThreads,
91 - .setNameFormat("restconf-worker") 116 + new ThreadFactoryBuilder()
92 - .build()); 117 + .setNameFormat("restconf-worker")
118 + .build());
93 listeners = new ListenerTracker(); 119 listeners = new ListenerTracker();
94 //TODO: YMS notification 120 //TODO: YMS notification
95 - //listeners.addListener(ymsService, new InternalYangNotificationListener());
96 log.info("Started"); 121 log.info("Started");
97 } 122 }
98 123
...@@ -104,34 +129,117 @@ public class RestconfManager implements RestconfService { ...@@ -104,34 +129,117 @@ public class RestconfManager implements RestconfService {
104 } 129 }
105 130
106 @Override 131 @Override
107 - public ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException { 132 + public ObjectNode runGetOperationOnDataResource(String uri)
108 - //TODO: YMS integration 133 + throws RestconfException {
109 - return null; 134 + YdtBuilder ydtBuilder = getYdtBuilder(QUERY_REQUEST);
135 + //Convert the URI to ydtBuilder
136 + convertUriToYdt(uri, ydtBuilder, NONE);
137 + YdtResponse ydtResponse = ymsService.executeOperation(ydtBuilder);
138 + YmsOperationExecutionStatus status = ydtResponse
139 + .getYmsOperationResult();
140 + if (status != EXECUTION_SUCCESS) {
141 + throw new RestconfException("YMS GET operation failed",
142 + INTERNAL_SERVER_ERROR);
143 + }
144 +
145 + YdtContext rootNode = ydtResponse.getRootNode();
146 + YdtContext curNode = ydtBuilder.getCurNode();
147 +
148 + ObjectNode result = convertYdtToJson(curNode.getName(), rootNode,
149 + ymsService.getYdtWalker());
150 + //if the query URI contain a key, something like list=key
151 + //here should only get get child with the specific key
152 + YdtContext child = curNode.getFirstChild();
153 + if (child != null &&
154 + child.getYdtType() == SINGLE_INSTANCE_LEAF_VALUE_NODE) {
155 +
156 + ArrayNode jsonNode = (ArrayNode) result.get(curNode.getName());
157 + for (JsonNode next : jsonNode) {
158 + if (next.findValue(child.getName())
159 + .asText().equals(child.getValue())) {
160 + return (ObjectNode) next;
161 + }
162 + }
163 + throw new RestconfException(String.format("No content for %s = %s",
164 + child.getName(),
165 + child.getValue()),
166 + INTERNAL_SERVER_ERROR);
167 + }
168 + return result;
169 + }
170 +
171 + private YmsOperationExecutionStatus
172 + invokeYmsOp(String uri, ObjectNode rootNode,
173 + YdtContextOperationType opType) {
174 + YdtBuilder ydtBuilder = getYdtBuilder(EDIT_CONFIG_REQUEST);
175 + //Convert the URI to ydtBuilder
176 + convertUriToYdt(uri, ydtBuilder, opType);
177 +
178 + //set default operation type for the payload node
179 + ydtBuilder.setDefaultEditOperationType(opType);
180 + //convert the payload json body to ydt
181 + convertJsonToYdt(rootNode, ydtBuilder);
182 +
183 + return ymsService
184 + .executeOperation(ydtBuilder)
185 + .getYmsOperationResult();
110 } 186 }
111 187
112 @Override 188 @Override
113 - public void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException { 189 + public void runPostOperationOnDataResource(String uri, ObjectNode rootNode)
114 - //TODO: YMS integration 190 + throws RestconfException {
191 + YmsOperationExecutionStatus status =
192 + invokeYmsOp(uri, rootNode, CREATE);
193 +
194 + if (status != EXECUTION_SUCCESS) {
195 + throw new RestconfException("YMS post operation failed.",
196 + INTERNAL_SERVER_ERROR);
197 + }
115 } 198 }
116 199
117 @Override 200 @Override
118 - public void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException { 201 + public void runPutOperationOnDataResource(String uri, ObjectNode rootNode)
119 - //TODO: YMS integration 202 + throws RestconfException {
203 + YmsOperationExecutionStatus status =
204 + invokeYmsOp(uri, rootNode, REPLACE);
205 +
206 + if (status != EXECUTION_SUCCESS) {
207 + throw new RestconfException("YMS put operation failed.",
208 + INTERNAL_SERVER_ERROR);
209 + }
210 + }
211 +
212 + @Override
213 + public void runDeleteOperationOnDataResource(String uri)
214 + throws RestconfException {
215 + //Get a root ydtBuilder
216 + YdtBuilder ydtBuilder = getYdtBuilder(EDIT_CONFIG_REQUEST);
217 + //Convert the URI to ydtBuilder
218 + convertUriToYdt(uri, ydtBuilder, DELETE);
219 + //Execute the delete operation
220 + YmsOperationExecutionStatus status = ymsService
221 + .executeOperation(ydtBuilder)
222 + .getYmsOperationResult();
223 + if (status != EXECUTION_SUCCESS) {
224 + throw new RestconfException("YMS delete operation failed.",
225 + INTERNAL_SERVER_ERROR);
226 + }
120 } 227 }
121 228
122 - /**
123 - * Process the delete operation on a data resource.
124 - *
125 - * @param uri URI of the data resource to be deleted.
126 - */
127 @Override 229 @Override
128 - public void runDeleteOperationOnDataResource(String uri) throws RestconfException { 230 + public void runPatchOperationOnDataResource(String uri, ObjectNode rootNode)
129 - //TODO: YMS integration 231 + throws RestconfException {
232 + YmsOperationExecutionStatus status = invokeYmsOp(uri, rootNode, MERGE);
233 +
234 + if (status != EXECUTION_SUCCESS) {
235 + throw new RestconfException("YMS patch operation failed.",
236 + INTERNAL_SERVER_ERROR);
237 + }
130 } 238 }
131 239
132 @Override 240 @Override
133 public String getRestconfRootPath() { 241 public String getRestconfRootPath() {
134 - return this.RESTCONF_ROOT; 242 + return RESTCONF_ROOT;
135 } 243 }
136 244
137 /** 245 /**
...@@ -143,16 +251,21 @@ public class RestconfManager implements RestconfService { ...@@ -143,16 +251,21 @@ public class RestconfManager implements RestconfService {
143 * @throws RestconfException if the worker thread fails to create 251 * @throws RestconfException if the worker thread fails to create
144 */ 252 */
145 @Override 253 @Override
146 - public void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException { 254 + public void subscribeEventStream(String streamId,
147 - BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<ObjectNode>(); 255 + ChunkedOutput<String> output)
256 + throws RestconfException {
257 + BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<>();
148 if (workerThreadPool instanceof ThreadPoolExecutor) { 258 if (workerThreadPool instanceof ThreadPoolExecutor) {
149 - if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >= maxNumOfWorkerThreads) { 259 + if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >=
150 - throw new RestconfException("no more work threads left to handle event subscription", 260 + maxNumOfWorkerThreads) {
151 - Response.Status.INTERNAL_SERVER_ERROR); 261 + throw new RestconfException("no more work threads left to " +
262 + "handle event subscription",
263 + INTERNAL_SERVER_ERROR);
152 } 264 }
153 } else { 265 } else {
154 - throw new RestconfException("Server ERROR: workerThreadPool NOT instanceof ThreadPoolExecutor", 266 + throw new RestconfException("Server ERROR: workerThreadPool NOT " +
155 - Response.Status.INTERNAL_SERVER_ERROR); 267 + "instanceof ThreadPoolExecutor",
268 + INTERNAL_SERVER_ERROR);
156 269
157 } 270 }
158 271
...@@ -169,10 +282,11 @@ public class RestconfManager implements RestconfService { ...@@ -169,10 +282,11 @@ public class RestconfManager implements RestconfService {
169 pool.shutdown(); // Disable new tasks from being submitted 282 pool.shutdown(); // Disable new tasks from being submitted
170 try { 283 try {
171 // Wait a while for existing tasks to terminate 284 // Wait a while for existing tasks to terminate
172 - if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) { 285 + if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, SECONDS)) {
173 pool.shutdownNow(); // Cancel currently executing tasks 286 pool.shutdownNow(); // Cancel currently executing tasks
174 // Wait a while for tasks to respond to being cancelled 287 // Wait a while for tasks to respond to being cancelled
175 - if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) { 288 + if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT,
289 + SECONDS)) {
176 log.error("Pool did not terminate"); 290 log.error("Pool did not terminate");
177 } 291 }
178 } 292 }
...@@ -190,7 +304,8 @@ public class RestconfManager implements RestconfService { ...@@ -190,7 +304,8 @@ public class RestconfManager implements RestconfService {
190 private final ChunkedOutput<String> output; 304 private final ChunkedOutput<String> output;
191 private final BlockingQueue<ObjectNode> bqueue; 305 private final BlockingQueue<ObjectNode> bqueue;
192 306
193 - public EventConsumer(ChunkedOutput<String> output, BlockingQueue<ObjectNode> q) { 307 + public EventConsumer(ChunkedOutput<String> output,
308 + BlockingQueue<ObjectNode> q) {
194 this.queueId = Thread.currentThread().getName(); 309 this.queueId = Thread.currentThread().getName();
195 this.output = output; 310 this.output = output;
196 this.bqueue = q; 311 this.bqueue = q;
...@@ -212,7 +327,8 @@ public class RestconfManager implements RestconfService { ...@@ -212,7 +327,8 @@ public class RestconfManager implements RestconfService {
212 */ 327 */
213 eventQueueList.remove(this.queueId); 328 eventQueueList.remove(this.queueId);
214 } catch (InterruptedException e) { 329 } catch (InterruptedException e) {
215 - log.error("ERROR: EventConsumer: bqueue.take() has been interrupted."); 330 + log.error("ERROR: EventConsumer: bqueue.take() " +
331 + "has been interrupted.");
216 log.debug("EventConsumer Exception:", e); 332 log.debug("EventConsumer Exception:", e);
217 } finally { 333 } finally {
218 try { 334 try {
...@@ -226,6 +342,10 @@ public class RestconfManager implements RestconfService { ...@@ -226,6 +342,10 @@ public class RestconfManager implements RestconfService {
226 342
227 } 343 }
228 344
345 + private YdtBuilder getYdtBuilder(YmsOperationType ymsOperationType) {
346 + return ymsService.getYdtBuilder(RESTCONF_ROOT, null, ymsOperationType);
347 + }
348 +
229 /** 349 /**
230 * The listener class acts as the event producer for the event queues. The 350 * The listener class acts as the event producer for the event queues. The
231 * queues are created by the event consumer threads and are removed when the 351 * queues are created by the event consumer threads and are removed when the
......
...@@ -19,6 +19,7 @@ package org.onosproject.protocol.restconf.server.rpp; ...@@ -19,6 +19,7 @@ package org.onosproject.protocol.restconf.server.rpp;
19 import com.fasterxml.jackson.core.JsonProcessingException; 19 import com.fasterxml.jackson.core.JsonProcessingException;
20 import com.fasterxml.jackson.databind.node.ObjectNode; 20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 import org.glassfish.jersey.server.ChunkedOutput; 21 import org.glassfish.jersey.server.ChunkedOutput;
22 +import org.onosproject.protocol.restconf.server.api.Patch;
22 import org.onosproject.protocol.restconf.server.api.RestconfException; 23 import org.onosproject.protocol.restconf.server.api.RestconfException;
23 import org.onosproject.protocol.restconf.server.api.RestconfService; 24 import org.onosproject.protocol.restconf.server.api.RestconfService;
24 import org.onosproject.rest.AbstractWebResource; 25 import org.onosproject.rest.AbstractWebResource;
...@@ -39,8 +40,11 @@ import javax.ws.rs.core.UriInfo; ...@@ -39,8 +40,11 @@ import javax.ws.rs.core.UriInfo;
39 import java.io.IOException; 40 import java.io.IOException;
40 import java.io.InputStream; 41 import java.io.InputStream;
41 42
43 +import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
44 +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
42 import static org.slf4j.LoggerFactory.getLogger; 45 import static org.slf4j.LoggerFactory.getLogger;
43 46
47 +
44 /* 48 /*
45 * This class is the main implementation of the RESTCONF Protocol 49 * This class is the main implementation of the RESTCONF Protocol
46 * Proxy module. Currently it only handles some basic operations 50 * Proxy module. Currently it only handles some basic operations
...@@ -134,7 +138,8 @@ public class RestconfWebResource extends AbstractWebResource { ...@@ -134,7 +138,8 @@ public class RestconfWebResource extends AbstractWebResource {
134 @Consumes(MediaType.APPLICATION_JSON) 138 @Consumes(MediaType.APPLICATION_JSON)
135 @Produces(MediaType.APPLICATION_JSON) 139 @Produces(MediaType.APPLICATION_JSON)
136 @Path("data/{identifier : .+}") 140 @Path("data/{identifier : .+}")
137 - public Response handlePostRequest(@PathParam("identifier") String uriString, InputStream stream) { 141 + public Response handlePostRequest(@PathParam("identifier") String uriString,
142 + InputStream stream) {
138 143
139 log.debug("handlePostRequest: {}", uriString); 144 log.debug("handlePostRequest: {}", uriString);
140 145
...@@ -145,14 +150,14 @@ public class RestconfWebResource extends AbstractWebResource { ...@@ -145,14 +150,14 @@ public class RestconfWebResource extends AbstractWebResource {
145 return Response.created(uriInfo.getRequestUri()).build(); 150 return Response.created(uriInfo.getRequestUri()).build();
146 } catch (JsonProcessingException e) { 151 } catch (JsonProcessingException e) {
147 log.error("ERROR: handlePostRequest ", e); 152 log.error("ERROR: handlePostRequest ", e);
148 - return Response.status(Response.Status.BAD_REQUEST).build(); 153 + return Response.status(BAD_REQUEST).build();
149 } catch (RestconfException e) { 154 } catch (RestconfException e) {
150 log.error("ERROR: handlePostRequest: {}", e.getMessage()); 155 log.error("ERROR: handlePostRequest: {}", e.getMessage());
151 log.debug("Exception in handlePostRequest:", e); 156 log.debug("Exception in handlePostRequest:", e);
152 return e.getResponse(); 157 return e.getResponse();
153 } catch (IOException ex) { 158 } catch (IOException ex) {
154 log.error("ERROR: handlePostRequest ", ex); 159 log.error("ERROR: handlePostRequest ", ex);
155 - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 160 + return Response.status(INTERNAL_SERVER_ERROR).build();
156 } 161 }
157 } 162 }
158 163
...@@ -174,7 +179,8 @@ public class RestconfWebResource extends AbstractWebResource { ...@@ -174,7 +179,8 @@ public class RestconfWebResource extends AbstractWebResource {
174 @Consumes(MediaType.APPLICATION_JSON) 179 @Consumes(MediaType.APPLICATION_JSON)
175 @Produces(MediaType.APPLICATION_JSON) 180 @Produces(MediaType.APPLICATION_JSON)
176 @Path("data/{identifier : .+}") 181 @Path("data/{identifier : .+}")
177 - public Response handlePutRequest(@PathParam("identifier") String uriString, InputStream stream) { 182 + public Response handlePutRequest(@PathParam("identifier") String uriString,
183 + InputStream stream) {
178 184
179 log.debug("handlePutRequest: {}", uriString); 185 log.debug("handlePutRequest: {}", uriString);
180 186
...@@ -185,14 +191,14 @@ public class RestconfWebResource extends AbstractWebResource { ...@@ -185,14 +191,14 @@ public class RestconfWebResource extends AbstractWebResource {
185 return Response.created(uriInfo.getRequestUri()).build(); 191 return Response.created(uriInfo.getRequestUri()).build();
186 } catch (JsonProcessingException e) { 192 } catch (JsonProcessingException e) {
187 log.error("ERROR: handlePutRequest ", e); 193 log.error("ERROR: handlePutRequest ", e);
188 - return Response.status(Response.Status.BAD_REQUEST).build(); 194 + return Response.status(BAD_REQUEST).build();
189 } catch (RestconfException e) { 195 } catch (RestconfException e) {
190 log.error("ERROR: handlePutRequest: {}", e.getMessage()); 196 log.error("ERROR: handlePutRequest: {}", e.getMessage());
191 log.debug("Exception in handlePutRequest:", e); 197 log.debug("Exception in handlePutRequest:", e);
192 return e.getResponse(); 198 return e.getResponse();
193 } catch (IOException ex) { 199 } catch (IOException ex) {
194 log.error("ERROR: handlePutRequest ", ex); 200 log.error("ERROR: handlePutRequest ", ex);
195 - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 201 + return Response.status(INTERNAL_SERVER_ERROR).build();
196 } 202 }
197 } 203 }
198 204
...@@ -223,4 +229,41 @@ public class RestconfWebResource extends AbstractWebResource { ...@@ -223,4 +229,41 @@ public class RestconfWebResource extends AbstractWebResource {
223 } 229 }
224 } 230 }
225 231
232 + /**
233 + * Handles a RESTCONF PATCH operation against a data resource.
234 + * If the PATCH request succeeds, a "200 OK" status-line is returned if
235 + * there is a message-body, and "204 No Content" is returned if no
236 + * response message-body is sent.
237 + *
238 + * @param uriString URI of the data resource
239 + * @param stream Input JSON object
240 + * @return HTTP response
241 + */
242 + @Patch
243 + @Consumes(MediaType.APPLICATION_JSON)
244 + @Produces(MediaType.APPLICATION_JSON)
245 + @Path("data/{identifier : .+}")
246 + public Response handlePatchRequest(@PathParam("identifier") String uriString,
247 + InputStream stream) {
248 +
249 + log.debug("handlePatchRequest: {}", uriString);
250 +
251 + try {
252 + ObjectNode rootNode = (ObjectNode) mapper().readTree(stream);
253 +
254 + service.runPatchOperationOnDataResource(uriString, rootNode);
255 + return Response.ok().build();
256 + } catch (JsonProcessingException e) {
257 + log.error("ERROR: handlePatchRequest ", e);
258 + return Response.status(BAD_REQUEST).build();
259 + } catch (RestconfException e) {
260 + log.error("ERROR: handlePatchRequest: {}", e.getMessage());
261 + log.debug("Exception in handlePatchRequest:", e);
262 + return e.getResponse();
263 + } catch (IOException ex) {
264 + log.error("ERROR: handlePatchRequest ", ex);
265 + return Response.status(INTERNAL_SERVER_ERROR).build();
266 + }
267 + }
268 +
226 } 269 }
......
1 +COMPILE_DEPS = [
2 + '//lib:CORE_DEPS',
3 + '//apps/yms/api:onos-apps-yms-api',
4 +]
5 +
6 +osgi_jar_with_tests (
7 + deps = COMPILE_DEPS,
8 +)
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 + <parent>
6 + <artifactId>onos-restconf-server</artifactId>
7 + <groupId>org.onosproject</groupId>
8 + <version>1.8.0-SNAPSHOT</version>
9 + </parent>
10 + <modelVersion>4.0.0</modelVersion>
11 +
12 + <artifactId>onos-restconf-server-utils</artifactId>
13 + <packaging>bundle</packaging>
14 + <dependencies>
15 + <dependency>
16 + <groupId>org.onosproject</groupId>
17 + <artifactId>onos-app-yms-api</artifactId>
18 + <version>${project.version}</version>
19 + </dependency>
20 + <dependency>
21 + <groupId>org.easymock</groupId>
22 + <artifactId>easymock</artifactId>
23 + <scope>test</scope>
24 + </dependency>
25 + </dependencies>
26 +
27 +</project>
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.protocol.restconf.server.utils.exceptions;
17 +
18 +/**
19 + * Represents class of errors related to Json parse utils.
20 + */
21 +public class JsonParseException extends RuntimeException {
22 +
23 + /**
24 + * Constructs an exception with the specified message.
25 + *
26 + * @param message the message describing the specific nature of the error
27 + */
28 + public JsonParseException(String message) {
29 + super(message);
30 + }
31 +
32 + /**
33 + * Constructs an exception with the specified message and the underlying
34 + * cause.
35 + *
36 + * @param message the message describing the specific nature of the error
37 + * @param cause the underlying cause of this error
38 + */
39 + public JsonParseException(String message, Throwable cause) {
40 + super(message, cause);
41 + }
42 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.protocol.restconf.server.utils.exceptions;
18 +
19 +/**
20 + * Represents class of errors related to YDT parse utils.
21 + */
22 +public class YdtParseException extends RuntimeException {
23 + /**
24 + * Constructs an exception with the specified message.
25 + *
26 + * @param message the message describing the specific nature of the error
27 + */
28 + public YdtParseException(String message) {
29 + super(message);
30 + }
31 +
32 + /**
33 + * Constructs an exception with the specified message and the underlying
34 + * cause.
35 + *
36 + * @param message the message describing the specific nature of the error
37 + * @param cause the underlying cause of this error
38 + */
39 + public YdtParseException(String message, Throwable cause) {
40 + super(message, cause);
41 + }
42 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Parse utils custom exceptions.
19 + */
20 +package org.onosproject.protocol.restconf.server.utils.exceptions;
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.protocol.restconf.server.utils.parser.api;
17 +
18 +import com.fasterxml.jackson.databind.node.JsonNodeType;
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +
21 +import java.util.Set;
22 +
23 +/**
24 + * Abstraction of an entity which provides interfaces to build and obtain JSON
25 + * data tree.
26 + */
27 +public interface JsonBuilder {
28 +
29 + /**
30 + * Adds a to half (a left brace/bracket and the field name) of a JSON
31 + * object/array to the JSON tree. This method is used by protocols which
32 + * knows the nature (object/array) of node.
33 + *
34 + * @param fieldName name of child to be added
35 + * @param nodeType the type of the child
36 + */
37 + void addNodeTopHalf(String fieldName, JsonNodeType nodeType);
38 +
39 + /**
40 + * Adds a child with value and a comma to the JSON tree.
41 + * Protocols unaware of nature of node (single/multiple) will use it to add
42 + * both single instance and multi instance node. Protocols aware of nature
43 + * of node will use it for single instance value node addition.
44 + *
45 + * @param fieldName name of child to be added
46 + * @param value the type of the child
47 + */
48 + void addNodeWithValueTopHalf(String fieldName, String value);
49 +
50 + /**
51 + * Adds a child with list of values to JSON data tree. This method is
52 + * used by protocols which knows the nature (object/array) of node for
53 + * ArrayNode addition.
54 + *
55 + * @param fieldName name of child to be added
56 + * @param sets the value list of the child
57 + */
58 + void addNodeWithSetTopHalf(String fieldName, Set<String> sets);
59 +
60 + /**
61 + * Adds the bottom half(a right brace/bracket) of a JSON object/array to
62 + * the JSON tree. for the text, a comma should be taken out.
63 + *
64 + * @param nodeType the type of the child
65 + */
66 + void addNodeBottomHalf(JsonNodeType nodeType);
67 +
68 + /**
69 + * Returns the JSON tree after build operations in the format of string.
70 + *
71 + * @return the final string JSON tree after build operations
72 + */
73 + String getTreeString();
74 +
75 + /**
76 + * Returns the JSON tree after build operations in the format of ObjectNode.
77 + *
78 + * @return the final ObjectNode JSON tree after build operations
79 + */
80 + ObjectNode getTreeNode();
81 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.protocol.restconf.server.utils.parser.api;
17 +
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +
21 +/**
22 + * Abstraction of an entity which provide call back methods which are called
23 + * by JSON walker while walking the JSON data tree. This interface needs to be
24 + * implemented by protocol implementing listener's based call backs while JSON
25 + * walk.
26 + */
27 +public interface JsonListener {
28 +
29 + /**
30 + * Callback invoked during a node entry.
31 + * All the related information about the node can be obtain from the JSON
32 + * object.
33 + *
34 + * @param fieldName the field name of the JSON Node value
35 + * @param node the JsonNode which is walked through
36 + */
37 + void enterJsonNode(String fieldName, JsonNode node);
38 +
39 + /**
40 + * Callback invoked during a node exit.
41 + * All the related information about the node can be obtain from the JSON
42 + * node.
43 + *
44 + * @param jsonNode JSON node which has been walked through
45 + */
46 + void exitJsonNode(JsonNode jsonNode);
47 +
48 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.protocol.restconf.server.utils.parser.api;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +
20 +/**
21 + * Abstraction of an entity which provides interfaces for Json walk.
22 + * This interface serve as common tools for anyone who needs to parse
23 + * the json node with depth-first algorithm.
24 + */
25 +public interface JsonWalker {
26 +
27 + /**
28 + * Walks the JSON data tree. Protocols implements JSON listener service
29 + * and walks JSON tree with input as implemented object. JSON walker
30 + * provides call backs to implemented methods. For the original json
31 + * node(come from NB), there is a field name which is something like the
32 + * module name of a YANG model. If not, the fieldName can be null.
33 + *
34 + * @param jsonListener Json listener implemented by the user
35 + * @param fieldName the original object node field
36 + * @param node the json node which needs to be walk
37 + */
38 + void walk(JsonListener jsonListener, String fieldName, ObjectNode node);
39 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Provider json related process interface.
19 + */
20 +package org.onosproject.protocol.restconf.server.utils.parser.api;
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.protocol.restconf.server.utils.parser.json;
18 +
19 +import com.fasterxml.jackson.databind.ObjectMapper;
20 +import com.fasterxml.jackson.databind.node.JsonNodeType;
21 +import com.fasterxml.jackson.databind.node.ObjectNode;
22 +import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
23 +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
24 +import org.slf4j.Logger;
25 +import org.slf4j.LoggerFactory;
26 +
27 +import java.io.IOException;
28 +import java.util.Set;
29 +
30 +import static com.google.common.base.Preconditions.checkNotNull;
31 +import static com.google.common.base.Strings.isNullOrEmpty;
32 +
33 +/**
34 + * Represents implementation of interfaces to build and obtain JSON data tree.
35 + */
36 +public class DefaultJsonBuilder implements JsonBuilder {
37 +
38 + private static final String LEFT_BRACE = "{";
39 + private static final String RIGHT_BRACE = "}";
40 + private static final String LEFT_BRACKET = "[";
41 + private static final String RIGHT_BRACKET = "]";
42 + private static final String COMMA = ",";
43 + private static final String COLON = ":";
44 + private static final String QUOTE = "\"";
45 + private static final String E_UNSUP_TYPE = "Unsupported node type %s " +
46 + "field name is %s fieldName";
47 +
48 + private Logger log = LoggerFactory.getLogger(getClass());
49 +
50 + private StringBuilder treeString;
51 +
52 + /**
53 + * Creates a Default Json Builder with a specific root name.
54 + *
55 + * @param rootName the start string of the Json builder
56 + */
57 + public DefaultJsonBuilder(String rootName) {
58 + checkNotNull(rootName);
59 + treeString = new StringBuilder(rootName);
60 + }
61 +
62 + /**
63 + * Creates a Default Json Builder with a default root name.
64 + */
65 + public DefaultJsonBuilder() {
66 + treeString = new StringBuilder(LEFT_BRACE);
67 + }
68 +
69 + @Override
70 + public void addNodeTopHalf(String fieldName, JsonNodeType nodeType) {
71 +
72 + appendField(fieldName);
73 +
74 + switch (nodeType) {
75 + case OBJECT:
76 + treeString.append(LEFT_BRACE);
77 + break;
78 + case ARRAY:
79 + treeString.append(LEFT_BRACKET);
80 + break;
81 + default:
82 + throw new JsonParseException(String.format(E_UNSUP_TYPE,
83 + nodeType, fieldName));
84 + }
85 + }
86 +
87 + @Override
88 + public void addNodeWithValueTopHalf(String fieldName, String value) {
89 + if (isNullOrEmpty(fieldName)) {
90 + return;
91 + }
92 + appendField(fieldName);
93 + if (value.isEmpty()) {
94 + return;
95 + }
96 + treeString.append(QUOTE)
97 + .append(value)
98 + .append(QUOTE + COMMA);
99 + }
100 +
101 + @Override
102 + public void addNodeWithSetTopHalf(String fieldName, Set<String> sets) {
103 + if (isNullOrEmpty(fieldName)) {
104 + return;
105 + }
106 + appendField(fieldName);
107 + treeString.append(LEFT_BRACKET);
108 + for (String el : sets) {
109 + treeString.append(QUOTE)
110 + .append(el)
111 + .append(QUOTE + COMMA);
112 + }
113 + }
114 +
115 + @Override
116 + public void addNodeBottomHalf(JsonNodeType nodeType) {
117 +
118 + switch (nodeType) {
119 + case OBJECT:
120 + removeCommaIfExist();
121 + treeString.append(RIGHT_BRACE + COMMA);
122 + break;
123 +
124 + case ARRAY:
125 + removeCommaIfExist();
126 + treeString.append(RIGHT_BRACKET + COMMA);
127 + break;
128 +
129 + case BINARY:
130 + case BOOLEAN:
131 + case MISSING:
132 + case NULL:
133 + case NUMBER:
134 + case POJO:
135 + case STRING:
136 + log.debug("Unimplemented node type {}", nodeType);
137 + break;
138 +
139 + default:
140 + throw new JsonParseException("Unsupported json node type " +
141 + nodeType);
142 + }
143 + }
144 +
145 + @Override
146 + public String getTreeString() {
147 + removeCommaIfExist();
148 + return treeString.append(RIGHT_BRACE).toString();
149 + }
150 +
151 + @Override
152 + public ObjectNode getTreeNode() {
153 + ObjectNode node = null;
154 + try {
155 + node = (ObjectNode) (new ObjectMapper()).readTree(getTreeString());
156 + } catch (IOException e) {
157 + log.error("Parse json string failed {}", e.getMessage());
158 + }
159 + return node;
160 + }
161 +
162 +
163 + private void appendField(String fieldName) {
164 + if (!isNullOrEmpty(fieldName)) {
165 + treeString.append(QUOTE)
166 + .append(fieldName)
167 + .append(QUOTE + COLON);
168 + }
169 + }
170 +
171 + private void removeCommaIfExist() {
172 + int lastIndex = treeString.length() - 1;
173 + if (treeString.charAt(lastIndex) == COMMA.charAt(0)) {
174 + treeString.deleteCharAt(lastIndex);
175 + }
176 + }
177 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.protocol.restconf.server.utils.parser.json;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.node.ArrayNode;
21 +import com.fasterxml.jackson.databind.node.ObjectNode;
22 +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonWalker;
23 +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonListener;
24 +
25 +import java.util.Iterator;
26 +import java.util.Map;
27 +
28 +/**
29 + * Represents implementation of JSON walk, which walks the JSON object node.
30 + */
31 +public class DefaultJsonWalker implements JsonWalker {
32 + @Override
33 + public void walk(JsonListener jsonListener, String fieldName,
34 + ObjectNode objectNode) {
35 +
36 + //enter the object node, the original ObjectNode should have a module
37 + //name as fieldName.
38 + jsonListener.enterJsonNode(fieldName, objectNode);
39 + //the node has no children, then exist and return.
40 + if (!objectNode.isContainerNode()) {
41 + jsonListener.exitJsonNode(objectNode);
42 + return;
43 + }
44 +
45 + Iterator<Map.Entry<String, JsonNode>> fields = objectNode.fields();
46 + while (fields.hasNext()) {
47 + //get the children entry of the node
48 + Map.Entry<String, JsonNode> currentChild = fields.next();
49 + String key = currentChild.getKey();
50 + JsonNode value = currentChild.getValue();
51 + //if the entry's value has its own children, do a recursion.
52 + //if the entry has no children, store the key and value.
53 + //for we don't know the specific type of the entry's value, we
54 + // should give it to a method which can handle JsonNode
55 + if (value.isContainerNode()) {
56 + walkJsonNode(jsonListener, key, value);
57 + } else {
58 + jsonListener.enterJsonNode(key, value);
59 + jsonListener.exitJsonNode(value);
60 + }
61 + }
62 + jsonListener.exitJsonNode(objectNode);
63 + }
64 +
65 + /**
66 + * Walks the JSON data tree. This method is called when we don't know
67 + * the exact type of a json node.
68 + *
69 + * @param jsonListener Json listener implemented by the user
70 + * @param fieldName the original object node field
71 + * @param rootNode the json node which needs to be walk
72 + */
73 + private void walkJsonNode(JsonListener jsonListener, String fieldName,
74 + JsonNode rootNode) {
75 + if (rootNode.isObject()) {
76 + walk(jsonListener, fieldName, (ObjectNode) rootNode);
77 + return;
78 + }
79 +
80 + if (rootNode.isArray()) {
81 + walkArrayNode(jsonListener, fieldName, (ArrayNode) rootNode);
82 + }
83 + }
84 +
85 + /**
86 + * Walks the JSON data tree. This method is called when the user knows the
87 + * json node type is ArrayNode.
88 + *
89 + * @param jsonListener Json listener implemented by the user
90 + * @param fieldName the original object node field
91 + * @param rootNode the json node which needs to be walk
92 + */
93 + private void walkArrayNode(JsonListener jsonListener, String fieldName,
94 + ArrayNode rootNode) {
95 + if (rootNode == null) {
96 + return;
97 + }
98 + //enter the array node.
99 + jsonListener.enterJsonNode(fieldName, rootNode);
100 + Iterator<JsonNode> children = rootNode.elements();
101 + while (children.hasNext()) {
102 + JsonNode currentChild = children.next();
103 + if (currentChild.isContainerNode()) {
104 + walkJsonNode(jsonListener, null, currentChild);
105 + }
106 + }
107 + jsonListener.exitJsonNode(rootNode);
108 + }
109 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.protocol.restconf.server.utils.parser.json;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.node.ArrayNode;
21 +import com.fasterxml.jackson.databind.node.JsonNodeType;
22 +import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
23 +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonListener;
24 +import org.onosproject.yms.ydt.YdtBuilder;
25 +import org.onosproject.yms.ydt.YdtContext;
26 +import org.slf4j.Logger;
27 +
28 +import java.util.HashSet;
29 +import java.util.Iterator;
30 +import java.util.Set;
31 +
32 +import static com.fasterxml.jackson.databind.node.JsonNodeType.ARRAY;
33 +import static com.google.common.base.Strings.isNullOrEmpty;
34 +import static org.onosproject.yms.ydt.YdtType.MULTI_INSTANCE_NODE;
35 +import static org.onosproject.yms.ydt.YdtType.SINGLE_INSTANCE_NODE;
36 +import static org.slf4j.LoggerFactory.getLogger;
37 +
38 +/**
39 + * Represents default implementation of codec JSON listener.
40 + */
41 +public class JsonToYdtListener implements JsonListener {
42 +
43 + private static final String INPUT_FIELD_NAME = "input";
44 + private static final String COLON = ":";
45 + private static final int INPUT_FIELD_LENGTH = 2;
46 + private static final String E_UNSUP_TYPE = "Unsupported node type %s " +
47 + "field name is %s fieldName";
48 +
49 + private Logger log = getLogger(getClass());
50 +
51 + private YdtBuilder ydtBuilder;
52 + private String defaultMultiInsNodeName;
53 + private YdtContext rpcModule;
54 +
55 + /**
56 + * Creates a listener for the process of a Json Object, the listener will
57 + * try to transfer the JSON object to a Ydt builder.
58 + *
59 + * @param ydtBuilder the YDT builder to build a YDT object
60 + */
61 + public JsonToYdtListener(YdtBuilder ydtBuilder) {
62 + this.ydtBuilder = ydtBuilder;
63 + }
64 +
65 + @Override
66 + public void enterJsonNode(String fieldName, JsonNode node) {
67 + if (isNullOrEmpty(fieldName)) {
68 + if (!isNullOrEmpty(defaultMultiInsNodeName)) {
69 + ydtBuilder.addChild(defaultMultiInsNodeName, null,
70 + MULTI_INSTANCE_NODE);
71 + }
72 + return;
73 + }
74 + JsonNodeType nodeType = node.getNodeType();
75 + switch (nodeType) {
76 + case OBJECT:
77 + processObjectNode(fieldName);
78 + break;
79 +
80 + case ARRAY:
81 + processArrayNode(fieldName, node);
82 + break;
83 +
84 + //TODO for now, just process the following three node type
85 + case STRING:
86 + case NUMBER:
87 + case BOOLEAN:
88 + ydtBuilder.addLeaf(fieldName, null, node.asText());
89 + break;
90 +
91 + case BINARY:
92 + case MISSING:
93 + case NULL:
94 + case POJO:
95 + log.debug("Unimplemented node type {}", nodeType);
96 + break;
97 +
98 + default:
99 + throw new JsonParseException(String.format(E_UNSUP_TYPE,
100 + nodeType, fieldName));
101 + }
102 + }
103 +
104 + @Override
105 + public void exitJsonNode(JsonNode jsonNode) {
106 + if (jsonNode.getNodeType() == ARRAY) {
107 + return;
108 + }
109 + ydtBuilder.traverseToParent();
110 + YdtContext curNode = ydtBuilder.getCurNode();
111 + //if the current node is the RPC node, then should go to the father
112 + //for we have enter the RPC node and Input node at the same time
113 + //and the input is the only child of RPC node.
114 +
115 + if (curNode == null) {
116 + return;
117 + }
118 + String name = curNode.getName();
119 + if (rpcModule != null && name.equals(rpcModule.getName())) {
120 + ydtBuilder.traverseToParent();
121 + }
122 + }
123 +
124 + private void processObjectNode(String fieldName) {
125 + String[] segments = fieldName.split(COLON);
126 + Boolean isLastInput = segments.length == INPUT_FIELD_LENGTH &&
127 + segments[INPUT_FIELD_LENGTH - 1].equals(INPUT_FIELD_NAME);
128 + int first = 0;
129 + int second = 1;
130 + if (isLastInput) {
131 + ydtBuilder.addChild(segments[first], null, SINGLE_INSTANCE_NODE);
132 + rpcModule = ydtBuilder.getCurNode();
133 + ydtBuilder.addChild(segments[second], null, SINGLE_INSTANCE_NODE);
134 + } else {
135 + ydtBuilder.addChild(fieldName, null, SINGLE_INSTANCE_NODE);
136 + }
137 + }
138 +
139 + private void processArrayNode(String fieldName, JsonNode node) {
140 + ArrayNode arrayNode = (ArrayNode) node;
141 + Set<String> sets = new HashSet<>();
142 + Iterator<JsonNode> elements = arrayNode.elements();
143 + boolean isLeafList = true;
144 + while (elements.hasNext()) {
145 + JsonNode element = elements.next();
146 + JsonNodeType eleType = element.getNodeType();
147 +
148 + if (eleType == JsonNodeType.STRING ||
149 + eleType == JsonNodeType.NUMBER ||
150 + eleType == JsonNodeType.BOOLEAN) {
151 + sets.add(element.asText());
152 + } else {
153 + isLeafList = false;
154 + }
155 + }
156 + if (isLeafList) {
157 + //leaf-list
158 + ydtBuilder.addLeaf(fieldName, null, sets);
159 + } else {
160 + this.defaultMultiInsNodeName = fieldName;
161 + }
162 + }
163 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.protocol.restconf.server.utils.parser.json;
18 +
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +import com.google.common.base.Splitter;
21 +import com.google.common.collect.Lists;
22 +import org.onosproject.protocol.restconf.server.utils.exceptions.JsonParseException;
23 +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
24 +import org.onosproject.yms.ydt.YdtBuilder;
25 +import org.onosproject.yms.ydt.YdtContext;
26 +import org.onosproject.yms.ydt.YdtContextOperationType;
27 +import org.onosproject.yms.ydt.YdtListener;
28 +import org.onosproject.yms.ydt.YdtType;
29 +import org.onosproject.yms.ydt.YdtWalker;
30 +
31 +import java.io.UnsupportedEncodingException;
32 +import java.net.URLDecoder;
33 +import java.util.ArrayList;
34 +import java.util.List;
35 +
36 +import static com.google.common.base.Preconditions.checkNotNull;
37 +import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
38 +
39 +/**
40 + * Utils to complete the conversion between JSON and YDT(YANG DATA MODEL).
41 + */
42 +public final class ParserUtils {
43 +
44 + private static final Splitter SLASH_SPLITTER = Splitter.on('/');
45 + private static final Splitter COMMA_SPLITTER = Splitter.on(',');
46 + private static final String EQUAL = "=";
47 + private static final String COMMA = ",";
48 + private static final String COLON = ":";
49 + private static final String URI_ENCODING_CHAR_SET = "ISO-8859-1";
50 + private static final String ERROR_LIST_MSG = "List/Leaf-list node should be " +
51 + "in format \"nodeName=key\"or \"nodeName=instance-value\"";
52 + private static final String ERROR_MODULE_MSG = "First node should be in " +
53 + "format \"moduleName:nodeName\"";
54 +
55 + // no instantiation
56 + private ParserUtils() {
57 + }
58 +
59 + /**
60 + * Converts URI identifier to YDT builder.
61 + *
62 + * @param id the uri identifier from web request
63 + * @param builder the base ydt builder
64 + * @param opType the ydt operation type for the uri
65 + */
66 + public static void convertUriToYdt(String id,
67 + YdtBuilder builder,
68 + YdtContextOperationType opType) {
69 + checkNotNull(id, "uri identifier should not be null");
70 + List<String> paths = urlPathArgsDecode(SLASH_SPLITTER.split(id));
71 + if (!paths.isEmpty()) {
72 + processPathSegments(paths, builder, opType);
73 + }
74 + }
75 +
76 + /**
77 + * Converts JSON objectNode to YDT builder. The objectNode can be any
78 + * standard JSON node, node just for RESTconf payload.
79 + *
80 + * @param objectNode the objectNode from web request
81 + * @param builder the base ydt builder
82 + */
83 + public static void convertJsonToYdt(ObjectNode objectNode,
84 + YdtBuilder builder) {
85 +
86 + JsonToYdtListener listener = new JsonToYdtListener(builder);
87 + new DefaultJsonWalker().walk(listener, null, objectNode);
88 + }
89 +
90 + /**
91 + * Converts a Ydt context tree to a JSON object.
92 + *
93 + * @param rootName the name of the YdtContext from which the YdtListener
94 + * start to builder a Json Object
95 + * @param context a abstract data model for YANG data
96 + * @param walker abstraction of an entity which provides interfaces for
97 + * YDT walk
98 + * @return the JSON node corresponding the YANG data
99 + */
100 + public static ObjectNode convertYdtToJson(String rootName,
101 + YdtContext context,
102 + YdtWalker walker) {
103 + JsonBuilder builder = new DefaultJsonBuilder();
104 + YdtListener listener = new YdtToJsonListener(rootName, builder);
105 + walker.walk(listener, context);
106 + return builder.getTreeNode();
107 + }
108 +
109 + /**
110 + * Converts a list of path segments to a YDT builder tree.
111 + *
112 + * @param paths the list of path segments split from URI
113 + * @param builder the base YDT builder
114 + * @param opType the YDT operation type for the Path segment
115 + * @return the YDT builder with the tree info of paths
116 + */
117 + private static YdtBuilder processPathSegments(List<String> paths,
118 + YdtBuilder builder,
119 + YdtContextOperationType opType) {
120 + if (paths.isEmpty()) {
121 + return builder;
122 + }
123 + boolean isLastNode = paths.size() == 1;
124 + YdtContextOperationType opTypeForThisNode = isLastNode ? opType : NONE;
125 +
126 + String path = paths.iterator().next();
127 + if (path.contains(COLON)) {
128 + addModule(builder, path);
129 + addNode(path, builder, opTypeForThisNode);
130 + } else if (path.contains(EQUAL)) {
131 + addListOrLeafList(path, builder, opTypeForThisNode);
132 + } else {
133 + addLeaf(path, builder, opTypeForThisNode);
134 + }
135 +
136 + if (isLastNode) {
137 + return builder;
138 + }
139 + List<String> remainPaths = paths.subList(1, paths.size());
140 + processPathSegments(remainPaths, builder, opType);
141 +
142 + return builder;
143 + }
144 +
145 + private static YdtBuilder addModule(YdtBuilder builder, String path) {
146 + String moduleName = getPreSegment(path, COLON);
147 + if (moduleName == null) {
148 + throw new JsonParseException(ERROR_MODULE_MSG);
149 + }
150 + builder.addChild(moduleName, null, YdtType.SINGLE_INSTANCE_NODE);
151 + return builder;
152 + }
153 +
154 + private static YdtBuilder addNode(String path, YdtBuilder builder,
155 + YdtContextOperationType opType) {
156 + String nodeName = getLatterSegment(path, COLON);
157 + builder.addChild(nodeName,
158 + null,
159 + YdtType.SINGLE_INSTANCE_NODE,
160 + opType);
161 + return builder;
162 + }
163 +
164 + private static YdtBuilder addListOrLeafList(String path,
165 + YdtBuilder builder,
166 + YdtContextOperationType opType) {
167 + String nodeName = getPreSegment(path, EQUAL);
168 + String keyStr = getLatterSegment(path, EQUAL);
169 + if (keyStr == null) {
170 + throw new JsonParseException(ERROR_LIST_MSG);
171 + }
172 + builder.setDefaultEditOperationType(opType);
173 + if (keyStr.contains(COMMA)) {
174 + List<String> keys = Lists.
175 + newArrayList(COMMA_SPLITTER.split(keyStr));
176 + builder.addMultiInstanceChild(nodeName, null, keys);
177 + } else {
178 + builder.addMultiInstanceChild(nodeName, null,
179 + Lists.newArrayList(keyStr));
180 + }
181 + return builder;
182 + }
183 +
184 + private static YdtBuilder addLeaf(String path, YdtBuilder builder,
185 + YdtContextOperationType opType) {
186 + checkNotNull(path);
187 + builder.addChild(path, null, opType);
188 + return builder;
189 + }
190 +
191 + /**
192 + * Returns the previous segment of a path which is separated by a split char.
193 + * For example:
194 + * <pre>
195 + * "foo:bar", ":" --> "foo"
196 + * </pre>
197 + *
198 + * @param path the original path string
199 + * @param splitChar char used to split the path
200 + * @return the previous segment of the path
201 + */
202 + private static String getPreSegment(String path, String splitChar) {
203 + int idx = path.indexOf(splitChar);
204 + if (idx == -1) {
205 + return null;
206 + }
207 +
208 + if (path.indexOf(splitChar, idx + 1) != -1) {
209 + return null;
210 + }
211 +
212 + return path.substring(0, idx);
213 + }
214 +
215 + /**
216 + * Returns the latter segment of a path which is separated by a split char.
217 + * For example:
218 + * <pre>
219 + * "foo:bar", ":" --> "bar"
220 + * </pre>
221 + *
222 + * @param path the original path string
223 + * @param splitChar char used to split the path
224 + * @return the latter segment of the path
225 + */
226 + private static String getLatterSegment(String path, String splitChar) {
227 + int idx = path.indexOf(splitChar);
228 + if (idx == -1) {
229 + return path;
230 + }
231 +
232 + if (path.indexOf(splitChar, idx + 1) != -1) {
233 + return null;
234 + }
235 +
236 + return path.substring(idx + 1);
237 + }
238 +
239 + /**
240 + * Converts a list of path from the original format to ISO-8859-1 code.
241 + *
242 + * @param paths the original paths
243 + * @return list of decoded paths
244 + */
245 + public static List<String> urlPathArgsDecode(Iterable<String> paths) {
246 + try {
247 + List<String> decodedPathArgs = new ArrayList<>();
248 + for (String pathArg : paths) {
249 + String decode = URLDecoder.decode(pathArg,
250 + URI_ENCODING_CHAR_SET);
251 + decodedPathArgs.add(decode);
252 + }
253 + return decodedPathArgs;
254 + } catch (UnsupportedEncodingException e) {
255 + throw new JsonParseException("Invalid URL path arg '" +
256 + paths + "': ", e);
257 + }
258 + }
259 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.protocol.restconf.server.utils.parser.json;
18 +
19 +import com.fasterxml.jackson.databind.node.JsonNodeType;
20 +import org.onosproject.protocol.restconf.server.utils.exceptions.YdtParseException;
21 +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonBuilder;
22 +import org.onosproject.yms.ydt.YdtContext;
23 +import org.onosproject.yms.ydt.YdtListener;
24 +
25 +import static com.google.common.base.Strings.isNullOrEmpty;
26 +
27 +/**
28 + * Represents implementation of codec YDT listener.
29 + */
30 +public class YdtToJsonListener implements YdtListener {
31 +
32 + private static final String EMPTY = "";
33 + private JsonBuilder jsonBuilder;
34 + //the root name of the json
35 + //the input YdtContext is usually a total tree of a YANG resource
36 + //this property is used to mark the start of the request node.
37 + private String rootName;
38 + //the parse state
39 + private boolean isBegin;
40 +
41 + /**
42 + * Creates a listener for the process of a Ydt, the listener will try to
43 + * transfer the Ydt to a JSON Object.
44 + *
45 + * @param rootName the name of a specific YdtContext begin to process
46 + * @param jsonBuilder the JSON builder to build a JSON object
47 + */
48 + public YdtToJsonListener(String rootName, JsonBuilder jsonBuilder) {
49 + this.jsonBuilder = jsonBuilder;
50 + this.rootName = rootName;
51 + this.isBegin = isNullOrEmpty(rootName);
52 + }
53 +
54 + @Override
55 + public void enterYdtNode(YdtContext ydtContext) {
56 + String name = ydtContext.getName();
57 +
58 + if (!isBegin && name.equals(rootName)) {
59 + isBegin = true;
60 + }
61 + if (!isBegin) {
62 + return;
63 + }
64 +
65 + switch (ydtContext.getYdtType()) {
66 +
67 + case SINGLE_INSTANCE_NODE:
68 + jsonBuilder.addNodeTopHalf(name, JsonNodeType.OBJECT);
69 + break;
70 + case MULTI_INSTANCE_NODE:
71 + YdtContext preNode = ydtContext.getPreviousSibling();
72 + if (preNode == null || !preNode.getName().equals(name)) {
73 + jsonBuilder.addNodeTopHalf(name, JsonNodeType.ARRAY);
74 + }
75 + jsonBuilder.addNodeTopHalf(EMPTY, JsonNodeType.OBJECT);
76 + break;
77 + case SINGLE_INSTANCE_LEAF_VALUE_NODE:
78 + jsonBuilder.addNodeWithValueTopHalf(name, ydtContext.getValue());
79 + break;
80 + case MULTI_INSTANCE_LEAF_VALUE_NODE:
81 + jsonBuilder.addNodeWithSetTopHalf(name, ydtContext.getValueSet());
82 + break;
83 + default:
84 + throw new YdtParseException("unknown Ydt type " +
85 + ydtContext.getYdtType());
86 + }
87 +
88 + }
89 +
90 + @Override
91 + public void exitYdtNode(YdtContext ydtContext) {
92 +
93 + if (!isBegin) {
94 + return;
95 + }
96 +
97 + String curName = ydtContext.getName();
98 + YdtContext nextNode = ydtContext.getNextSibling();
99 + switch (ydtContext.getYdtType()) {
100 +
101 + case SINGLE_INSTANCE_NODE:
102 + jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
103 + break;
104 + case MULTI_INSTANCE_NODE:
105 + if (nextNode == null || !nextNode.getName().equals(curName)) {
106 + jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
107 + jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
108 + } else {
109 + jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
110 + }
111 + break;
112 + case SINGLE_INSTANCE_LEAF_VALUE_NODE:
113 + jsonBuilder.addNodeBottomHalf(JsonNodeType.STRING);
114 + break;
115 + case MULTI_INSTANCE_LEAF_VALUE_NODE:
116 + jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
117 + break;
118 + default:
119 + throw new YdtParseException("Unknown Ydt type " +
120 + ydtContext.getYdtType());
121 + }
122 + if (curName.equals(rootName) &&
123 + (nextNode == null || !nextNode.getName().equals(rootName))) {
124 + isBegin = false;
125 + }
126 + }
127 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Provider utilities to support the data format based encoding and decoding,
19 + * used by YMSC to operate on different data format and YDT(YANG DATA TYPE).
20 + */
21 +
22 +package org.onosproject.protocol.restconf.server.utils.parser.json;
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.restconf.utils.parser.json;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.ObjectMapper;
21 +import com.fasterxml.jackson.databind.node.ObjectNode;
22 +import org.junit.Before;
23 +import org.junit.Test;
24 +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonListener;
25 +import org.onosproject.protocol.restconf.server.utils.parser.json.DefaultJsonWalker;
26 +
27 +import java.io.InputStream;
28 +import java.util.ArrayList;
29 +import java.util.List;
30 +
31 +import static org.hamcrest.MatcherAssert.assertThat;
32 +import static org.hamcrest.core.Is.is;
33 +
34 +public class DefaultJsonWalkerTest {
35 +
36 + private static final String[] EXP_TEXT_ARRAY = {
37 + "Enter: type is OBJECT name is null",
38 + "Enter: type is STRING name is name",
39 + "Exit: type is STRING",
40 + "Enter: type is STRING name is surname",
41 + "Exit: type is STRING",
42 + "Exit: type is OBJECT"
43 + };
44 +
45 + private static final String[] EXP_NODE_LIST_ARRAY = {
46 + "Enter: type is OBJECT name is null",
47 + "Enter: type is STRING name is surname",
48 + "Exit: type is STRING",
49 + "Enter: type is ARRAY name is networklist",
50 + "Exit: type is ARRAY",
51 + "Exit: type is OBJECT"
52 + };
53 +
54 + private static final String[] EXP_NODE_WITH_ARRAY = {
55 + "Enter: type is OBJECT name is null",
56 + "Enter: type is STRING name is surname",
57 + "Exit: type is STRING",
58 + "Enter: type is ARRAY name is networklist",
59 + "Enter: type is OBJECT name is null",
60 + "Enter: type is STRING name is network-id",
61 + "Exit: type is STRING",
62 + "Enter: type is STRING name is server-provided",
63 + "Exit: type is STRING",
64 + "Exit: type is OBJECT",
65 + "Enter: type is OBJECT name is null",
66 + "Enter: type is STRING name is network-id",
67 + "Exit: type is STRING",
68 + "Enter: type is STRING name is server-provided",
69 + "Exit: type is STRING",
70 + "Exit: type is OBJECT",
71 + "Enter: type is OBJECT name is null",
72 + "Enter: type is STRING name is network-id",
73 + "Exit: type is STRING",
74 + "Enter: type is STRING name is server-provided",
75 + "Exit: type is STRING",
76 + "Exit: type is OBJECT",
77 + "Exit: type is ARRAY",
78 + "Exit: type is OBJECT"
79 + };
80 + private static final String WRONG_CONTENT_MSG = "Wrong content in array";
81 +
82 + private final List<String> logger = new ArrayList<>();
83 +
84 + DefaultJsonWalker defaultJsonWalker = new DefaultJsonWalker();
85 + InternalJsonListener jsonListener = new InternalJsonListener();
86 +
87 + ObjectNode arrayNode;
88 + ObjectNode textNode;
89 + ObjectNode listNode;
90 +
91 + @Before
92 + public void setup() throws Exception {
93 + textNode = loadJsonFile("/textNode.json");
94 + listNode = loadJsonFile("/listNode.json");
95 + arrayNode = loadJsonFile("/arrayNode.json");
96 + }
97 +
98 + private ObjectNode loadJsonFile(String path) throws Exception {
99 + InputStream jsonStream = getClass().getResourceAsStream(path);
100 + ObjectMapper mapper = new ObjectMapper();
101 + return (ObjectNode) mapper.readTree(jsonStream);
102 + }
103 +
104 + private void assertWalkResult(String[] expectArray, List<String> logger) {
105 + for (int i = 0; i < expectArray.length; i++) {
106 + assertThat(WRONG_CONTENT_MSG,
107 + true,
108 + is(logger.get(i).contentEquals(expectArray[i])));
109 + }
110 + }
111 +
112 + @Test
113 + public void testWalkTextNode() throws Exception {
114 +
115 + defaultJsonWalker.walk(jsonListener, null, textNode);
116 + assertWalkResult(EXP_TEXT_ARRAY, logger);
117 + }
118 +
119 + @Test
120 + public void testWalkNodeWithList() throws Exception {
121 + defaultJsonWalker.walk(jsonListener, null, listNode);
122 + assertWalkResult(EXP_NODE_LIST_ARRAY, logger);
123 + }
124 +
125 + @Test
126 + public void testWalkNodeWithArray() throws Exception {
127 + defaultJsonWalker.walk(jsonListener, null, arrayNode);
128 + assertWalkResult(EXP_NODE_WITH_ARRAY, logger);
129 + }
130 +
131 + private class InternalJsonListener implements JsonListener {
132 +
133 + @Override
134 + public void enterJsonNode(String fieldName, JsonNode node) {
135 + logger.add("Enter: type is " + node.getNodeType() + " name is " +
136 + fieldName);
137 + }
138 +
139 + @Override
140 + public void exitJsonNode(JsonNode node) {
141 + logger.add("Exit: type is " + node.getNodeType());
142 + }
143 + }
144 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "surname": "Bangalore",
3 + "networklist":[
4 + {
5 + "network-id": "520",
6 + "server-provided": "123"
7 + },
8 + {
9 + "network-id": "521",
10 + "server-provided": "124"
11 + },
12 + {
13 + "network-id": "523",
14 + "server-provided": "125"
15 + }
16 +
17 + ]
18 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "surname": "Bangalore",
3 + "networklist":[
4 + "0.79",
5 + "1.04",
6 + "3.14"
7 + ]
8 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "Huawei",
3 + "surname": "Bangalore"
4 +}
...\ No newline at end of file ...\ No newline at end of file