Remove RestconfError.ErrorType
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / services / impl / RestconfDataServiceImpl.java
index 2beb0fef8e5b7183fbbffa505e4366823d4b373f..12d1e5f6d49c103910bfd201ed3d70c079780c33 100644 (file)
@@ -17,11 +17,15 @@ import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsCo
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH;
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART;
 
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH;
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
 import java.net.URI;
 import java.time.Clock;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.net.URI;
 import java.time.Clock;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
@@ -32,21 +36,23 @@ import javax.ws.rs.core.UriInfo;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMActionService;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
+import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 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.PatchStatusContext;
 import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
 import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
-import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler;
-import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
-import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataService;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataService;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
@@ -63,20 +69,24 @@ import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfInvokeOperations
 import org.opendaylight.restconf.nb.rfc8040.streams.Configuration;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
 import org.opendaylight.restconf.nb.rfc8040.streams.Configuration;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -100,44 +110,27 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
 
     private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
 
     private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
+    private final SchemaContextHandler schemaContextHandler;
+    private final MdsalRestconfStrategy restconfStrategy;
+    private final DOMMountPointService mountPointService;
     private final SubscribeToStreamUtil streamUtils;
     private final SubscribeToStreamUtil streamUtils;
-
-    // FIXME: evaluate thread-safety of updates (synchronized) vs. access (mostly unsynchronized) here
-    private SchemaContextHandler schemaContextHandler;
-    private TransactionChainHandler transactionChainHandler;
-    private DOMMountPointServiceHandler mountPointServiceHandler;
-    private volatile ActionServiceHandler actionServiceHandler;
+    private final DOMActionService actionService;
+    private final DOMDataBroker dataBroker;
 
     public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
 
     public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
-            final TransactionChainHandler transactionChainHandler,
-            final DOMMountPointServiceHandler mountPointServiceHandler,
+            final DOMDataBroker dataBroker, final DOMMountPointService  mountPointService,
             final RestconfStreamsSubscriptionService delegRestconfSubscrService,
             final RestconfStreamsSubscriptionService delegRestconfSubscrService,
-            final ActionServiceHandler actionServiceHandler,
-            final Configuration configuration) {
-        this.actionServiceHandler = requireNonNull(actionServiceHandler);
+            final DOMActionService actionService, final Configuration configuration) {
         this.schemaContextHandler = requireNonNull(schemaContextHandler);
         this.schemaContextHandler = requireNonNull(schemaContextHandler);
-        this.transactionChainHandler = requireNonNull(transactionChainHandler);
-        this.mountPointServiceHandler = requireNonNull(mountPointServiceHandler);
+        this.dataBroker = requireNonNull(dataBroker);
+        this.restconfStrategy = new MdsalRestconfStrategy(dataBroker);
+        this.mountPointService = requireNonNull(mountPointService);
         this.delegRestconfSubscrService = requireNonNull(delegRestconfSubscrService);
         this.delegRestconfSubscrService = requireNonNull(delegRestconfSubscrService);
+        this.actionService = requireNonNull(actionService);
         streamUtils = configuration.isUseSSE() ? SubscribeToStreamUtil.serverSentEvents()
                 : SubscribeToStreamUtil.webSockets();
     }
 
         streamUtils = configuration.isUseSSE() ? SubscribeToStreamUtil.serverSentEvents()
                 : SubscribeToStreamUtil.webSockets();
     }
 
