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 ( | ... | ... |
protocols/restconf/server/api/src/main/java/org/onosproject/protocol/restconf/server/api/Patch.java
0 → 100644
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 | } | ... | ... |
protocols/restconf/server/utils/BUCK
0 → 100644
protocols/restconf/server/utils/pom.xml
0 → 100644
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 |
-
Please register or login to post a comment