Merge "Bug 1073: Added Transaction Chain support to InMemoryDataTreeModification."
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.java
index 2e198ec053d64e018c62224fea94f46167e2b300..c0ce90e15dde780a18e40e0e216c78059c9183fc 100644 (file)
@@ -1,5 +1,6 @@
 /**
  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2014 Brocade Communication Systems, Inc.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -7,19 +8,13 @@
  */
 package org.opendaylight.controller.sal.restconf.impl;
 
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
 import java.net.URI;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -31,25 +26,22 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 
+import org.apache.commons.lang3.StringUtils;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
-import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
-import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
-import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
-import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
-import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
-import org.opendaylight.controller.sal.restconf.impl.RestCodec;
-import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.StructuredData;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
+import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
+import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.controller.sal.streams.listeners.Notificator;
 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
@@ -62,7 +54,6 @@ import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
-import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
@@ -76,34 +67,22 @@ import org.opendaylight.yangtools.yang.model.util.EmptyType;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
 
-@SuppressWarnings("all")
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
 public class RestconfImpl implements RestconfService {
     private final static RestconfImpl INSTANCE = new RestconfImpl();
 
+    private static final int CHAR_NOT_FOUND = -1;
+
     private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
 
     private final static SimpleDateFormat REVISION_FORMAT =  new SimpleDateFormat("yyyy-MM-dd");
 
-    private final static String RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19";
-
-    private final static String RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules";
-
-    private final static String RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module";
-
-    private final static String RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE = "streams";
-
-    private final static String RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE = "stream";
-
-    private final static String RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations";
-
     private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
 
     private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
@@ -132,8 +111,8 @@ public class RestconfImpl implements RestconfService {
         final Module restconfModule = this.getRestconfModule();
 
         final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
-        final DataSchemaNode moduleSchemaNode =
-                this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                        restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
 
         Set<Module> allModules = this.controllerContext.getAllModules();
         for (final Module module : allModules) {
@@ -141,8 +120,8 @@ public class RestconfImpl implements RestconfService {
             modulesAsData.add(moduleCompositeNode);
         }
 
-        final DataSchemaNode modulesSchemaNode =
-                this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                   restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
         QName qName = modulesSchemaNode.getQName();
         final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
         return new StructuredData(modulesNode, modulesSchemaNode, null);
@@ -154,14 +133,14 @@ public class RestconfImpl implements RestconfService {
 
         final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode streamSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE);
+        final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                             restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
         for (final String streamName : availableStreams) {
             streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
         }
 
-        final DataSchemaNode streamsSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                     restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
         QName qName = streamsSchemaNode.getQName();
         final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
         return new StructuredData(streamsNode, streamsSchemaNode, null);
@@ -178,22 +157,22 @@ public class RestconfImpl implements RestconfService {
             modules = this.controllerContext.getAllModules(mountPoint);
         }
         else {
-            throw new ResponseException(Status.BAD_REQUEST,
-                "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
-                ControllerContext.MOUNT);
+            throw new RestconfDocumentedException(
+                    "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
+                    ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode moduleSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                         restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
 
         for (final Module module : modules) {
             modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
         }
 
-        final DataSchemaNode modulesSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                  restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
         QName qName = modulesSchemaNode.getQName();
         final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
         return new StructuredData(modulesNode, modulesSchemaNode, mountPoint);
@@ -215,14 +194,15 @@ public class RestconfImpl implements RestconfService {
         }
 
         if (module == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
+            throw new RestconfDocumentedException(
                     "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
-                    moduleNameAndRevision.getRevision() + "' was not found.");
+                    moduleNameAndRevision.getRevision() + "' was not found.",
+                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
         }
 
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode moduleSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                          restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
         final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
         return new StructuredData(moduleNode, moduleSchemaNode, mountPoint);
     }
@@ -244,9 +224,9 @@ public class RestconfImpl implements RestconfService {
             modules = this.controllerContext.getAllModules(mountPoint);
         }
         else {
-            throw new ResponseException(Status.BAD_REQUEST,
-                "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
-            ControllerContext.MOUNT);
+            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 );
         }
 
         return this.operationsFromModulesToStructuredData(modules, mountPoint);
@@ -256,12 +236,12 @@ public class RestconfImpl implements RestconfService {
                                                                  final MountInstance mountPoint) {
         final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode operationsSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                              restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
         QName qName = operationsSchemaNode.getQName();
         SchemaPath path = operationsSchemaNode.getPath();
         ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
-                             new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, qName, path);
+                             new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
         final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
         for (final Module module : modules) {
             Set<RpcDefinition> rpcs = module.getRpcs();
@@ -289,11 +269,11 @@ public class RestconfImpl implements RestconfService {
     }
 
     private Module getRestconfModule() {
-        QName qName = QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
-                                   RESTCONF_MODULE_DRAFT02_NAME);
-        final Module restconfModule = this.controllerContext.findModuleByNameAndRevision(qName);
+        Module restconfModule = controllerContext.getRestconfModule();
         if (restconfModule == null) {
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR, "Restconf module was not found.");
+            throw new RestconfDocumentedException(
+                    "ietf-restconf module was not found.", ErrorType.APPLICATION,
+                    ErrorTag.OPERATION_NOT_SUPPORTED );
         }
 
         return restconfModule;
@@ -313,8 +293,9 @@ public class RestconfImpl implements RestconfService {
         Iterable<String> split = splitter.split(moduleNameAndRevision);
         final List<String> pathArgs = Lists.<String>newArrayList(split);
         if (pathArgs.size() < 2) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'");
+            throw new RestconfDocumentedException(
+                    "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         try {
@@ -324,7 +305,9 @@ public class RestconfImpl implements RestconfService {
             return QName.create(null, moduleRevision, moduleName);
         }
         catch (ParseException e) {
-            throw new ResponseException(Status.BAD_REQUEST, "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'");
+            throw new RestconfDocumentedException(
+                    "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
     }
 
@@ -396,66 +379,6 @@ public class RestconfImpl implements RestconfService {
         return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
     }
 
-    private DataSchemaNode getSchemaNode(final Module restconfModule, final String schemaNodeName) {
-        Set<GroupingDefinition> groupings = restconfModule.getGroupings();
-
-        final Predicate<GroupingDefinition> filter = new Predicate<GroupingDefinition>() {
-            @Override
-            public boolean apply(final GroupingDefinition g) {
-                return Objects.equal(g.getQName().getLocalName(),
-                                     RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE);
-            }
-        };
-
-        Iterable<GroupingDefinition> filteredGroups = Iterables.filter(groupings, filter);
-
-        final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null);
-
-        List<DataSchemaNode> instanceDataChildrenByName =
-                this.controllerContext.findInstanceDataChildrenByName(restconfGrouping,
-                                                            RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE);
-        final DataSchemaNode restconfContainer = Iterables.getFirst(instanceDataChildrenByName, null);
-
-        if (Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                    RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                   RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                   RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
-            final DataSchemaNode modules = Iterables.getFirst(instances, null);
-            instances = this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) modules),
-                                                               RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                         RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                         RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
-            final DataSchemaNode modules = Iterables.getFirst(instances, null);
-            instances = this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) modules),
-                                                                 RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-
-        return null;
-    }
-
     @Override
     public Object getRoot() {
         return null;
@@ -463,109 +386,183 @@ public class RestconfImpl implements RestconfService {
 
     @Override
     public StructuredData invokeRpc(final String identifier, final CompositeNode payload) {
-        final RpcDefinition rpc = this.resolveIdentifierInInvokeRpc(identifier);
-        if (Objects.equal(rpc.getQName().getNamespace().toString(), SAL_REMOTE_NAMESPACE) &&
-            Objects.equal(rpc.getQName().getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
+        final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier);
+        QName rpcName = rpc.getRpcDefinition().getQName();
+        URI rpcNamespace = rpcName.getNamespace();
+        if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
+            Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
+            return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition());
+        }
 
-            final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
-            final SimpleNode<? extends Object> pathNode = value == null ? null :
-                                   value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
-            final Object pathValue = pathNode == null ? null : pathNode.getValue();
+        validateInput( rpc.getRpcDefinition().getInput(), payload );
 
-            if (!(pathValue instanceof InstanceIdentifier)) {
-                throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
-                                             "Instance identifier was not normalized correctly.");
-            }
+        return callRpc(rpc, payload);
+    }
 
-            final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
-            String streamName = null;
-            if (!Iterables.isEmpty(pathIdentifier.getPath())) {
-                String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
-                streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
-            }
+    private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) {
+        if( inputSchema != null && payload == null )
+        {
+            //expected a non null payload
+            throw new RestconfDocumentedException( "Input is required.",
+                                                   ErrorType.PROTOCOL,
+                                                   ErrorTag.MALFORMED_MESSAGE );
+        }
+        else if( inputSchema == null && payload != 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
+        // validate in a more central location inside MD-SAL core.
+        //}
+    }
 
-            if (Strings.isNullOrEmpty(streamName)) {
-                throw new ResponseException(Status.BAD_REQUEST,
-                         "Path is empty or contains data node which is not Container or List build-in type.");
-            }
+    private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
+                                                          final RpcDefinition rpc) {
+        final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
+        final SimpleNode<? extends Object> pathNode = value == null ? null :
+                               value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
+        final Object pathValue = pathNode == null ? null : pathNode.getValue();
+
+        if (!(pathValue instanceof InstanceIdentifier)) {
+            throw new RestconfDocumentedException(
+                    "Instance identifier was not normalized correctly.",
+                    ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED );
+        }
 
-            final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
-                                 QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
-            final List<Node<?>> output = new ArrayList<Node<?>>();
-            output.add(streamNameNode);
+        final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
+        String streamName = null;
+        if (!Iterables.isEmpty(pathIdentifier.getPath())) {
+            String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
+            streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
+        }
 
-            final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
-                                                  rpc.getOutput().getQName(), null, output, null, null);
+        if (Strings.isNullOrEmpty(streamName)) {
+            throw new RestconfDocumentedException(
+                    "Path is empty or contains data node which is not Container or List build-in type.",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+        }
 
-            if (!Notificator.existListenerFor(pathIdentifier)) {
-                Notificator.createListener(pathIdentifier, streamName);
-            }
+        final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
+                             QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
+        final List<Node<?>> output = new ArrayList<Node<?>>();
+        output.add(streamNameNode);
+
+        final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
+                                              rpc.getOutput().getQName(), null, output, null, null);
 
-            return new StructuredData(responseData, rpc.getOutput(), null);
+        if (!Notificator.existListenerFor(pathIdentifier)) {
+            Notificator.createListener(pathIdentifier, streamName);
         }
 
-        RpcDefinition rpcDefinition = this.controllerContext.getRpcDefinition(identifier);
-        return this.callRpc(rpcDefinition, payload);
+        return new StructuredData(responseData, rpc.getOutput(), null);
     }
 
     @Override
     public StructuredData invokeRpc(final String identifier, final String noPayload) {
-        if (!Strings.isNullOrEmpty(noPayload)) {
-            throw new ResponseException(Status.UNSUPPORTED_MEDIA_TYPE,
-                                                       "Content-Type contains unsupported Media Type.");
+        if (StringUtils.isNotBlank(noPayload)) {
+            throw new RestconfDocumentedException(
+                    "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
-
-        final RpcDefinition rpc = this.resolveIdentifierInInvokeRpc(identifier);
-        return this.callRpc(rpc, null);
+        return invokeRpc( identifier, (CompositeNode)null );
     }
 
-    private RpcDefinition resolveIdentifierInInvokeRpc(final String identifier) {
-        if (identifier.indexOf("/") < 0) {
-            final String identifierDecoded = this.controllerContext.urlPathArgDecode(identifier);
-            final RpcDefinition rpc = this.controllerContext.getRpcDefinition(identifierDecoded);
-            if (rpc != null) {
-                return rpc;
-            }
+    private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
+        String identifierEncoded = null;
+        MountInstance mountPoint = null;
+        if (identifier.contains(ControllerContext.MOUNT)) {
+            // mounted RPC call - look up mount instance.
+            InstanceIdWithSchemaNode mountPointId = controllerContext
+                    .toMountPointIdentifier(identifier);
+            mountPoint = mountPointId.getMountPoint();
+
+            int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
+                    + ControllerContext.MOUNT.length() + 1;
+            String remoteRpcName = identifier.substring(startOfRemoteRpcName);
+            identifierEncoded = remoteRpcName;
+
+        } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
+            final String slashErrorMsg = String
+                    .format("Identifier %n%s%ncan\'t contain slash "
+                            + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
+                            identifier);
+            throw new RestconfDocumentedException(
+                    slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+        } else {
+            identifierEncoded = identifier;
+        }
+
+        final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
+        RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
 
-            throw new ResponseException(Status.NOT_FOUND, "RPC does not exist.");
+        if (rpc == null) {
+            throw new RestconfDocumentedException(
+                    "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
         }
 
-        final String slashErrorMsg = String.format(
-                "Identifier %n%s%ncan\'t contain slash character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
-                identifier);
+        if (mountPoint == null) {
+            return new BrokerRpcExecutor(rpc, broker);
+        } else {
+            return new MountPointRpcExecutor(rpc, mountPoint);
+        }
 
-        throw new ResponseException(Status.NOT_FOUND, slashErrorMsg);
     }
 
-    private StructuredData callRpc(final RpcDefinition rpc, final CompositeNode payload) {
-        if (rpc == null) {
-            throw new ResponseException(Status.NOT_FOUND, "RPC does not exist.");
+    private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload) {
+        if (rpcExecutor == null) {
+            throw new RestconfDocumentedException(
+                    "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
         }
 
         CompositeNode rpcRequest = null;
+        RpcDefinition rpc = rpcExecutor.getRpcDefinition();
+        QName rpcName = rpc.getQName();
+
         if (payload == null) {
-            rpcRequest = NodeFactory.createMutableCompositeNode(rpc.getQName(), null, null, null, null);
-        }
-        else {
+            rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
+        } else {
             final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
-            final List<Node<?>> input = new ArrayList<Node<?>>();
-            input.add(value);
-
-            rpcRequest = NodeFactory.createMutableCompositeNode(rpc.getQName(), null, input, null, null);
+            List<Node<?>> input = Collections.<Node<?>> singletonList(value);
+            rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
         }
 
-        final RpcResult<CompositeNode> rpcResult = broker.invokeRpc(rpc.getQName(), rpcRequest);
+        RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
 
-        if (!rpcResult.isSuccessful()) {
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR, "Operation failed");
-        }
+        checkRpcSuccessAndThrowException(rpcResult);
 
-        CompositeNode result = rpcResult.getResult();
-        if (result == null) {
+        if (rpcResult.getResult() == null) {
             return null;
         }
 
-        return new StructuredData(result, rpc.getOutput(), null);
+        if( rpc.getOutput() == null )
+        {
+            return null; //no output, nothing to send back.
+        }
+
+        return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null);
+    }
+
+    private void checkRpcSuccessAndThrowException(RpcResult<CompositeNode> rpcResult) {
+        if (rpcResult.isSuccessful() == false) {
+
+            Collection<RpcError> rpcErrors = rpcResult.getErrors();
+            if( rpcErrors == null || rpcErrors.isEmpty() ) {
+                throw new RestconfDocumentedException(
+                    "The operation was not successful and there were no RPC errors returned",
+                    ErrorType.RPC, ErrorTag.OPERATION_FAILED );
+            }
+
+            List<RestconfError> errorList = Lists.newArrayList();
+            for( RpcError rpcError: rpcErrors ) {
+                errorList.add( new RestconfError( rpcError ) );
+            }
+
+            throw new RestconfDocumentedException( errorList );
+        }
     }
 
     @Override
@@ -601,6 +598,9 @@ public class RestconfImpl implements RestconfService {
     @Override
     public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
+
+        validateInput(iiWithData.getSchemaNode(), payload);
+
         MountInstance mountPoint = iiWithData.getMountPoint();
         final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
         RpcResult<TransactionStatus> status = null;
@@ -614,7 +614,7 @@ public class RestconfImpl implements RestconfService {
             }
         }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error updating data" );
+            throw new RestconfDocumentedException( "Error updating data", e );
         }
 
         if( status.getResult() == TransactionStatus.COMMITED )
@@ -625,10 +625,17 @@ public class RestconfImpl implements RestconfService {
 
     @Override
     public Response createConfigurationData(final String identifier, final CompositeNode payload) {
+        if( payload == null ) {
+            throw new RestconfDocumentedException( "Input is required.",
+                    ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE );
+        }
+
         URI payloadNS = this.namespace(payload);
         if (payloadNS == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
+            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 );
         }
 
         InstanceIdWithSchemaNode iiWithData = null;
@@ -637,9 +644,10 @@ public class RestconfImpl implements RestconfService {
              // payload represents mount point data and URI represents path to the mount point
 
             if (this.endsWithMountPoint(identifier)) {
-                throw new ResponseException(Status.BAD_REQUEST,
-                            "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
-                            "\" for POST operation.");
+                throw new RestconfDocumentedException(
+                        "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
+                        "\" for POST operation.",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
 
             final String completeIdentifier = this.addMountPointIdentifier(identifier);
@@ -654,8 +662,9 @@ public class RestconfImpl implements RestconfService {
             MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
             final Module module = this.findModule(mountPoint, payload);
             if (module == null) {
-                throw new ResponseException(Status.BAD_REQUEST,
-                                            "Module was not found for \"" + payloadNS + "\"");
+                throw new RestconfDocumentedException(
+                        "Module was not found for \"" + payloadNS + "\"",
+                        ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
             }
 
             String payloadName = this.getName(payload);
@@ -682,7 +691,7 @@ public class RestconfImpl implements RestconfService {
             }
         }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error creating data" );
+            throw new RestconfDocumentedException( "Error creating data", e );
         }
 
         if (status == null) {
@@ -697,16 +706,24 @@ public class RestconfImpl implements RestconfService {
 
     @Override
     public Response createConfigurationData(final CompositeNode payload) {
+        if( payload == null ) {
+            throw new RestconfDocumentedException( "Input is required.",
+                    ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE );
+        }
+
         URI payloadNS = this.namespace(payload);
         if (payloadNS == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
+            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 );
         }
 
         final Module module = this.findModule(null, payload);
         if (module == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
+            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 );
         }
 
         String payloadName = this.getName(payload);
@@ -731,7 +748,7 @@ public class RestconfImpl implements RestconfService {
             }
         }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error creating data" );
+            throw new RestconfDocumentedException( "Error creating data", e );
         }
 
         if (status == null) {
@@ -760,7 +777,7 @@ public class RestconfImpl implements RestconfService {
             }
         }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error creating data" );
+            throw new RestconfDocumentedException( "Error creating data", e );
         }
 
         if( status.getResult() == TransactionStatus.COMMITED )
@@ -773,12 +790,14 @@ public class RestconfImpl implements RestconfService {
     public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
         final String streamName = Notificator.createStreamNameFromUri(identifier);
         if (Strings.isNullOrEmpty(streamName)) {
-            throw new ResponseException(Status.BAD_REQUEST, "Stream name is empty.");
+            throw new RestconfDocumentedException(
+                    "Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         final ListenerAdapter listener = Notificator.getListenerFor(streamName);
         if (listener == null) {
-            throw new ResponseException(Status.BAD_REQUEST, "Stream was not found.");
+            throw new RestconfDocumentedException(
+                    "Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
         }
 
         broker.registerToListenDataChanges(listener);
@@ -882,9 +901,10 @@ public class RestconfImpl implements RestconfService {
             }
 
             if (dataNodeKeyValueObject == null) {
-                throw new ResponseException(Status.BAD_REQUEST,
-                            "Data contains list \"" + dataNode.getNodeType().getLocalName() +
-                            "\" which does not contain key: \"" + key.getLocalName() + "\"");
+                throw new RestconfDocumentedException(
+                        "Data contains list \"" + dataNode.getNodeType().getLocalName() +
+                        "\" which does not contain key: \"" + key.getLocalName() + "\"",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
 
             keyValues.put(key, dataNodeKeyValueObject);
@@ -919,14 +939,16 @@ public class RestconfImpl implements RestconfService {
         if (schema == null) {
             QName nodeType = node == null ? null : node.getNodeType();
             String localName = nodeType == null ? null : nodeType.getLocalName();
-            String _plus = ("Data schema node was not found for " + localName);
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
-                                        "Data schema node was not found for " + localName );
+
+            throw new RestconfDocumentedException(
+                    "Data schema node was not found for " + localName,
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         if (!(schema instanceof DataNodeContainer)) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                                        "Root element has to be container or list yang datatype.");
+            throw new RestconfDocumentedException(
+                    "Root element has to be container or list yang datatype.",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         if ((node instanceof CompositeNodeWrapper)) {
@@ -935,8 +957,9 @@ public class RestconfImpl implements RestconfService {
                 try {
                     this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
                 }
-                catch (NumberFormatException e) {
-                    throw new ResponseException(Status.BAD_REQUEST, e.getMessage());
+                catch (IllegalArgumentException e) {
+                    throw new RestconfDocumentedException(
+                                    e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                 }
             }
 
@@ -950,9 +973,10 @@ public class RestconfImpl implements RestconfService {
                                final DataSchemaNode schema, final QName previousAugment,
                                final MountInstance mountPoint) {
         if (schema == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                                        "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
-                                        "\" does not exist in yang schema.");
+            throw new RestconfDocumentedException(
+                    "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
+                    "\" does not exist in yang schema.",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         QName currentAugment = null;
@@ -962,13 +986,14 @@ public class RestconfImpl implements RestconfService {
         else {
             currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
             if (nodeBuilder.getQname() == null) {
-                throw new ResponseException(Status.BAD_REQUEST,
+                throw new RestconfDocumentedException(
                         "Data has bad format.\nIf data is in XML format then namespace for \"" +
                         nodeBuilder.getLocalName() +
                         "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
                         "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
                          "\" should be corresponding to namespace \"" +
-                        schema.getQName().getNamespace() + "\".");
+                        schema.getQName().getNamespace() + "\".",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
         }
 
@@ -986,11 +1011,12 @@ public class RestconfImpl implements RestconfService {
                                .append("\n");
                     }
 
-                    throw new ResponseException(Status.BAD_REQUEST,
+                    throw new RestconfDocumentedException(
                                  "Node \"" + child.getLocalName() +
                                  "\" is added as augment from more than one module. " +
                                  "Therefore node must have namespace (XML format) or module name (JSON format)." +
-                                 "\nThe node is added as augment from modules with namespaces:\n" + builder);
+                                 "\nThe node is added as augment from modules with namespaces:\n" + builder,
+                                 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                 }
 
                 boolean rightNodeSchemaFound = false;
@@ -1006,8 +1032,9 @@ public class RestconfImpl implements RestconfService {
                 }
 
                 if (!rightNodeSchemaFound) {
-                    throw new ResponseException(Status.BAD_REQUEST,
-                                      "Schema node \"" + child.getLocalName() + "\" was not found in module.");
+                    throw new RestconfDocumentedException(
+                               "Schema node \"" + child.getLocalName() + "\" was not found in module.",
+                               ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
                 }
             }
 
@@ -1022,9 +1049,10 @@ public class RestconfImpl implements RestconfService {
                     }
 
                     if (!foundKey) {
-                        throw new ResponseException(Status.BAD_REQUEST,
+                        throw new RestconfDocumentedException(
                                        "Missing key in URI \"" + listKey.getLocalName() +
-                                       "\" of list \"" + schema.getQName().getLocalName() + "\"");
+                                       "\" of list \"" + schema.getQName().getLocalName() + "\"",
+                                       ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                     }
                 }
             }