ec7b33dc09bf8e6f12e0fd296969282ee6e5b4e4
[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.nio.charset.StandardCharsets;
18 import java.util.List;
19 import javax.ws.rs.core.MediaType;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
22 import org.opendaylight.netconf.sal.rest.impl.JsonToPatchBodyReader;
23 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
24 import org.opendaylight.netconf.sal.rest.impl.PatchJsonBodyWriter;
25 import org.opendaylight.netconf.sal.restconf.api.JSONRestconfService;
26 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
27 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
28 import org.opendaylight.restconf.common.errors.RestconfError;
29 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
30 import org.opendaylight.restconf.common.patch.PatchContext;
31 import org.opendaylight.restconf.common.patch.PatchStatusContext;
32 import org.opendaylight.restconf.common.util.SimpleUriInfo;
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 {JSONRestconfServiceRfc8040Impl from restconf-nb-rfc8040
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     @SuppressWarnings("checkstyle:IllegalCatch")
173     @Override
174     public Optional<String> patch(final String uriPath, final String payload)
175             throws OperationFailedException {
176
177         String output = null;
178         Preconditions.checkNotNull(payload, "payload can't be null");
179
180         LOG.debug("patch: uriPath: {}, payload: {}", uriPath, payload);
181
182         final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
183
184         JsonToPatchBodyReader jsonToPatchBodyReader = new JsonToPatchBodyReader();
185         final PatchContext context = jsonToPatchBodyReader.readFrom(uriPath, entityStream);
186
187         LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier());
188         LOG.debug("Parsed NormalizedNode: {}", context.getData());
189
190         try {
191             PatchStatusContext patchStatusContext = RestconfImpl.getInstance()
192                 .patchConfigurationData(context, new SimpleUriInfo(uriPath));
193             output = toJson(patchStatusContext);
194         } catch (final Exception e) {
195             propagateExceptionAs(uriPath, e, "PATCH");
196         }
197         return Optional.fromNullable(output);
198     }
199
200     @Override
201     public void close() {
202     }
203
204     private  String toJson(final PatchStatusContext patchStatusContext) throws IOException {
205         final PatchJsonBodyWriter writer = new PatchJsonBodyWriter();
206         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
207         writer.writeTo(patchStatusContext, PatchStatusContext.class, null, EMPTY_ANNOTATIONS,
208                 MediaType.APPLICATION_JSON_TYPE, null, outputStream);
209         return outputStream.toString(StandardCharsets.UTF_8.name());
210     }
211
212     private static String toJson(final NormalizedNodeContext readData) throws IOException {
213         final NormalizedNodeJsonBodyWriter writer = new NormalizedNodeJsonBodyWriter();
214         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
215         writer.writeTo(readData, NormalizedNodeContext.class, null, EMPTY_ANNOTATIONS,
216                 MediaType.APPLICATION_JSON_TYPE, null, outputStream);
217         return outputStream.toString(StandardCharsets.UTF_8.name());
218     }
219
220     private static boolean isDataMissing(final Exception exception) {
221         boolean dataMissing = false;
222         if (exception instanceof RestconfDocumentedException) {
223             final RestconfDocumentedException rde = (RestconfDocumentedException)exception;
224             if (!rde.getErrors().isEmpty()) {
225                 if (rde.getErrors().get(0).getErrorTag() == ErrorTag.DATA_MISSING) {
226                     dataMissing = true;
227                 }
228             }
229         }
230
231         return dataMissing;
232     }
233
234     private static void propagateExceptionAs(final String uriPath, final Exception exception, final String operation)
235             throws OperationFailedException {
236         LOG.debug("Error for uriPath: {}", uriPath, exception);
237
238         if (exception instanceof RestconfDocumentedException) {
239             throw new OperationFailedException(String.format(
240                     "%s failed for URI %s", operation, uriPath), exception.getCause(),
241                     toRpcErrors(((RestconfDocumentedException)exception).getErrors()));
242         }
243
244         throw new OperationFailedException(String.format("%s failed for URI %s", operation, uriPath), exception);
245     }
246
247     private static RpcError[] toRpcErrors(final List<RestconfError> from) {
248         final RpcError[] to = new RpcError[from.size()];
249         int index = 0;
250         for (final RestconfError e: from) {
251             to[index++] = RpcResultBuilder.newError(toRpcErrorType(e.getErrorType()), e.getErrorTag().getTagValue(),
252                     e.getErrorMessage());
253         }
254
255         return to;
256     }
257
258     private static ErrorType toRpcErrorType(final RestconfError.ErrorType errorType) {
259         switch (errorType) {
260             case TRANSPORT: {
261                 return ErrorType.TRANSPORT;
262             }
263             case RPC: {
264                 return ErrorType.RPC;
265             }
266             case PROTOCOL: {
267                 return ErrorType.PROTOCOL;
268             }
269             default: {
270                 return ErrorType.APPLICATION;
271             }
272         }
273     }
274 }