BUG 2412 - restconf @POST createConfigurationData method migration
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.java
index a875615172228cb629c52aeabaf0dceefa9c71b9..8fb2ea7ea72488d344a8a380544e9b6abbcf4c51 100644 (file)
@@ -33,6 +33,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
@@ -249,75 +250,105 @@ public class RestconfImpl implements RestconfService {
                 mountPoint, schemaContext), moduleContainerBuilder.build());
     }
 
-
-    @Override
-    public StructuredData getAvailableStreams(final UriInfo uriInfo) {
-        final Set<String> availableStreams = Notificator.getStreamNames();
-
-        final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
-        final Module restconfModule = getRestconfModule();
-        final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
-                Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
-        for (final String streamName : availableStreams) {
-            streamsAsData.add(toStreamCompositeNode(streamName, streamSchemaNode));
-        }
-
-        final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
-                Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
-        final QName qName = streamsSchemaNode.getQName();
-        final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
-        return new StructuredData(streamsNode, streamsSchemaNode, null, parsePrettyPrintParameter(uriInfo));
-    }
-
     @Override
-    public StructuredData getModule(final String identifier, final UriInfo uriInfo) {
+    public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
+        Preconditions.checkNotNull(identifier);
         final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
         Module module = null;
         DOMMountPoint mountPoint = null;
+        final SchemaContext schemaContext;
         if (identifier.contains(ControllerContext.MOUNT)) {
             final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
             mountPoint = mountPointIdentifier.getMountPoint();
             module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
+            schemaContext = mountPoint.getSchemaContext();
         } else {
             module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
+            schemaContext = controllerContext.getGlobalSchema();
         }
 
         if (module == null) {
-            throw new RestconfDocumentedException("Module with name '" + moduleNameAndRevision.getLocalName()
-                    + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.",
-                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
+            final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
+                    + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
+            throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
         }
 
         final Module restconfModule = getRestconfModule();
-        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
-                Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
-        final CompositeNode moduleNode = toModuleCompositeNode(module, moduleSchemaNode);
-        return new StructuredData(moduleNode, moduleSchemaNode, mountPoint, parsePrettyPrintParameter(uriInfo));
+        final Set<Module> modules = Collections.singleton(module);
+        final MapNode moduleMap = makeModuleMapNode(modules);
+
+        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
+        Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
+
+        return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint,
+                schemaContext), moduleMap);
+    }
+
+    @Override
+    public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
+        final SchemaContext schemaContext = controllerContext.getGlobalSchema();
+        final Set<String> availableStreams = Notificator.getStreamNames();
+        final Module restconfModule = getRestconfModule();
+        final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
+                Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
+        Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
+                .mapBuilder((ListSchemaNode) streamSchemaNode);
+
+        for (final String streamName : availableStreams) {
+            listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
+        }
+
+        final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
+        Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
+                Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
+        streamsContainerBuilder.withChild(listStreamsBuilder.build());
+
+
+        return new NormalizedNodeContext(new InstanceIdentifierContext(null, streamsContainerSchemaNode, null,
+                schemaContext), streamsContainerBuilder.build());
     }
 
     @Override
-    public StructuredData getOperations(final UriInfo uriInfo) {
+    public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
         final Set<Module> allModules = controllerContext.getAllModules();
-        return operationsFromModulesToStructuredData(allModules, null, parsePrettyPrintParameter(uriInfo));
+        return operationsFromModulesToNormalizedContext(allModules, null);
     }
 
     @Override
-    public StructuredData getOperations(final String identifier, final UriInfo uriInfo) {
+    public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
         Set<Module> modules = null;
         DOMMountPoint mountPoint = null;
         if (identifier.contains(ControllerContext.MOUNT)) {
             final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
             mountPoint = mountPointIdentifier.getMountPoint();
             modules = controllerContext.getAllModules(mountPoint);
+
         } else {
-            throw new RestconfDocumentedException(
-                    "URI has bad format. If operations behind mount point should be showed, URI has to end with "
-                            + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+            final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
+            throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
         }
 
-        return operationsFromModulesToStructuredData(modules, mountPoint, parsePrettyPrintParameter(uriInfo));
+        return operationsFromModulesToNormalizedContext(modules, mountPoint);
+    }
+
+    private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
+            final DOMMountPoint mountPoint) {
+
+        // FIXME find best way to change restconf-netconf yang schema for provide this functionality
+        final String errMsg = "We are not able support view operations functionality yet.";
+        throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
     }
 