-    @Override
-    public synchronized void updateHandlers(final Object... handlers) {
-        for (final Object object : handlers) {
-            if (object instanceof SchemaContextHandler) {
-                schemaContextHandler = (SchemaContextHandler) object;
-            } else if (object instanceof ActionServiceHandler) {
-                actionServiceHandler = (ActionServiceHandler) object;
-            } else if (object instanceof DOMMountPointServiceHandler) {
-                mountPointServiceHandler = (DOMMountPointServiceHandler) object;
-            } else if (object instanceof TransactionChainHandler) {
-                transactionChainHandler = (TransactionChainHandler) object;
-            }
-        }
-    }
-
     @Override
     public Response readData(final UriInfo uriInfo) {
         return readData(null, uriInfo);
     @Override
     public Response readData(final UriInfo uriInfo) {
         return readData(null, uriInfo);
@@ -147,30 +140,43 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     public Response readData(final String identifier, final UriInfo uriInfo) {
         final EffectiveModelContext schemaContextRef = this.schemaContextHandler.get();
         final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
     public Response readData(final String identifier, final UriInfo uriInfo) {
         final EffectiveModelContext schemaContextRef = this.schemaContextHandler.get();
         final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
-                identifier, schemaContextRef, Optional.of(this.mountPointServiceHandler.get()));
+                identifier, schemaContextRef, Optional.of(mountPointService));
         final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(instanceIdentifier, uriInfo);
 
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
         final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(instanceIdentifier, uriInfo);
 
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
+
+        // FIXME: this looks quite crazy, why do we even have it?
+        if (mountPoint == null && identifier != null && identifier.contains(STREAMS_PATH)
+            && !identifier.contains(STREAM_PATH_PART)) {
+            createAllYangNotificationStreams(schemaContextRef, uriInfo);
+        }
+
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
-        final NormalizedNode<?, ?> node = readData(identifier, parameters.getContent(),
-                instanceIdentifier.getInstanceIdentifier(), strategy, parameters.getWithDefault(), schemaContextRef,
-                uriInfo);
+        final NormalizedNode node;
+        if (parameters.getFieldPaths() != null && !parameters.getFieldPaths().isEmpty()) {
+            node = ReadDataTransactionUtil.readData(parameters.getContent(), instanceIdentifier.getInstanceIdentifier(),
+                    strategy, parameters.getWithDefault(), schemaContextRef, parameters.getFieldPaths());
+        } else {
+            node = ReadDataTransactionUtil.readData(parameters.getContent(), instanceIdentifier.getInstanceIdentifier(),
+                    strategy, parameters.getWithDefault(), schemaContextRef);
+        }
+
+        // FIXME: this is utter craziness, refactor it properly!
         if (identifier != null && identifier.contains(STREAM_PATH) && identifier.contains(STREAM_ACCESS_PATH_PART)
                 && identifier.contains(STREAM_LOCATION_PATH_PART)) {
         if (identifier != null && identifier.contains(STREAM_PATH) && identifier.contains(STREAM_ACCESS_PATH_PART)
                 && identifier.contains(STREAM_LOCATION_PATH_PART)) {
-            final String value = (String) node.getValue();
+            final String value = (String) node.body();
             final String streamName = value.substring(value.indexOf(NOTIFICATION_STREAM + '/'));
             this.delegRestconfSubscrService.subscribeToStream(streamName, uriInfo);
         }
         if (node == null) {
             throw new RestconfDocumentedException(
                     "Request could not be completed because the relevant data model content does not exist",
             final String streamName = value.substring(value.indexOf(NOTIFICATION_STREAM + '/'));
             this.delegRestconfSubscrService.subscribeToStream(streamName, uriInfo);
         }
         if (node == null) {
             throw new RestconfDocumentedException(
                     "Request could not be completed because the relevant data model content does not exist",
-                    RestconfError.ErrorType.PROTOCOL,
-                    RestconfError.ErrorTag.DATA_MISSING);
+                    ErrorType.PROTOCOL, RestconfError.ErrorTag.DATA_MISSING);
         }
 
         if (parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL)
                     || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) {
         }
 
         if (parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL)
                     || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) {
-            final QName type = node.getNodeType();
+            final QName type = node.getIdentifier().getNodeType();
             return Response.status(200)
                     .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
                     .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null)
             return Response.status(200)
                     .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
                     .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null)
@@ -182,81 +188,32 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         return Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)).build();
     }
 
         return Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)).build();
     }
 
