Split Restconf implementations (draft02 and RFC) - base api
[netconf.git] / restconf / restconf-nb-bierman02 / src / main / java / org / opendaylight / netconf / sal / restconf / impl / JSONRestconfServiceImpl.java
1 /*
2  * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.sal.restconf.impl;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import java.io.ByteArrayInputStream;
13 import java.io.ByteArrayOutputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.lang.annotation.Annotation;
17 import java.net.URI;
18 import java.nio.charset.StandardCharsets;
19 import java.util.Collections;
20 import java.util.List;
21 import javax.ws.rs.core.MediaType;
22 import javax.ws.rs.core.MultivaluedHashMap;
23 import javax.ws.rs.core.MultivaluedMap;
24 import javax.ws.rs.core.PathSegment;
25 import javax.ws.rs.core.UriBuilder;
26 import javax.ws.rs.core.UriInfo;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
29 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
30 import org.opendaylight.netconf.sal.restconf.api.JSONRestconfService;
31 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
32 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
33 import org.opendaylight.yangtools.yang.common.OperationFailedException;
34 import org.opendaylight.yangtools.yang.common.RpcError;
35 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
36 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * Implementation of the JSONRestconfService interface using the restconf Draft02 implementation.
42  *
43  * @author Thomas Pantelis
44  * @deprecated Replaced by {@link JSONRestconfServiceDraft18}
45  */
46 @Deprecated
47 public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseable {
48     private static final Logger LOG = LoggerFactory.getLogger(JSONRestconfServiceImpl.class);
49
50     private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
51
52     @SuppressWarnings("checkstyle:IllegalCatch")
53     @Override
54     public void put(final String uriPath, final String payload) throws OperationFailedException {
55         Preconditions.checkNotNull(payload, "payload can't be null");
56
57         LOG.debug("put: uriPath: {}, payload: {}", uriPath, payload);
58
59         final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
60         final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, false);
61
62         LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier());
63         LOG.debug("Parsed NormalizedNode: {}", context.getData());
64
65         try {
66             RestconfImpl.getInstance().updateConfigurationData(uriPath, context, new SimpleUriInfo(uriPath));
67         } catch (final Exception e) {
68             propagateExceptionAs(uriPath, e, "PUT");
69         }
70     }
71
72     @SuppressWarnings("checkstyle:IllegalCatch")
73     @Override
74     public void post(final String uriPath, final String payload)
75             throws OperationFailedException {
76         Preconditions.checkNotNull(payload, "payload can't be null");
77
78         LOG.debug("post: uriPath: {}, payload: {}", uriPath, payload);
79
80         final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
81         final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
82
83         LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier());
84         LOG.debug("Parsed NormalizedNode: {}", context.getData());
85
86         try {
87             RestconfImpl.getInstance().createConfigurationData(uriPath, context, new SimpleUriInfo(uriPath));
88         } catch (final Exception e) {
89             propagateExceptionAs(uriPath, e, "POST");
90         }
91     }
92
93     @SuppressWarnings("checkstyle:IllegalCatch")
94     @Override
95     public void delete(final String uriPath) throws OperationFailedException {
96         LOG.debug("delete: uriPath: {}", uriPath);
97
98         try {
99             RestconfImpl.getInstance().deleteConfigurationData(uriPath);
100         } catch (final Exception e) {
101             propagateExceptionAs(uriPath, e, "DELETE");
102         }
103     }
104
105     @SuppressWarnings("checkstyle:IllegalCatch")
106     @Override
107     public Optional<String> get(final String uriPath, final LogicalDatastoreType datastoreType)
108             throws OperationFailedException {
109         LOG.debug("get: uriPath: {}", uriPath);
110
111         try {
112             NormalizedNodeContext readData;
113             final SimpleUriInfo uriInfo = new SimpleUriInfo(uriPath);
114             if (datastoreType == LogicalDatastoreType.CONFIGURATION) {
115                 readData = RestconfImpl.getInstance().readConfigurationData(uriPath, uriInfo);
116             } else {
117                 readData = RestconfImpl.getInstance().readOperationalData(uriPath, uriInfo);
118             }
119
120             final Optional<String> result = Optional.of(toJson(readData));
121
122             LOG.debug("get returning: {}", result.get());
123
124             return result;
125         } catch (final Exception e) {
126             if (!isDataMissing(e)) {
127                 propagateExceptionAs(uriPath, e, "GET");
128             }
129
130             LOG.debug("Data missing - returning absent");
131             return Optional.absent();
132         }
133     }
134
135     @SuppressWarnings("checkstyle:IllegalCatch")
136     @Override
137     public Optional<String> invokeRpc(final String uriPath, final Optional<String> input)
138             throws OperationFailedException {
139         Preconditions.checkNotNull(uriPath, "uriPath can't be null");
140
141         final String actualInput = input.isPresent() ? input.get() : null;
142
143         LOG.debug("invokeRpc: uriPath: {}, input: {}", uriPath, actualInput);
144
145         String output = null;
146         try {
147             NormalizedNodeContext outputContext;
148             if (actualInput != null) {
149                 final InputStream entityStream = new ByteArrayInputStream(actualInput.getBytes(StandardCharsets.UTF_8));
150                 final NormalizedNodeContext inputContext =
151                         JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
152
153                 LOG.debug("Parsed YangInstanceIdentifier: {}", inputContext.getInstanceIdentifierContext()
154                         .getInstanceIdentifier());
155                 LOG.debug("Parsed NormalizedNode: {}", inputContext.getData());
156
157                 outputContext = RestconfImpl.getInstance().invokeRpc(uriPath, inputContext, null);
158             } else {
159                 outputContext = RestconfImpl.getInstance().invokeRpc(uriPath, "", null);
160             }
161
162             if (outputContext.getData() != null) {
163                 output = toJson(outputContext);
164             }
165         } catch (final Exception e) {
166             propagateExceptionAs(uriPath, e, "RPC");
167         }
168
169         return Optional.fromNullable(output);
170     }
171
172     @Override
173     public void close() {
174     }
175
176     private static String toJson(final NormalizedNodeContext readData) throws IOException {
177         final NormalizedNodeJsonBodyWriter writer = new NormalizedNodeJsonBodyWriter();
178         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
179         writer.writeTo(readData, NormalizedNodeContext.class, null, EMPTY_ANNOTATIONS,
180                 MediaType.APPLICATION_JSON_TYPE, null, outputStream);
181         return outputStream.toString(StandardCharsets.UTF_8.name());
182     }
183
184     private static boolean isDataMissing(final Exception exception) {
185         boolean dataMissing = false;
186         if (exception instanceof RestconfDocumentedException) {
187             final RestconfDocumentedException rde = (RestconfDocumentedException)exception;
188             if (!rde.getErrors().isEmpty()) {
189                 if (rde.getErrors().get(0).getErrorTag() == ErrorTag.DATA_MISSING) {
190                     dataMissing = true;
191                 }
192             }
193         }
194
195         return dataMissing;
196     }
197
198     private static void propagateExceptionAs(final String uriPath, final Exception exception, final String operation)
199             throws OperationFailedException {
200         LOG.debug("Error for uriPath: {}", uriPath, exception);
201
202         if (exception instanceof RestconfDocumentedException) {
203             throw new OperationFailedException(String.format(
204                     "%s failed for URI %s", operation, uriPath), exception.getCause(),
205                     toRpcErrors(((RestconfDocumentedException)exception).getErrors()));
206         }
207
208         throw new OperationFailedException(String.format("%s failed for URI %s", operation, uriPath), exception);
209     }
210
211     private static RpcError[] toRpcErrors(final List<RestconfError> from) {
212         final RpcError[] to = new RpcError[from.size()];
213         int index = 0;
214         for (final RestconfError e: from) {
215             to[index++] = RpcResultBuilder.newError(toRpcErrorType(e.getErrorType()), e.getErrorTag().getTagValue(),
216                     e.getErrorMessage());
217         }
218
219         return to;
220     }
221
222     private static ErrorType toRpcErrorType(final RestconfError.ErrorType errorType) {
223         switch (errorType) {
224             case TRANSPORT: {
225                 return ErrorType.TRANSPORT;
226             }
227             case RPC: {
228                 return ErrorType.RPC;
229             }
230             case PROTOCOL: {
231                 return ErrorType.PROTOCOL;
232             }
233             default: {
234                 return ErrorType.APPLICATION;
235             }
236         }
237     }
238
239     private static class SimpleUriInfo implements UriInfo {
240         private final String path;
241         private final MultivaluedMap<String, String> queryParams;
242
243         SimpleUriInfo(String path) {
244             this(path, new MultivaluedHashMap<>());
245         }
246
247         SimpleUriInfo(String path, MultivaluedMap<String, String> queryParams) {
248             this.path = path;
249             this.queryParams = queryParams;
250         }
251
252         @Override
253         public String getPath() {
254             return path;
255         }
256
257         @Override
258         public String getPath(boolean decode) {
259             return path;
260         }
261
262         @Override
263         public List<PathSegment> getPathSegments() {
264             throw new UnsupportedOperationException();
265         }
266
267         @Override
268         public List<PathSegment> getPathSegments(boolean decode) {
269             throw new UnsupportedOperationException();
270         }
271
272         @Override
273         public URI getRequestUri() {
274             return URI.create(path);
275         }
276
277         @Override
278         public UriBuilder getRequestUriBuilder() {
279             return UriBuilder.fromUri(getRequestUri());
280         }
281
282         @Override
283         public URI getAbsolutePath() {
284             return getRequestUri();
285         }
286
287         @Override
288         public UriBuilder getAbsolutePathBuilder() {
289             return UriBuilder.fromUri(getAbsolutePath());
290         }
291
292         @Override
293         public URI getBaseUri() {
294             return URI.create("");
295         }
296
297         @Override
298         public UriBuilder getBaseUriBuilder() {
299             return UriBuilder.fromUri(getBaseUri());
300         }
301
302         @Override
303         public MultivaluedMap<String, String> getPathParameters() {
304             return new MultivaluedHashMap<>();
305         }
306
307         @Override
308         public MultivaluedMap<String, String> getPathParameters(boolean decode) {
309             return getPathParameters();
310         }
311
312         @Override
313         public MultivaluedMap<String, String> getQueryParameters() {
314             return queryParams;
315         }
316
317         @Override
318         public MultivaluedMap<String, String> getQueryParameters(boolean decode) {
319             return getQueryParameters();
320         }
321
322         @Override
323         public List<String> getMatchedURIs() {
324             return Collections.emptyList();
325         }
326
327         @Override
328         public List<String> getMatchedURIs(boolean decode) {
329             return getMatchedURIs();
330         }
331
332         @Override
333         public List<Object> getMatchedResources() {
334             return Collections.emptyList();
335         }
336
337         @Override
338         public URI resolve(URI uri) {
339             return uri;
340         }
341
342         @Override
343         public URI relativize(URI uri) {
344             return uri;
345         }
346     }
347 }