+    /**
+     * @deprecated method will be removed in Lithium release
+     */
+    @Deprecated
     private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
             final DOMMountPoint mountPoint, final boolean prettyPrint) {
         final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
@@ -393,6 +424,15 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
+    /**
+     * @deprecated method will be removed for Lithium release
+     *      so, please use toStreamEntryNode method
+     *
+     * @param streamName
+     * @param streamSchemaNode
+     * @return
+     */
+    @Deprecated
     private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
         final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
         List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
@@ -427,6 +467,15 @@ public class RestconfImpl implements RestconfService {
         return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
     }
 
+    /**
+     * @deprecated method will be removed for Lithium release
+     *      so, please use toModuleEntryNode method
+     *
+     * @param module
+     * @param moduleSchemaNode
+     * @return
+     */
+    @Deprecated
     private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
         final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
         List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
@@ -479,6 +528,29 @@ public class RestconfImpl implements RestconfService {
         return callRpc(rpc, payload, parsePrettyPrintParameter(uriInfo));
     }
 
+    private void validateInput(final DataSchemaNode inputSchema, final NormalizedNodeContext payload) {
+        if (inputSchema != null && payload.getData() == null) {
+            // expected a non null payload
+            throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        } else if (inputSchema == null && payload.getData() != null) {
+            // did not expect any input
+            throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        }
+        // else
+        // {
+        // TODO: Validate "mandatory" and "config" values here??? Or should those be
+        // those be
+        // validate in a more central location inside MD-SAL core.
+        // }
+    }
+
+    /**
+     * @deprecated method will be removed for Lithium release
+     *
+     * @param inputSchema
+     * @param payload
+     */
+    @Deprecated
     private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
         if (inputSchema != null && payload == null) {
             // expected a non null payload
@@ -510,7 +582,7 @@ public class RestconfImpl implements RestconfService {
         final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
         String streamName = null;
         if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
-            final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier);
+            final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
 
             LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
                     DATASTORE_PARAM_NAME);
@@ -736,26 +808,16 @@ public class RestconfImpl implements RestconfService {
     }
 
     @Override
-    public Response updateConfigurationData(final String identifier, final Node<?> payload) {
+    public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
+        Preconditions.checkNotNull(identifier);
         final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
 
         validateInput(iiWithData.getSchemaNode(), payload);
-
-        final DOMMountPoint mountPoint = iiWithData.getMountPoint();
         validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
-        final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
-        validateListKeysEqualityInPayloadAndUri(iiWithData, value);
-        final NormalizedNode<?, ?> datastoreNormalizedNode = compositeNodeToDatastoreNormalizedNode(value,
-                iiWithData.getSchemaNode());
+        validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
 
-
-        YangInstanceIdentifier normalizedII;
-        if (mountPoint != null) {
-            normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(
-                    iiWithData.getInstanceIdentifier());
-        } else {
-            normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
-        }
+        final DOMMountPoint mountPoint = iiWithData.getMountPoint();
+        final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
 
         /*
          * There is a small window where another write transaction could be updating the same data
@@ -774,11 +836,9 @@ public class RestconfImpl implements RestconfService {
         while(true) {
             try {
                 if (mountPoint != null) {
-                    broker.commitConfigurationDataPut(mountPoint, normalizedII,
-                            datastoreNormalizedNode).checkedGet();
+                    broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
                 } else {
-                    broker.commitConfigurationDataPut(normalizedII,
-                            datastoreNormalizedNode).checkedGet();
+                    broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
                 }
 
                 break;
@@ -799,6 +859,37 @@ public class RestconfImpl implements RestconfService {
         return Response.status(Status.OK).build();
     }
 
+    private void validateTopLevelNodeName(final NormalizedNodeContext node,
+            final YangInstanceIdentifier identifier) {
+
+        final String payloadName = node.getData().getNodeType().getLocalName();
+        final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
+
+        //no arguments
+        if ( ! pathArguments.hasNext()) {
+            //no "data" payload
+            if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
+                throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        //any arguments
+        } else {
+            final String identifierName = pathArguments.next().getNodeType().getLocalName();
+            if ( ! payloadName.equals(identifierName)) {
+                throw new RestconfDocumentedException("Payload name (" + payloadName
+                        + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
+                        ErrorTag.MALFORMED_MESSAGE);
+            }
+        }
+    }
+
+    /**
+     * @deprecated method will be removed for Lithium release
+     *
+     * @param node
+     * @param identifier
+     */
+    @Deprecated
     private void validateTopLevelNodeName(final Node<?> node,
             final YangInstanceIdentifier identifier) {
         final String payloadName = getName(node);
@@ -829,6 +920,29 @@ public class RestconfImpl implements RestconfService {
      *             if key values or key count in payload and URI isn't equal
      *
      */
+    private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
+            final NormalizedNode<?, ?> payload) {
+        if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
+            final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
+            final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
+            if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
+                final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
+                        .getKeyValues();
+                isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
+            }
+        }
+    }
+
+    /**
+     * @deprecated method will be removed for Lithium release
+     *
+     * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
+     *
+     * @throws RestconfDocumentedException
+     *             if key values or key count in payload and URI isn't equal
+     *
+     */
+    @Deprecated
     private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
             final CompositeNode payload) {
         if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
@@ -842,6 +956,39 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
+    private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
+            final List<QName> keyDefinitions) {
+        for (final QName keyDefinition : keyDefinitions) {
+            final Object uriKeyValue = uriKeyValues.get(keyDefinition);
+            // should be caught during parsing URI to InstanceIdentifier
+            if (uriKeyValue == null) {
+                final String errMsg = "Missing key " + keyDefinition + " in URI.";
+                throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+            }
+            // TODO thing about the possibility to fix
+//            final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
+//            if (payloadKeyValues.isEmpty()) {
+//                final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
+//                throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+//            }
+//
+//            final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
+//            if (!uriKeyValue.equals(payloadKeyValue)) {
+//                final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
+//                        "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
+//                throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+//            }
+        }
+    }
+
+    /**
+     * @deprecated method will be removed for Lithium release
+     *
+     * @param uriKeyValues
+     * @param payload
+     * @param keyDefinitions
+     */
+    @Deprecated
     private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
             final List<QName> keyDefinitions) {
         for (final QName keyDefinition : keyDefinitions) {
@@ -868,61 +1015,30 @@ public class RestconfImpl implements RestconfService {
     }
 
     @Override
-    public Response createConfigurationData(final String identifier, final Node<?> payload) {
+    public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
         if (payload == null) {
             throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
 
-        final URI payloadNS = namespace(payload);
+        final URI payloadNS = payload.getData().getNodeType().getNamespace();
         if (payloadNS == null) {
             throw new RestconfDocumentedException(
                     "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
                     ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
         }
 
-        InstanceIdentifierContext iiWithData = null;
-        CompositeNode value = null;
-        if (representsMountPointRootData(payload)) {
-            // payload represents mount point data and URI represents path to the mount point
-
-            if (endsWithMountPoint(identifier)) {
-                throw new RestconfDocumentedException("URI has bad format. URI should be without \""
-                        + ControllerContext.MOUNT + "\" for POST operation.", ErrorType.PROTOCOL,
-                        ErrorTag.INVALID_VALUE);
-            }
-
-            final String completeIdentifier = addMountPointIdentifier(identifier);
-            iiWithData = controllerContext.toInstanceIdentifier(completeIdentifier);
-
-            value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
-        } else {
-            final InstanceIdentifierContext incompleteInstIdWithData = controllerContext
-                    .toInstanceIdentifier(identifier);
-            final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
-            final DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint();
-            final Module module = findModule(mountPoint, payload);
-
-            final String payloadName = getName(payload);
-            final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace(
-                    parentSchema, payloadName, module.getNamespace());
-            value = this.normalizeNode(payload, schemaNode, mountPoint);
+        final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
 
-            iiWithData = addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode,incompleteInstIdWithData.getSchemaContext());
-        }
-
-        final NormalizedNode<?, ?> datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value,
-                iiWithData.getSchemaNode());
-        final DOMMountPoint mountPoint = iiWithData.getMountPoint();
-        YangInstanceIdentifier normalizedII;
+        final InstanceIdentifierContext iiWithData = mountPoint != null
+                ? controllerContext.toMountPointIdentifier(identifier)
+                : controllerContext.toInstanceIdentifier(identifier);
+        final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
 
         try {
             if (mountPoint != null) {
-                normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
-                        .getInstanceIdentifier());
-                broker.commitConfigurationDataPost(mountPoint, normalizedII, datastoreNormalizedData);
+                broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
             } else {
-                normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
-                broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData);
+                broker.commitConfigurationDataPost(normalizedII, payload.getData());
             }
         } catch(final RestconfDocumentedException e) {
             throw e;
@@ -930,11 +1046,17 @@ public class RestconfImpl implements RestconfService {
             throw new RestconfDocumentedException("Error creating data", e);
         }
 
-        return Response.status(Status.NO_CONTENT).build();
+
+        final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
+        final URI location = resolveLocation(uriInfo, "config", mountPoint, normalizedII);
+        if (location != null) {
+            responseBuilder.location(location);
+        }
+        return responseBuilder.build();
     }
 
     @Override
-    public Response createConfigurationData(final Node<?> payload) {
+    public Response createConfigurationData(final Node<?> payload, final UriInfo uriInfo) {
         if (payload == null) {
             throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
@@ -947,6 +1069,11 @@ public class RestconfImpl implements RestconfService {
         }
 
         final Module module = this.findModule(null, payload);
+        if (module == null) {
+            throw new RestconfDocumentedException(
+                    "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
+                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
+        }
 
         final String payloadName = getName(payload);
         final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace(module,
@@ -973,7 +1100,24 @@ public class RestconfImpl implements RestconfService {
             throw new RestconfDocumentedException("Error creating data", e);
         }
 
-        return Response.status(Status.NO_CONTENT).build();
+        final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
+        final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
+        if (location != null) {
+            responseBuilder.location(location);
+        }
+        return responseBuilder.build();
+    }
+
+    private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
+        final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+        uriBuilder.path("config");
+        try {
+            uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
+        } catch (final Exception e) {
+            LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
+            return null;
+        }
+        return uriBuilder.build();
     }
 
     @Override
@@ -1234,7 +1378,12 @@ public class RestconfImpl implements RestconfService {
         return identifier + "/" + ControllerContext.MOUNT;
     }
 
-    private CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
+    /**
+     * @deprecated method will be removed in Lithium release
+     *      we don't wish to use Node and CompositeNode anywhere
+     */
+    @Deprecated
+    public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
         if (schema == null) {
             final String localName = node == null ? null :
                     node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
@@ -1507,6 +1656,13 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
+    /**
+     * @deprecated method will be removed for Lithium release
+     *
+     * @param data
+     * @return
+     */
+    @Deprecated
     private String getName(final Node<?> data) {
         if (data instanceof NodeWrapper) {
             return ((NodeWrapper<?>) data).getLocalName();
@@ -1655,4 +1811,49 @@ public class RestconfImpl implements RestconfService {
         Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
                 "moduleSchemaNode has to be of type ListSchemaNode");\r        final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;\r        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders\r                .mapEntryBuilder(listModuleSchemaNode);\r\r        List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(\r                (listModuleSchemaNode), "name");\r        final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);\r        Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);\r        moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())\r                .build());\r\r        instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(\r                (listModuleSchemaNode), "revision");\r        final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);\r        Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);\r        final String revision = REVISION_FORMAT.format(module.getRevision());\r        moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)\r                .build());\r\r        instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(\r                (listModuleSchemaNode), "namespace");\r        final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);\r        Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);\r        moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)\r                .withValue(module.getNamespace().toString()).build());\r\r        instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(\r                (listModuleSchemaNode), "feature");\r        final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);\r        Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);\r        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders\r                .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);\r        for (final FeatureDefinition feature : module.getFeatures()) {\r            featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))\r                    .withValue(feature.getQName().getLocalName()).build());\r        }\r        moduleNodeValues.withChild(featuresBuilder.build());
 \r        return moduleNodeValues.build();\r    }
+
+    protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
+        Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
+                "streamSchemaNode has to be of type ListSchemaNode");
+        final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
+                .mapEntryBuilder(listStreamSchemaNode);
+
+        List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
+                (listStreamSchemaNode), "name");
+        final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+        Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
+        streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
+                .build());
+
+        instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
+                (listStreamSchemaNode), "description");
+        final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+        Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
+        streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
+                .withValue("DESCRIPTION_PLACEHOLDER").build());
+
+        instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
+                (listStreamSchemaNode), "replay-support");
+        final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+        Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
+        streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
+                .withValue(Boolean.valueOf(true)).build());
+
+        instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
+                (listStreamSchemaNode), "replay-log-creation-time");
+        final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+        Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
+        streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
+                .withValue("").build());
+
+        instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
+                (listStreamSchemaNode), "events");
+        final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+        Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
+        streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
+                .withValue("").build());
+
+        return streamNodeValues.build();
+    }
 }