-    /**
-     * Read specific type of data from data store via transaction and if identifier read data from
-     * streams then put streams from actual schema context to datastore.
-     *
-     * @param identifier    identifier of data to read
-     * @param content       type of data to read (config, state, all)
-     * @param strategy      {@link RestconfStrategy} - object that perform the actual DS operations
-     * @param withDefa      value of with-defaults parameter
-     * @param schemaContext schema context
-     * @param uriInfo       uri info
-     * @return {@link NormalizedNode}
-     */
-    private NormalizedNode<?, ?> readData(final String identifier, final String content,
-            final YangInstanceIdentifier path, final RestconfStrategy strategy, final String withDefa,
-            final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
-        if (identifier != null && identifier.contains(STREAMS_PATH) && !identifier.contains(STREAM_PATH_PART)) {
-            createAllYangNotificationStreams(strategy, schemaContext, uriInfo);
-        }
-        return ReadDataTransactionUtil.readData(content, path, strategy, withDefa, schemaContext);
-    }
-
-    private void createAllYangNotificationStreams(final RestconfStrategy strategy,
-            final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
-        strategy.prepareReadWriteExecution();
-        final boolean exist = checkExist(schemaContext, strategy);
-
+    private void createAllYangNotificationStreams(final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
+        final DOMDataTreeWriteTransaction transaction = dataBroker.newWriteOnlyTransaction();
         for (final NotificationDefinition notificationDefinition : schemaContext.getNotifications()) {
         for (final NotificationDefinition notificationDefinition : schemaContext.getNotifications()) {
-            final NotificationListenerAdapter notifiStreamXML =
+            writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction,
                 CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
                 CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
-                    NotificationOutputType.XML);
-            final NotificationListenerAdapter notifiStreamJSON =
+                    NotificationOutputType.XML));
+            writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction,
                 CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
                 CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext,
-                    NotificationOutputType.JSON);
-            writeNotificationStreamToDatastore(schemaContext, uriInfo, strategy, exist, notifiStreamXML);
-            writeNotificationStreamToDatastore(schemaContext, uriInfo, strategy, exist, notifiStreamJSON);
+                    NotificationOutputType.JSON));
         }
         try {
         }
         try {
-            strategy.commit().get();
+            transaction.commit().get();
         } catch (final InterruptedException | ExecutionException e) {
             throw new RestconfDocumentedException("Problem while putting data to DS.", e);
         }
     }
 
     private void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext,
         } catch (final InterruptedException | ExecutionException e) {
             throw new RestconfDocumentedException("Problem while putting data to DS.", e);
         }
     }
 
     private void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext,
-            final UriInfo uriInfo, final RestconfStrategy strategy, final boolean exist,
-            final NotificationListenerAdapter listener) {
+            final UriInfo uriInfo, final DOMDataTreeWriteOperations tx, final NotificationListenerAdapter listener) {
         final URI uri = streamUtils.prepareUriByStreamName(uriInfo, listener.getStreamName());
         final URI uri = streamUtils.prepareUriByStreamName(uriInfo, listener.getStreamName());
-        final NormalizedNode<?, ?> mapToStreams =
-            RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
-                listener.getSchemaPath().getLastComponent(), schemaContext.getNotifications(), null,
-                listener.getOutputType(), uri, SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist);
-        writeDataToDS(schemaContext,
-            listener.getSchemaPath().getLastComponent().getLocalName(), strategy, exist, mapToStreams);
-    }
-
-    private static boolean checkExist(final EffectiveModelContext schemaContext, final RestconfStrategy strategy) {
-        try {
-            return strategy.exists(LogicalDatastoreType.OPERATIONAL,
-                IdentifierCodec.deserialize(Rfc8040.MonitoringModule.PATH_TO_STREAMS, schemaContext)).get();
-        } catch (final InterruptedException | ExecutionException exception) {
-            throw new RestconfDocumentedException("Problem while checking data if exists", exception);
-        }
-    }
+        final MapEntryNode mapToStreams = RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
+                listener.getSchemaPath().lastNodeIdentifier(), schemaContext.getNotifications(), null,
+                listener.getOutputType(), uri);
 
 
-    private static void writeDataToDS(final EffectiveModelContext schemaContext, final String name,
-                                      final RestconfStrategy strategy, final boolean exist,
-                                      final NormalizedNode<?, ?> mapToStreams) {
-        final String pathId;
-        if (exist) {
-            pathId = Rfc8040.MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + name;
-        } else {
-            pathId = Rfc8040.MonitoringModule.PATH_TO_STREAMS;
-        }
-        strategy.merge(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(pathId, schemaContext),
-            mapToStreams);
+        tx.merge(LogicalDatastoreType.OPERATIONAL,
+            Rfc8040.restconfStateStreamPath(mapToStreams.getIdentifier()), mapToStreams);
     }
 
     @Override
     }
 
     @Override
