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.data.api.YangInstanceIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
41 import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
42 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
43 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
44 import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException;
45 import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
46 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
47 import org.opendaylight.yangtools.yang.model.api.Module;
48 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
49 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
50 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 @Consumes({Draft02.MediaTypes.PATCH + RestconfService.JSON})
56 public class JsonToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<PATCHContext> {
58 private final static Logger LOG = LoggerFactory.getLogger(JsonToPATCHBodyReader.class);
59 private String patchId;
62 public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
67 public PATCHContext readFrom(Class<PATCHContext> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
69 return readFrom(getInstanceIdentifierContext(), entityStream);
70 } catch (final Exception e) {
71 throw propagateExceptionAs(e);
75 private static RuntimeException propagateExceptionAs(Exception e) throws RestconfDocumentedException {
76 if(e instanceof RestconfDocumentedException) {
77 throw (RestconfDocumentedException)e;
80 if(e instanceof ResultAlreadySetException) {
81 LOG.debug("Error parsing json input:", e);
83 throw new RestconfDocumentedException("Error parsing json input: Failed to create new parse result data. ");
86 throw new RestconfDocumentedException("Error parsing json input: " + e.getMessage(), ErrorType.PROTOCOL,
87 ErrorTag.MALFORMED_MESSAGE, e);
90 public PATCHContext readFrom(final String uriPath, final InputStream entityStream) throws
91 RestconfDocumentedException {
93 return readFrom(ControllerContext.getInstance().toInstanceIdentifier(uriPath), entityStream);
94 } catch (final Exception e) {
95 propagateExceptionAs(e);
100 private PATCHContext readFrom(final InstanceIdentifierContext<?> path, final InputStream entityStream) throws IOException {
101 if (entityStream.available() < 1) {
102 return new PATCHContext(path, null, null);
105 final JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream));
106 final List<PATCHEntity> resultList = read(jsonReader, path);
109 return new PATCHContext(path, resultList, patchId);
112 private List<PATCHEntity> read(final JsonReader in, InstanceIdentifierContext path) throws
115 boolean inEdit = false;
116 boolean inValue = false;
117 String operation = null;
118 String target = null;
119 String editId = null;
120 List<PATCHEntity> resultCollection = new ArrayList<>();
122 while (in.hasNext()) {
129 Boolean.toString(in.nextBoolean());
138 if (inEdit && operation != null & target != null & inValue) {
139 StringModuleInstanceIdentifierCodec codec = new StringModuleInstanceIdentifierCodec(path
140 .getSchemaContext());
142 YangInstanceIdentifier targetII = codec.deserialize(codec.serialize(path
143 .getInstanceIdentifier()) + target);
144 SchemaNode targetSchemaNode = SchemaContextUtil.findDataSchemaNode(path.getSchemaContext(),
145 codec.getDataContextTree().getChild(targetII).getDataSchemaNode().getPath()
148 final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
149 final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
151 //keep on parsing json from place where target points
152 final JsonParserStream jsonParser = JsonParserStream.create(writer, path.getSchemaContext(),
154 jsonParser.parse(in);
155 resultCollection.add(new PATCHEntity(editId, operation, targetII.getParent(), resultHolder.getResult()));
168 final String name = in.nextName();
171 case "edit" : inEdit = true;
173 case "operation" : operation = in.nextString();
175 case "target" : target = in.nextString();
177 case "value" : inValue = true;
179 case "patch-id" : patchId = in.nextString();
181 case "edit-id" : editId = in.nextString();
197 return ImmutableList.copyOf(resultCollection);
200 private class StringModuleInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec {
202 private final DataSchemaContextTree dataContextTree;
203 private final SchemaContext context;
205 StringModuleInstanceIdentifierCodec(SchemaContext context) {
206 this.context = Preconditions.checkNotNull(context);
207 this.dataContextTree = DataSchemaContextTree.from(context);
211 protected Module moduleForPrefix(@Nonnull String prefix) {
212 return context.findModuleByName(prefix, null);
217 protected DataSchemaContextTree getDataContextTree() {
218 return dataContextTree;
223 protected String prefixForNamespace(@Nonnull URI namespace) {
224 final Module module = context.findModuleByNamespaceAndRevision(namespace, null);
225 return module == null ? null : module.getName();