package org.opendaylight.restconf.jersey.providers;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.isPatchOperationWithValue;
-
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
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.opendaylight.netconf.sal.restconf.impl.ControllerContext;
import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
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.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
@Provider
@Consumes({Rfc8040.MediaTypes.PATCH + RestconfConstants.JSON})
-public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
- implements MessageBodyReader<PatchContext> {
-
+public class JsonToPatchBodyReader extends AbstractToPatchBodyReader {
private static final Logger LOG = LoggerFactory.getLogger(JsonToPatchBodyReader.class);
- private String patchId;
- @Override
- public boolean isReadable(final Class<?> type, final Type genericType,
- final Annotation[] annotations, final MediaType mediaType) {
- return true;
- }
+ private String patchId;
@SuppressWarnings("checkstyle:IllegalCatch")
@Override
- public PatchContext readFrom(final Class<PatchContext> type, final Type genericType,
- final Annotation[] annotations, final MediaType mediaType,
- final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream)
+ protected PatchContext readBody(final InstanceIdentifierContext<?> path, final InputStream entityStream)
throws IOException, WebApplicationException {
try {
- return readFrom(getInstanceIdentifierContext(), entityStream);
+ return readFrom(path, entityStream);
} catch (final Exception e) {
throw propagateExceptionAs(e);
}
private PatchContext readFrom(final InstanceIdentifierContext<?> path, final InputStream entityStream)
throws IOException {
- if (entityStream.available() < 1) {
- return new PatchContext(path, null, null);
- }
-
final JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream));
final List<PatchEntity> resultList = read(jsonReader, path);
jsonReader.close();
@Nonnull final StringModuleInstanceIdentifierCodec codec,
@Nonnull final List<PatchEntity> resultCollection) throws IOException {
switch (name) {
- case "edit" :
+ case "edit":
if (in.peek() == JsonToken.BEGIN_ARRAY) {
in.beginArray();
}
break;
- case "patch-id" :
+ case "patch-id":
this.patchId = in.nextString();
break;
default:
private void readEditDefinition(@Nonnull final PatchEdit edit, @Nonnull final JsonReader in,
@Nonnull final InstanceIdentifierContext<?> path,
@Nonnull final StringModuleInstanceIdentifierCodec codec) throws IOException {
- final StringBuilder sb = new StringBuilder();
+ String deferredValue = null;
in.beginObject();
while (in.hasNext()) {
final String editDefinition = in.nextName();
switch (editDefinition) {
- case "edit-id" :
+ case "edit-id":
edit.setId(in.nextString());
break;
- case "operation" :
- edit.setOperation(in.nextString());
+ case "operation":
+ edit.setOperation(PatchEditOperation.valueOf(in.nextString().toUpperCase()));
break;
- case "target" :
+ case "target":
// target can be specified completely in request URI
final String target = in.nextString();
if (target.equals("/")) {
}
break;
- case "value" :
- // save data defined in value node for next (later) processing, because target needs to be read
- // always first and there is no ordering in Json input
- readValueNode(sb, in);
+ case "value":
+ Preconditions.checkArgument(edit.getData() == null && deferredValue == null,
+ "Multiple value entries found");
+
+ if (edit.getTargetSchemaNode() == null) {
+ final StringBuilder sb = new StringBuilder();
+
+ // save data defined in value node for next (later) processing, because target needs to be read
+ // always first and there is no ordering in Json input
+ readValueNode(sb, in);
+ deferredValue = sb.toString();
+ } else {
+ // We have a target schema node, reuse this reader without buffering the value.
+ edit.setData(readEditData(in, edit.getTargetSchemaNode(), path));
+ }
break;
default:
+ // FIXME: this does not look right, as it can wreck our logic
break;
}
}
in.endObject();
- // read saved data to normalized node when target schema is already known
- edit.setData(
- readEditData(new JsonReader(new StringReader(sb.toString())), edit.getTargetSchemaNode(), path));
+ if (deferredValue != null) {
+ // read saved data to normalized node when target schema is already known
+ edit.setData(readEditData(new JsonReader(new StringReader(deferredValue)), edit.getTargetSchemaNode(),
+ path));
+ }
}
/**
private static PatchEntity prepareEditOperation(@Nonnull final PatchEdit edit) {
if (edit.getOperation() != null && edit.getTargetSchemaNode() != null
&& checkDataPresence(edit.getOperation(), edit.getData() != null)) {
- if (!isPatchOperationWithValue(edit.getOperation())) {
+ if (!edit.getOperation().isWithValue()) {
return new PatchEntity(edit.getId(), edit.getOperation(), edit.getTarget());
}
* @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 String operation, final boolean hasData) {
- return isPatchOperationWithValue(operation) == hasData;
+ private static boolean checkDataPresence(@Nonnull final PatchEditOperation operation, final boolean hasData) {
+ return operation.isWithValue() == hasData;
}
/**
*/
private static final class PatchEdit {
private String id;
- private String operation;
+ private PatchEditOperation operation;
private YangInstanceIdentifier target;
private SchemaNode targetSchemaNode;
private NormalizedNode<?, ?> data;
- public String getId() {
+ String getId() {
return id;
}
- public void setId(final String id) {
- this.id = id;
+ void setId(final String id) {
+ this.id = Preconditions.checkNotNull(id);
}
- public String getOperation() {
+ PatchEditOperation getOperation() {
return operation;
}
- public void setOperation(final String operation) {
- this.operation = operation;
+ void setOperation(final PatchEditOperation operation) {
+ this.operation = Preconditions.checkNotNull(operation);
}
- public YangInstanceIdentifier getTarget() {
+ YangInstanceIdentifier getTarget() {
return target;
}
- public void setTarget(final YangInstanceIdentifier target) {
- this.target = target;
+ void setTarget(final YangInstanceIdentifier target) {
+ this.target = Preconditions.checkNotNull(target);
}
- public SchemaNode getTargetSchemaNode() {
+ SchemaNode getTargetSchemaNode() {
return targetSchemaNode;
}
- public void setTargetSchemaNode(final SchemaNode targetSchemaNode) {
- this.targetSchemaNode = targetSchemaNode;
+ void setTargetSchemaNode(final SchemaNode targetSchemaNode) {
+ this.targetSchemaNode = Preconditions.checkNotNull(targetSchemaNode);
}
- public NormalizedNode<?, ?> getData() {
+ NormalizedNode<?, ?> getData() {
return data;
}
- public void setData(final NormalizedNode<?, ?> data) {
- this.data = data;
+ void setData(final NormalizedNode<?, ?> data) {
+ this.data = Preconditions.checkNotNull(data);
}
- public void clear() {
+ void clear() {
id = null;
operation = null;
target = null;