@@ -267,14 +224,13 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
         final InstanceIdentifierContext<? extends SchemaNode> iid = payload.getInstanceIdentifierContext();
 
 
         final InstanceIdentifierContext<? extends SchemaNode> iid = payload.getInstanceIdentifierContext();
 
-        PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload);
-        PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
-        PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
+        validInputData(iid.getSchemaNode(), payload);
+        validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
+        validateListKeysEqualityInPayloadAndUri(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final EffectiveModelContext ref = mountPoint == null
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final EffectiveModelContext ref = mountPoint == null
-                ? this.schemaContextHandler.get()
-                : mountPoint.getEffectiveModelContext();
+                ? this.schemaContextHandler.get() : modelContext(mountPoint);
 
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
         return PutDataTransactionUtil.putData(payload, ref, strategy, checkedParms.insert, checkedParms.point);
 
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
         return PutDataTransactionUtil.putData(payload, ref, strategy, checkedParms.insert, checkedParms.point);
@@ -291,21 +247,21 @@ public class RestconfDataServiceImpl implements RestconfDataService {
                 case INSERT:
                     if (insertUsed) {
                         throw new RestconfDocumentedException("Insert parameter can be used only once.",
                 case INSERT:
                     if (insertUsed) {
                         throw new RestconfDocumentedException("Insert parameter can be used only once.",
-                            RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
+                            ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
                     }
 
                     insertUsed = true;
                     final String str = entry.getValue().get(0);
                     }
 
                     insertUsed = true;
                     final String str = entry.getValue().get(0);
-                    insert = Insert.valueOf(str);
+                    insert = Insert.forValue(str);
                     if (insert == null) {
                         throw new RestconfDocumentedException("Unrecognized insert parameter value '" + str + "'",
                     if (insert == null) {
                         throw new RestconfDocumentedException("Unrecognized insert parameter value '" + str + "'",
-                            RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
+                            ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
                     }
                     break;
                 case POINT:
                     if (pointUsed) {
                         throw new RestconfDocumentedException("Point parameter can be used only once.",
                     }
                     break;
                 case POINT:
                     if (pointUsed) {
                         throw new RestconfDocumentedException("Point parameter can be used only once.",
-                            RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
+                            ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
                     }
 
                     pointUsed = true;
                     }
 
                     pointUsed = true;
@@ -313,7 +269,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
                     break;
                 default:
                     throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey(),
                     break;
                 default:
                     throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey(),
-                            RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
+                            ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
             }
         }
 
             }
         }
 
@@ -325,13 +281,13 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         if (pointUsed) {
             if (!insertUsed) {
                 throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.",
         if (pointUsed) {
             if (!insertUsed) {
                 throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.",
-                    RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
+                    ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
             }
 
             }
 
-            if (insert != Insert.BEFORE || insert != Insert.AFTER) {
+            if (insert != Insert.BEFORE && insert != Insert.AFTER) {
                 throw new RestconfDocumentedException(
                     "Point parameter can be used only with 'after' or 'before' values of Insert parameter.",
                 throw new RestconfDocumentedException(
                     "Point parameter can be used only with 'after' or 'before' values of Insert parameter.",
-                    RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
+                    ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT);
             }
         }
     }
             }
         }
     }
@@ -358,7 +314,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     @Override
     public Response deleteData(final String identifier) {
         final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
     @Override
     public Response deleteData(final String identifier) {
         final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
-                identifier, this.schemaContextHandler.get(), Optional.of(this.mountPointServiceHandler.get()));
+                identifier, this.schemaContextHandler.get(), Optional.of(mountPointService));
 
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
 
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
@@ -384,27 +340,26 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final InstanceIdentifierContext<? extends SchemaNode> iid = payload
                 .getInstanceIdentifierContext();
 
         final InstanceIdentifierContext<? extends SchemaNode> iid = payload
                 .getInstanceIdentifierContext();
 
-        PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload);
-        PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
-        PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
+        validInputData(iid.getSchemaNode(), payload);
+        validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
+        validateListKeysEqualityInPayloadAndUri(payload);
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final EffectiveModelContext ref = mountPoint == null
 
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final EffectiveModelContext ref = mountPoint == null
-                ? this.schemaContextHandler.get()
-                : mountPoint.getEffectiveModelContext();
+                ? this.schemaContextHandler.get() : modelContext(mountPoint);
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
 
         return PlainPatchDataTransactionUtil.patchData(payload, strategy, ref);
     }
 
     private EffectiveModelContext getSchemaContext(final DOMMountPoint mountPoint) {
         final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
 
         return PlainPatchDataTransactionUtil.patchData(payload, strategy, ref);
     }
 
     private EffectiveModelContext getSchemaContext(final DOMMountPoint mountPoint) {
-        return mountPoint == null ? schemaContextHandler.get() : mountPoint.getEffectiveModelContext();
+        return mountPoint == null ? schemaContextHandler.get() : modelContext(mountPoint);
     }
 
     // FIXME: why is this synchronized?
     public synchronized RestconfStrategy getRestconfStrategy(final DOMMountPoint mountPoint) {
         if (mountPoint == null) {
     }
 
     // FIXME: why is this synchronized?
     public synchronized RestconfStrategy getRestconfStrategy(final DOMMountPoint mountPoint) {
         if (mountPoint == null) {
-            return new MdsalRestconfStrategy(transactionChainHandler);
+            return restconfStrategy;
         }
 
         return RestconfStrategy.forMountPoint(mountPoint).orElseThrow(() -> {
         }
 
         return RestconfStrategy.forMountPoint(mountPoint).orElseThrow(() -> {
@@ -423,11 +378,13 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     public Response invokeAction(final NormalizedNodeContext payload) {
         final InstanceIdentifierContext<?> context = payload.getInstanceIdentifierContext();
         final DOMMountPoint mountPoint = context.getMountPoint();
     public Response invokeAction(final NormalizedNodeContext payload) {
         final InstanceIdentifierContext<?> context = payload.getInstanceIdentifierContext();
         final DOMMountPoint mountPoint = context.getMountPoint();
-        final SchemaPath schemaPath = context.getSchemaNode().getPath();
+        final Absolute schemaPath = Absolute.of(ImmutableList.copyOf(context.getSchemaNode().getPath()
+            .getPathFromRoot()));
         final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier();
         final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier();
-        final NormalizedNode<?, ?> data = payload.getData();
+        final NormalizedNode data = payload.getData();
 
 
-        if (yangIIdContext.isEmpty() && !RestconfDataServiceConstant.NETCONF_BASE_QNAME.equals(data.getNodeType())) {
+        if (yangIIdContext.isEmpty()
+            && !RestconfDataServiceConstant.NETCONF_BASE_QNAME.equals(data.getIdentifier().getNodeType())) {
             throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument",
                 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
             throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument",
                 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
@@ -435,12 +392,12 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final DOMActionResult response;
         final EffectiveModelContext schemaContextRef;
         if (mountPoint != null) {
         final DOMActionResult response;
         final EffectiveModelContext schemaContextRef;
         if (mountPoint != null) {
-            response = RestconfInvokeOperationsUtil.invokeActionViaMountPoint(mountPoint, (ContainerNode) data,
-                schemaPath, yangIIdContext);
-            schemaContextRef = mountPoint.getEffectiveModelContext();
+            response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath, yangIIdContext,
+                mountPoint);
+            schemaContextRef = modelContext(mountPoint);
         } else {
         } else {
-            response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath,
-                this.actionServiceHandler, yangIIdContext);
+            response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath, yangIIdContext,
+                actionService);
             schemaContextRef = this.schemaContextHandler.get();
         }
         final DOMActionResult result = RestconfInvokeOperationsUtil.checkActionResponse(response);
             schemaContextRef = this.schemaContextHandler.get();
         }
         final DOMActionResult result = RestconfInvokeOperationsUtil.checkActionResponse(response);
@@ -455,11 +412,99 @@ public class RestconfDataServiceImpl implements RestconfDataService {
             }
         }
 
             }
         }
 
-        if (resultData != null && resultData.getValue().isEmpty()) {
+        if (resultData != null && resultData.isEmpty()) {
             throw new WebApplicationException(Response.Status.NO_CONTENT);
         }
 
         return Response.status(200).entity(new NormalizedNodeContext(new InstanceIdentifierContext<>(yangIIdContext,
                 resultNodeSchema, mountPoint, schemaContextRef), resultData)).build();
     }
             throw new WebApplicationException(Response.Status.NO_CONTENT);
         }
 
         return Response.status(200).entity(new NormalizedNodeContext(new InstanceIdentifierContext<>(yangIIdContext,
                 resultNodeSchema, mountPoint, schemaContextRef), resultData)).build();
     }
+
+    /**
+     * Valid input data with {@link SchemaNode}.
+     *
+     * @param schemaNode {@link SchemaNode}
+     * @param payload    input data
+     */
+    @VisibleForTesting
+    public static void validInputData(final SchemaNode schemaNode, final NormalizedNodeContext payload) {
+        if (schemaNode != null && payload.getData() == null) {
+            throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        } else if (schemaNode == null && payload.getData() != null) {
+            throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        }
+    }
+
+    /**
+     * Valid top level node name.
+     *
+     * @param path    path of node
+     * @param payload data
+     */
+    @VisibleForTesting
+    public static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodeContext payload) {
+        final String payloadName = payload.getData().getIdentifier().getNodeType().getLocalName();
+
+        if (path.isEmpty()) {
+            if (!payload.getData().getIdentifier().getNodeType().equals(
+                RestconfDataServiceConstant.NETCONF_BASE_QNAME)) {
+                throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        } else {
+            final String identifierName = path.getLastPathArgument().getNodeType().getLocalName();
+            if (!payloadName.equals(identifierName)) {
+                throw new RestconfDocumentedException(
+                        "Payload name (" + payloadName + ") is different from identifier name (" + identifierName + ")",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        }
+    }
+
+
+    /**
+     * 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
+     */
+    @VisibleForTesting
+    public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodeContext payload) {
+        final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
+        final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
+        final SchemaNode schemaNode = iiWithData.getSchemaNode();
+        final NormalizedNode data = payload.getData();
+        if (schemaNode instanceof ListSchemaNode) {
+            final List<QName> keyDefinitions = ((ListSchemaNode) schemaNode).getKeyDefinition();
+            if (lastPathArgument instanceof NodeIdentifierWithPredicates && data instanceof MapEntryNode) {
+                final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument).asMap();
+                isEqualUriAndPayloadKeyValues(uriKeyValues, (MapEntryNode) data, keyDefinitions);
+            }
+        }
+    }
+
+    private static void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final MapEntryNode payload,
+            final List<QName> keyDefinitions) {
+        final Map<QName, Object> mutableCopyUriKeyValues = new HashMap<>(uriKeyValues);
+        for (final QName keyDefinition : keyDefinitions) {
+            final Object uriKeyValue = RestconfDocumentedException.throwIfNull(
+                    mutableCopyUriKeyValues.remove(keyDefinition), ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
+                    "Missing key %s in URI.", keyDefinition);
+
+            final Object dataKeyValue = payload.getIdentifier().getValue(keyDefinition);
+
+            if (!uriKeyValue.equals(dataKeyValue)) {
+                final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName()
+                        + "' specified in the URI doesn't match the value '" + dataKeyValue
+                        + "' specified in the message body. ";
+                throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+            }
+        }
+    }
+
+    private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
+        return mountPoint.getService(DOMSchemaService.class)
+            .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
+            .orElse(null);
+    }
 }
 }