2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netconf.sal.rest.impl;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableList;
13 import com.google.gson.stream.JsonReader;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.io.InputStreamReader;
17 import java.lang.annotation.Annotation;
18 import java.lang.reflect.Type;
20 import java.util.ArrayList;
21 import java.util.List;
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24 import javax.ws.rs.Consumes;
25 import javax.ws.rs.WebApplicationException;
26 import javax.ws.rs.core.MediaType;
27 import javax.ws.rs.core.MultivaluedMap;
28 import javax.ws.rs.ext.MessageBodyReader;
29 import javax.ws.rs.ext.Provider;
30 import org.opendaylight.netconf.sal.rest.api.Draft02;
31 import org.opendaylight.netconf.sal.rest.api.RestconfService;
32 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
33 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
34 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
35 import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
36 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
37 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
38 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
42 import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
43 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
44 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
45 import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException;
46 import org.opendaylight.yangtools.yang.data.util.AbstractStringInstanceIdentifierCodec;
47 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
48 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
49 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.Module;
51 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 @Consumes({Draft02.MediaTypes.PATCH + RestconfService.JSON})
57 public class JsonToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<PATCHContext> {
59 private final static Logger LOG = LoggerFactory.getLogger(JsonToPATCHBodyReader.class);
60 private String patchId;
63 public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
68 public PATCHContext readFrom(Class<PATCHContext> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
70 return readFrom(getInstanceIdentifierContext(), entityStream);
71 } catch (final Exception e) {
72 throw propagateExceptionAs(e);
76 private static RuntimeException propagateExceptionAs(Exception e) throws RestconfDocumentedException {
77 if(e instanceof RestconfDocumentedException) {
78 throw (RestconfDocumentedException)e;
81 if(e instanceof ResultAlreadySetException) {
82 LOG.debug("Error parsing json input:", e);
84 throw new RestconfDocumentedException("Error parsing json input: Failed to create new parse result data. ");
87 throw new RestconfDocumentedException("Error parsing json input: " + e.getMessage(), ErrorType.PROTOCOL,
88 ErrorTag.MALFORMED_MESSAGE, e);
91 public PATCHContext readFrom(final String uriPath, final InputStream entityStream) throws
92 RestconfDocumentedException {
94 return readFrom(ControllerContext.getInstance().toInstanceIdentifier(uriPath), entityStream);
95 } catch (final Exception e) {
96 propagateExceptionAs(e);
101 private PATCHContext readFrom(final InstanceIdentifierContext<?> path, final InputStream entityStream) throws IOException {
102 if (entityStream.available() < 1) {
103 return new PATCHContext(path, null, null);
106 final JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream));
107 final List<PATCHEntity> resultList = read(jsonReader, path);
110 return new PATCHContext(path, resultList, patchId);
113 private List<PATCHEntity> read(final JsonReader in, InstanceIdentifierContext path) throws
116 boolean inEdit = false;
117 boolean inValue = false;
118 String operation = null;
119 String target = null;
120 String editId = null;
121 List<PATCHEntity> resultCollection = new ArrayList<>();
123 while (in.hasNext()) {
130 Boolean.toString(in.nextBoolean());
139 if (inEdit && operation != null & target != null & inValue) {
140 //let's do the stuff - find out target node
141 // StringInstanceIdentifierCodec codec = new StringInstanceIdentifierCodec(path
142 // .getSchemaContext());
143 // if (path.getInstanceIdentifier().toString().equals("/")) {
144 // final YangInstanceIdentifier deserialized = codec.deserialize(target);
146 DataSchemaNode targetNode = ((DataNodeContainer)(path.getSchemaNode())).getDataChildByName
147 (target.replace("/", ""));
148 if (targetNode == null) {
149 LOG.debug("Target node {} not found in path {} ", target, path.getSchemaNode());
150 throw new RestconfDocumentedException("Error parsing input", ErrorType.PROTOCOL,
151 ErrorTag.MALFORMED_MESSAGE);
154 final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
155 final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
157 //keep on parsing json from place where target points
158 final JsonParserStream jsonParser = JsonParserStream.create(writer, path.getSchemaContext(),
159 path.getSchemaNode());
160 jsonParser.parse(in);
162 final YangInstanceIdentifier targetII = path.getInstanceIdentifier().node(targetNode.getQName());
163 resultCollection.add(new PATCHEntity(editId, operation, targetII, resultHolder.getResult
178 final String name = in.nextName();
181 case "edit" : inEdit = true;
183 case "operation" : operation = in.nextString();
185 case "target" : target = in.nextString();
187 case "value" : inValue = true;
189 case "patch-id" : patchId = in.nextString();
191 case "edit-id" : editId = in.nextString();
207 return ImmutableList.copyOf(resultCollection);
210 private class StringInstanceIdentifierCodec extends AbstractStringInstanceIdentifierCodec {
212 private final DataSchemaContextTree dataContextTree;
213 private final SchemaContext context;
215 StringInstanceIdentifierCodec(SchemaContext context) {
216 this.context = Preconditions.checkNotNull(context);
217 this.dataContextTree = DataSchemaContextTree.from(context);
222 protected DataSchemaContextTree getDataContextTree() {
223 return dataContextTree;
228 protected String prefixForNamespace(@Nonnull URI namespace) {
229 final Module module = context.findModuleByNamespaceAndRevision(namespace, null);
230 return module == null ? null : module.getName();
235 protected QName createQName(@Nonnull String prefix, @Nonnull String localName) {
236 throw new UnsupportedOperationException("Not implemented");