* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.netconf.sal.rest.impl;
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import java.io.StringReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import javax.annotation.Nonnull;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.netconf.sal.rest.api.Draft02;
import org.opendaylight.netconf.sal.rest.api.RestconfService;
import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
-import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
-import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEditOperation;
+import org.opendaylight.restconf.common.patch.PatchEntity;
+import org.opendaylight.restconf.common.util.RestUtil;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier;
import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Patch reader for JSON.
*
- * @deprecated This class will be replaced by
- * {@link org.opendaylight.restconf.jersey.providers.JsonToPatchBodyReader}
+ * @deprecated This class will be replaced by JsonToPatchBodyReader in restconf-nb-rfc8040
*/
@Deprecated
@Provider
implements MessageBodyReader<PatchContext> {
private static final Logger LOG = LoggerFactory.getLogger(JsonToPatchBodyReader.class);
- private String patchId;
+
+ public JsonToPatchBodyReader(final ControllerContext controllerContext) {
+ super(controllerContext);
+ }
@Override
public boolean isReadable(final Class<?> type, final Type genericType,
public PatchContext readFrom(final Class<PatchContext> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType,
final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream)
- throws IOException, WebApplicationException {
+ throws WebApplicationException {
try {
return readFrom(getInstanceIdentifierContext(), entityStream);
} catch (final Exception e) {
public PatchContext readFrom(final String uriPath, final InputStream entityStream) throws
RestconfDocumentedException {
try {
- return readFrom(ControllerContext.getInstance().toInstanceIdentifier(uriPath), entityStream);
+ return readFrom(getControllerContext().toInstanceIdentifier(uriPath), entityStream);
} catch (final Exception e) {
propagateExceptionAs(e);
return null; // no-op
private PatchContext readFrom(final InstanceIdentifierContext<?> path, final InputStream entityStream)
throws IOException {
- if (entityStream.available() < 1) {
+ final Optional<InputStream> nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream);
+ if (nonEmptyInputStreamOptional.isEmpty()) {
return new PatchContext(path, null, null);
}
- final JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream));
- final List<PatchEntity> resultList = read(jsonReader, path);
+ final JsonReader jsonReader = new JsonReader(new InputStreamReader(nonEmptyInputStreamOptional.get(),
+ StandardCharsets.UTF_8));
+ AtomicReference<String> patchId = new AtomicReference<>();
+ final List<PatchEntity> resultList = read(jsonReader, path, patchId);
jsonReader.close();
- return new PatchContext(path, resultList, this.patchId);
+ return new PatchContext(path, resultList, patchId.get());
}
private static RuntimeException propagateExceptionAs(final Exception exception) throws RestconfDocumentedException {
- if (exception instanceof RestconfDocumentedException) {
- throw (RestconfDocumentedException)exception;
- }
+ Throwables.throwIfInstanceOf(exception, RestconfDocumentedException.class);
+ LOG.debug("Error parsing json input", exception);
if (exception instanceof ResultAlreadySetException) {
- LOG.debug("Error parsing json input:", exception);
throw new RestconfDocumentedException("Error parsing json input: Failed to create new parse result data. ");
}
+ RestconfDocumentedException.throwIfYangError(exception);
throw new RestconfDocumentedException("Error parsing json input: " + exception.getMessage(), ErrorType.PROTOCOL,
ErrorTag.MALFORMED_MESSAGE, exception);
}
- private List<PatchEntity> read(final JsonReader in, final InstanceIdentifierContext<?> path) throws IOException {
+ private List<PatchEntity> read(final JsonReader in, final InstanceIdentifierContext<?> path,
+ final AtomicReference<String> patchId) throws IOException {
final List<PatchEntity> resultCollection = new ArrayList<>();
final StringModuleInstanceIdentifierCodec codec = new StringModuleInstanceIdentifierCodec(
path.getSchemaContext());
case END_DOCUMENT:
break;
case NAME:
- parseByName(in.nextName(), edit, in, path, codec, resultCollection);
+ parseByName(in.nextName(), edit, in, path, codec, resultCollection, patchId);
break;
case END_OBJECT:
in.endObject();
* @param resultCollection collection of parsed edits
* @throws IOException if operation fails
*/
- private void parseByName(@Nonnull final String name, @Nonnull final PatchEdit edit,
- @Nonnull final JsonReader in, @Nonnull final InstanceIdentifierContext<?> path,
- @Nonnull final StringModuleInstanceIdentifierCodec codec,
- @Nonnull final List<PatchEntity> resultCollection) throws IOException {
+ private void parseByName(final @NonNull String name, final @NonNull PatchEdit edit,
+ final @NonNull JsonReader in, final @NonNull InstanceIdentifierContext<?> path,
+ final @NonNull StringModuleInstanceIdentifierCodec codec,
+ final @NonNull List<PatchEntity> resultCollection,
+ final @NonNull AtomicReference<String> patchId) throws IOException {
switch (name) {
case "edit" :
if (in.peek() == JsonToken.BEGIN_ARRAY) {
break;
case "patch-id" :
- this.patchId = in.nextString();
+ patchId.set(in.nextString());
break;
default:
break;
* @param codec StringModuleInstanceIdentifierCodec codec
* @throws IOException if operation fails
*/
- private void readEditDefinition(@Nonnull final PatchEdit edit, @Nonnull final JsonReader in,
- @Nonnull final InstanceIdentifierContext<?> path,
- @Nonnull final StringModuleInstanceIdentifierCodec codec) throws IOException {
- final StringBuffer value = new StringBuffer();
+ private void readEditDefinition(final @NonNull PatchEdit edit, final @NonNull JsonReader in,
+ final @NonNull InstanceIdentifierContext<?> path,
+ final @NonNull StringModuleInstanceIdentifierCodec codec) throws IOException {
+ final StringBuilder value = new StringBuilder();
in.beginObject();
while (in.hasNext()) {
edit.setId(in.nextString());
break;
case "operation" :
- edit.setOperation(PatchEditOperation.valueOf(in.nextString().toUpperCase()));
+ edit.setOperation(PatchEditOperation.valueOf(in.nextString().toUpperCase(Locale.ROOT)));
break;
case "target" :
// target can be specified completely in request URI
edit.setTargetSchemaNode(path.getSchemaContext());
} else {
edit.setTarget(codec.deserialize(codec.serialize(path.getInstanceIdentifier()).concat(target)));
- edit.setTargetSchemaNode(SchemaContextUtil.findDataSchemaNode(path.getSchemaContext(),
- codec.getDataContextTree().getChild(edit.getTarget()).getDataSchemaNode().getPath()
- .getParent()));
+
+ final EffectiveStatement<?, ?> parentStmt = SchemaInferenceStack.ofInstantiatedPath(
+ path.getSchemaContext(),
+ codec.getDataContextTree().findChild(edit.getTarget()).orElseThrow().getDataSchemaNode()
+ .getPath().getParent())
+ .currentStatement();
+ verify(parentStmt instanceof SchemaNode, "Unexpected parent %s", parentStmt);
+ edit.setTargetSchemaNode((SchemaNode) parentStmt);
}
break;
* @param in JsonReader reader
* @throws IOException if operation fails
*/
- private void readValueNode(@Nonnull final StringBuffer value, @Nonnull final JsonReader in) throws IOException {
+ private void readValueNode(final @NonNull StringBuilder value, final @NonNull JsonReader in) throws IOException {
in.beginObject();
- value.append("{");
+ value.append('{');
- value.append("\"" + in.nextName() + "\"" + ":");
+ value.append('"').append(in.nextName()).append("\":");
if (in.peek() == JsonToken.BEGIN_ARRAY) {
in.beginArray();
- value.append("[");
+ value.append('[');
while (in.hasNext()) {
if (in.peek() == JsonToken.STRING) {
- value.append("\"" + in.nextString() + "\"");
+ value.append('"').append(in.nextString()).append('"');
} else {
readValueObject(value, in);
}
if (in.peek() != JsonToken.END_ARRAY) {
- value.append(",");
+ value.append(',');
}
}
in.endArray();
- value.append("]");
+ value.append(']');
} else {
readValueObject(value, in);
}
in.endObject();
- value.append("}");
+ value.append('}');
}
/**
* @param in JsonReader reader
* @throws IOException if operation fails
*/
- private void readValueObject(@Nonnull final StringBuffer value, @Nonnull final JsonReader in) throws IOException {
+ private void readValueObject(final @NonNull StringBuilder value, final @NonNull JsonReader in) throws IOException {
// read simple leaf value
if (in.peek() == JsonToken.STRING) {
- value.append("\"" + in.nextString() + "\"");
+ value.append('"').append(in.nextString()).append('"');
return;
}
in.beginObject();
- value.append("{");
+ value.append('{');
while (in.hasNext()) {
- value.append("\"" + in.nextName() + "\"");
- value.append(":");
+ value.append('"').append(in.nextName()).append("\":");
if (in.peek() == JsonToken.STRING) {
- value.append("\"" + in.nextString() + "\"");
+ value.append('"').append(in.nextString()).append('"');
} else {
if (in.peek() == JsonToken.BEGIN_ARRAY) {
in.beginArray();
- value.append("[");
+ value.append('[');
while (in.hasNext()) {
if (in.peek() == JsonToken.STRING) {
- value.append("\"" + in.nextString() + "\"");
+ value.append('"').append(in.nextString()).append('"');
} else {
readValueObject(value, in);
}
if (in.peek() != JsonToken.END_ARRAY) {
- value.append(",");
+ value.append(',');
}
}
in.endArray();
- value.append("]");
+ value.append(']');
} else {
readValueObject(value, in);
}
}
if (in.peek() != JsonToken.END_OBJECT) {
- value.append(",");
+ value.append(',');
}
}
in.endObject();
- value.append("}");
+ value.append('}');
}
/**
* @param in reader JsonReader reader
* @return NormalizedNode representing data
*/
- private static NormalizedNode<?, ?> readEditData(@Nonnull final JsonReader in,
- @Nonnull final SchemaNode targetSchemaNode, @Nonnull final InstanceIdentifierContext<?> path) {
+ private static NormalizedNode readEditData(final @NonNull JsonReader in,
+ final @NonNull SchemaNode targetSchemaNode, final @NonNull InstanceIdentifierContext<?> path) {
final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
- JsonParserStream.create(writer, path.getSchemaContext(), targetSchemaNode).parse(in);
+ final EffectiveModelContext context = path.getSchemaContext();
+ JsonParserStream.create(writer, JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(context),
+ SchemaInferenceStack.ofInstantiatedPath(context, targetSchemaNode.getPath()).toInference())
+ .parse(in);
return resultHolder.getResult();
}
* @param edit Instance of PatchEdit
* @return PatchEntity Patch entity
*/
- private static PatchEntity prepareEditOperation(@Nonnull final PatchEdit edit) {
+ private static PatchEntity prepareEditOperation(final @NonNull PatchEdit edit) {
if (edit.getOperation() != null && edit.getTargetSchemaNode() != null
&& checkDataPresence(edit.getOperation(), edit.getData() != null)) {
if (edit.getOperation().isWithValue()) {
* @return true if data is present when operation requires it or if there are no data when operation does not
* allow it, false otherwise
*/
- private static boolean checkDataPresence(@Nonnull final PatchEditOperation operation, final boolean hasData) {
+ private static boolean checkDataPresence(final @NonNull PatchEditOperation operation, final boolean hasData) {
return operation.isWithValue() == hasData;
}
private PatchEditOperation operation;
private YangInstanceIdentifier target;
private SchemaNode targetSchemaNode;
- private NormalizedNode<?, ?> data;
+ private NormalizedNode data;
public String getId() {
return this.id;
this.targetSchemaNode = targetSchemaNode;
}
- public NormalizedNode<?, ?> getData() {
+ public NormalizedNode getData() {
return this.data;
}
- public void setData(final NormalizedNode<?, ?> data) {
+ public void setData(final NormalizedNode data) {
this.data = data;
}