Create MdsalRestconfServer 93/107893/10
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 16 Sep 2023 09:24:35 +0000 (11:24 +0200)
committerRobert Varga <nite@hq.sk>
Fri, 29 Sep 2023 13:33:47 +0000 (13:33 +0000)
The actual binding to MD-SAL services is independent of other
concerns, just as JAX-RS binding.

Introduce MdsalRestconfServer to act as an intermediary. This allows us
to centralize data binding operations and later act as the central
implementation dispatch.

This reduces the number of callers to
InstanceIdentifierParser.toInstanceIdentifier(), as we have a proper
indirection to bind either the root or a non-empty identifier and can
provide the first cut of differentiated binding.

In RestconfOperationsServiceImpl this has the nice effect of bringing
together the two distinct invocation paths, leading to
OperationsContent.bodyFor(EffectiveModelContext) being redundant.

In RestconfDataServiceImpl we lose getRestconfStrategy(), which is now
invoked on the server. We also clarify variable names, so they have a
semantic meaning.

RestconfInvokeOperationsServiceImpl integration sees RestconfStrategy
grow awareness of DOMRpcService, providing a clean invocation interface
centered around RestconfFuture<Optional<ContainerNode>>. That
co-location of concerns also allow us to perform only a single error
mapping, we any DOMRpcResult's errors into a
RestconfDocumentedException.

JIRA: NETCONF-1157
Change-Id: I9ecefb2e065b4ad6ed8e4226713da851a1050619
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
20 files changed:
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/JaxRsNorthbound.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestconfApplication.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtil.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/MdsalRestconfServer.java [new file with mode: 0644]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/OperationsContent.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImpl.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfOperationsServiceImpl.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategy.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategy.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/CreateStreamUtilTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/MdsalRestconfServerTest.java [new file with mode: 0644]
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf799Test.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf822Test.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImplTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImplTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfOperationsServiceImplTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/MdsalRestconfStrategyTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/NetconfRestconfStrategyTest.java

index 05967718f725597efe5c8ef66afaa20ac84489b6..901ea211eb695943ff970cec89b378a5db364f02 100644 (file)
@@ -31,6 +31,7 @@ import org.opendaylight.mdsal.dom.api.DOMNotificationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.MdsalRestconfServer;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfDataStreamServiceImpl;
 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.restconf.nb.rfc8040.streams.WebSocketInitializer;
@@ -79,9 +80,9 @@ public final class JaxRsNorthbound implements AutoCloseable {
             @Reference final DOMMountPointService mountPointService,
             @Reference final DOMNotificationService notificationService, @Reference final DOMRpcService rpcService,
             @Reference final DOMSchemaService schemaService, @Reference final DatabindProvider databindProvider,
-            final Configuration configuration) throws ServletException {
+            @Reference final MdsalRestconfServer server, final Configuration configuration) throws ServletException {
         this(webServer, webContextSecurer, servletSupport, filterAdapterConfiguration, actionService, dataBroker,
-            mountPointService, notificationService, rpcService, schemaService, databindProvider,
+            mountPointService, notificationService, rpcService, schemaService, databindProvider, server,
             configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count(),
             new StreamsConfiguration(configuration.maximum$_$fragment$_$length(), configuration.idle$_$timeout(),
                 configuration.heartbeat$_$interval(), configuration.use$_$sse()));
@@ -92,7 +93,7 @@ public final class JaxRsNorthbound implements AutoCloseable {
             final DOMActionService actionService, final DOMDataBroker dataBroker,
             final DOMMountPointService mountPointService, final DOMNotificationService notificationService,
             final DOMRpcService rpcService, final DOMSchemaService schemaService,
-            final DatabindProvider databindProvider,
+            final DatabindProvider databindProvider, final MdsalRestconfServer server,
             final String pingNamePrefix, final int pingMaxThreadCount,
             final StreamsConfiguration streamsConfiguration) throws ServletException {
         final var scheduledThreadPool = new ScheduledThreadPoolWrapper(pingMaxThreadCount,
@@ -105,8 +106,8 @@ public final class JaxRsNorthbound implements AutoCloseable {
             .addServlet(ServletDetails.builder()
                 .addUrlPattern("/*")
                 .servlet(servletSupport.createHttpServletBuilder(
-                    new RestconfApplication(databindProvider, mountPointService, dataBroker, rpcService, actionService,
-                        notificationService, schemaService, streamsConfiguration))
+                    new RestconfApplication(databindProvider, server, mountPointService, dataBroker, rpcService,
+                        actionService, notificationService, schemaService, streamsConfiguration))
                     .build())
                 .asyncSupported(true)
                 .build())
index 805eff2ae4ac52f1df565560a1b1c52288e2acf8..9b8b88d26239a1b59dcac31f36add7af10df384e 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.MdsalRestconfServer;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfDataServiceImpl;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfImpl;
 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfInvokeOperationsServiceImpl;
@@ -28,29 +29,30 @@ import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 
 @Singleton
 public class RestconfApplication extends AbstractRestconfApplication {
-    private RestconfApplication(final DatabindProvider databindProvider, final DOMMountPointService mountPointService,
+    private RestconfApplication(final DatabindProvider databindProvider, final MdsalRestconfServer server,
+            final DOMMountPointService mountPointService,
             final RestconfStreamsSubscriptionService streamSubscription, final DOMDataBroker dataBroker,
-            final DOMRpcService rpcService, final DOMActionService actionService,
-            final DOMNotificationService notificationService, final DOMSchemaService domSchemaService,
-            final StreamsConfiguration configuration) {
+            final DOMActionService actionService, final DOMNotificationService notificationService,
+            final DOMSchemaService domSchemaService, final StreamsConfiguration configuration) {
         super(databindProvider, List.of(
             streamSubscription,
-            new RestconfDataServiceImpl(databindProvider, dataBroker, mountPointService, streamSubscription,
-                actionService, configuration),
-            new RestconfInvokeOperationsServiceImpl(databindProvider, rpcService, mountPointService, configuration),
-            new RestconfOperationsServiceImpl(databindProvider, mountPointService),
+            new RestconfDataServiceImpl(databindProvider, server, dataBroker, streamSubscription, actionService,
+                configuration),
+            new RestconfInvokeOperationsServiceImpl(databindProvider, server, mountPointService, configuration),
+            new RestconfOperationsServiceImpl(databindProvider, server),
             new RestconfSchemaServiceImpl(domSchemaService, mountPointService),
             new RestconfImpl(databindProvider)));
     }
 
     @Inject
-    public RestconfApplication(final DatabindProvider databindProvider, final DOMMountPointService mountPointService,
-            final DOMDataBroker dataBroker, final DOMRpcService rpcService, final DOMActionService actionService,
+    public RestconfApplication(final DatabindProvider databindProvider, final MdsalRestconfServer server,
+            final DOMMountPointService mountPointService, final DOMDataBroker dataBroker,
+            final DOMRpcService rpcService, final DOMActionService actionService,
             final DOMNotificationService notificationService, final DOMSchemaService domSchemaService,
             final StreamsConfiguration configuration) {
-        this(databindProvider, mountPointService,
+        this(databindProvider, server, mountPointService,
             new RestconfStreamsSubscriptionServiceImpl(dataBroker, notificationService, databindProvider,
                 configuration),
-            dataBroker, rpcService, actionService, notificationService, domSchemaService, configuration);
+            dataBroker, actionService, notificationService, domSchemaService, configuration);
     }
 }
index 8e5502928b97bb28c576edbb620ae38d7bf8c42e..d94ea1e47a5c67c5c9ca2a9407f130f1350b9d1e 100644 (file)
@@ -18,7 +18,6 @@ import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.DeviceNotificationListenerAdaptor;
@@ -108,7 +107,7 @@ final class CreateStreamUtil {
      *     </pre>
      */
     // FIXME: this really should be a normal RPC implementation
-    static DOMRpcResult createDataChangeNotifiStream(final ListenersBroker listenersBroker, final ContainerNode input,
+    static ContainerNode createDataChangeNotifiStream(final ListenersBroker listenersBroker, final ContainerNode input,
             final EffectiveModelContext refSchemaCtx) {
         // parsing out of container with settings and path
         final YangInstanceIdentifier path = preparePath(input);
@@ -126,10 +125,10 @@ final class CreateStreamUtil {
         listenersBroker.registerDataChangeListener(path, streamName, outputType);
 
         // building of output
-        return new DefaultDOMRpcResult(Builders.containerBuilder()
+        return Builders.containerBuilder()
             .withNodeIdentifier(SAL_REMOTE_OUTPUT_NODEID)
             .withChild(ImmutableNodes.leafNode(STREAM_NAME_NODEID, streamName))
-            .build());
+            .build();
     }
 
     /**
@@ -141,7 +140,8 @@ final class CreateStreamUtil {
      * @param mountPointService dom mount point service
      * @return {@link DOMRpcResult} - Output of RPC - example in JSON
      */
-    static DOMRpcResult createDeviceNotificationListener(final String baseUrl, final ContainerNode input,
+    // FIXME: this should be an RPC invocation
+    static ContainerNode createDeviceNotificationListener(final String baseUrl, final ContainerNode input,
             final SubscribeToStreamUtil streamUtil, final DOMMountPointService mountPointService) {
         // parsing out of container with settings and path
         // FIXME: ugly cast
@@ -187,12 +187,11 @@ final class CreateStreamUtil {
                 mountPointService, mountPoint.getIdentifier());
         notificationListenerAdapter.listen(mountNotifService, notificationPaths);
 
-        // building of output
-        return new DefaultDOMRpcResult(Builders.containerBuilder()
+        return Builders.containerBuilder()
             .withNodeIdentifier(new NodeIdentifier(SubscribeDeviceNotificationOutput.QNAME))
-            .withChild(ImmutableNodes.leafNode(DEVICE_NOTIFICATION_STREAM_PATH, baseUrl + deviceName
-                + "?" + RestconfStreamsConstants.NOTIFICATION_TYPE + "=" + RestconfStreamsConstants.DEVICE))
-            .build());
+            .withChild(ImmutableNodes.leafNode(DEVICE_NOTIFICATION_STREAM_PATH, baseUrl + deviceName + "?"
+                + RestconfStreamsConstants.NOTIFICATION_TYPE + "=" + RestconfStreamsConstants.DEVICE))
+            .build();
     }
 
     /**
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/MdsalRestconfServer.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/MdsalRestconfServer.java
new file mode 100644 (file)
index 0000000..efa0b9b
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMMountPoint;
+import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
+import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A RESTCONF server implemented on top of MD-SAL.
+ */
+// FIXME: factor out the 'RestconfServer' interface once we're ready
+// FIXME: this should live in 'org.opendaylight.restconf.server.mdsal' package
+@Singleton
+@Component(service = MdsalRestconfServer.class)
+public final class MdsalRestconfServer {
+    private static final Logger LOG = LoggerFactory.getLogger(MdsalRestconfServer.class);
+    private static final VarHandle LOCAL_STRATEGY;
+
+    static {
+        try {
+            LOCAL_STRATEGY = MethodHandles.lookup()
+                .findVarHandle(MdsalRestconfServer.class, "localStrategy", RestconfStrategy.class);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private final @NonNull DOMMountPointService mountPointService;
+    private final @NonNull DOMDataBroker dataBroker;
+    private final @Nullable DOMRpcService rpcService;
+
+    @SuppressWarnings("unused")
+    private volatile RestconfStrategy localStrategy;
+
+    @Inject
+    @Activate
+    public MdsalRestconfServer(@Reference final DOMDataBroker dataBroker, @Reference final DOMRpcService rpcService,
+            @Reference final DOMMountPointService mountPointService) {
+        this.dataBroker = requireNonNull(dataBroker);
+        this.rpcService = requireNonNull(rpcService);
+        this.mountPointService = requireNonNull(mountPointService);
+    }
+
+    @NonNull InstanceIdentifierContext bindRequestPath(final DatabindContext databind, final String identifier) {
+        // FIXME: go through ApiPath first. That part should eventually live in callers
+        // FIXME: DatabindContext looks like it should be internal
+        return verifyNotNull(ParserIdentifier.toInstanceIdentifier(requireNonNull(identifier), databind.modelContext(),
+            mountPointService));
+    }
+
+    @SuppressWarnings("static-method")
+    @NonNull InstanceIdentifierContext bindRequestRoot(final DatabindContext databind) {
+        return InstanceIdentifierContext.ofLocalRoot(databind.modelContext());
+    }
+
+    @VisibleForTesting
+    @NonNull RestconfStrategy getRestconfStrategy(final EffectiveModelContext modelContext,
+            final @Nullable DOMMountPoint mountPoint) {
+        if (mountPoint == null) {
+            return localStrategy(modelContext);
+        }
+
+        final var ret = RestconfStrategy.forMountPoint(modelContext, mountPoint);
+        if (ret == null) {
+            final var mountId = mountPoint.getIdentifier();
+            LOG.warn("Mount point {} does not expose a suitable access interface", mountId);
+            throw new RestconfDocumentedException("Could not find a supported access interface in mount point "
+                + mountId);
+        }
+        return ret;
+    }
+
+    private @NonNull RestconfStrategy localStrategy(final EffectiveModelContext modelContext) {
+        final var local = (RestconfStrategy) LOCAL_STRATEGY.getAcquire(this);
+        if (local != null && modelContext.equals(local.modelContext())) {
+            return local;
+        }
+
+        final var created = new MdsalRestconfStrategy(modelContext, dataBroker, rpcService);
+        LOCAL_STRATEGY.setRelease(this, created);
+        return created;
+    }
+}
index 356fbae483b04f77940a5dfc4deee1d951a3d653..51e9c4ff1e442998b5162e436c44c5ae88ec6d37 100644 (file)
@@ -96,22 +96,6 @@ enum OperationsContent {
         this.emptyBody = requireNonNull(emptyBody);
     }
 
-    /**
-     * Return the content for a particular {@link EffectiveModelContext}.
-     *
-     * @param context Context to use
-     * @return Content of HTTP GET operation as a String
-     */
-    public final @NonNull String bodyFor(final EffectiveModelContext context) {
-        if (isEmptyContext(context)) {
-            // No modules, or defensive return empty content
-            return emptyBody;
-        }
-
-        final var moduleRpcs = getModuleRpcs(context, context.getModuleStatements());
-        return moduleRpcs.isEmpty() ? emptyBody : createBody(moduleRpcs);
-    }
-
     /**
      * Return content with RPCs and actions for a particular {@link Inference}.
      *
index 7e7244ea6f97067303d55efc981cf0cb7f0ca56e..4d00582d3348af88ea0a7a6af41d23b733132757 100644 (file)
@@ -22,13 +22,13 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
 import java.net.URI;
 import java.time.Clock;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import javax.ws.rs.Consumes;
@@ -58,12 +58,12 @@ import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.nb.rfc8040.MediaTypes;
+import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
 import org.opendaylight.restconf.nb.rfc8040.databind.ChildBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
 import org.opendaylight.restconf.nb.rfc8040.databind.JsonChildBody;
@@ -82,14 +82,11 @@ import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.nb.rfc8040.monitoring.RestconfStateStreams;
 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.transactions.RestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenersBroker;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
 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.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
@@ -119,35 +116,22 @@ import org.slf4j.LoggerFactory;
 public final class RestconfDataServiceImpl {
     private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
-    private static final VarHandle LOCAL_STRATEGY;
-
-    static {
-        try {
-            LOCAL_STRATEGY = MethodHandles.lookup()
-                .findVarHandle(RestconfDataServiceImpl.class, "localStrategy", RestconfStrategy.class);
-        } catch (NoSuchFieldException | IllegalAccessException e) {
-            throw new ExceptionInInitializerError(e);
-        }
-    }
 
     private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
     private final DatabindProvider databindProvider;
-    private final DOMMountPointService mountPointService;
     private final SubscribeToStreamUtil streamUtils;
     private final DOMActionService actionService;
+    private final MdsalRestconfServer server;
+    @Deprecated(forRemoval = true)
     private final DOMDataBroker dataBroker;
     private final ListenersBroker listenersBroker = ListenersBroker.getInstance();
 
-    @SuppressWarnings("unused")
-    private volatile RestconfStrategy localStrategy;
-
-    public RestconfDataServiceImpl(final DatabindProvider databindProvider,
-            final DOMDataBroker dataBroker, final DOMMountPointService  mountPointService,
-            final RestconfStreamsSubscriptionService delegRestconfSubscrService,
+    public RestconfDataServiceImpl(final DatabindProvider databindProvider, final MdsalRestconfServer server,
+            final DOMDataBroker dataBroker, final RestconfStreamsSubscriptionService delegRestconfSubscrService,
             final DOMActionService actionService, final StreamsConfiguration configuration) {
         this.databindProvider = requireNonNull(databindProvider);
+        this.server = requireNonNull(server);
         this.dataBroker = requireNonNull(dataBroker);
-        this.mountPointService = requireNonNull(mountPointService);
         this.delegRestconfSubscrService = requireNonNull(delegRestconfSubscrService);
         this.actionService = requireNonNull(actionService);
         streamUtils = configuration.useSSE() ? SubscribeToStreamUtil.serverSentEvents()
@@ -170,7 +154,8 @@ public final class RestconfDataServiceImpl {
         MediaType.TEXT_XML
     })
     public Response readData(@Context final UriInfo uriInfo) {
-        return readData(null, uriInfo);
+        final var readParams = QueryParams.newReadDataParams(uriInfo);
+        return readData(server.bindRequestRoot(databindProvider.currentContext()), readParams).getValue();
     }
 
     /**
@@ -192,11 +177,10 @@ public final class RestconfDataServiceImpl {
     public Response readData(@Encoded @PathParam("identifier") final String identifier,
             @Context final UriInfo uriInfo) {
         final var readParams = QueryParams.newReadDataParams(uriInfo);
-        final var schemaContextRef = databindProvider.currentContext().modelContext();
-        // FIXME: go through
-        final var instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier, schemaContextRef,
-            mountPointService);
-        final var mountPoint = instanceIdentifier.getMountPoint();
+        final var databind = databindProvider.currentContext();
+        final var schemaContextRef = databind.modelContext();
+        final var reqPath = server.bindRequestPath(databind, identifier);
+        final var mountPoint = reqPath.getMountPoint();
 
         // FIXME: this looks quite crazy, why do we even have it?
         if (mountPoint == null && identifier != null && identifier.contains(STREAMS_PATH)
@@ -204,46 +188,52 @@ public final class RestconfDataServiceImpl {
             createAllYangNotificationStreams(schemaContextRef, uriInfo);
         }
 
-        final var queryParams = QueryParams.newQueryParameters(readParams, instanceIdentifier);
-        final var fieldPaths = queryParams.fieldPaths();
-        // FIXME:the model context should be coming from instanceIdentifier!
-        final var strategy = getRestconfStrategy(schemaContextRef, mountPoint);
-        final NormalizedNode node;
-        if (fieldPaths != null && !fieldPaths.isEmpty()) {
-            node = strategy.readData(readParams.content(), instanceIdentifier.getInstanceIdentifier(),
-                readParams.withDefaults(), fieldPaths);
-        } else {
-            node = strategy.readData(readParams.content(), instanceIdentifier.getInstanceIdentifier(),
-                readParams.withDefaults());
-        }
+        final var nodeAndResponse = readData(reqPath, readParams);
 
         // 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)) {
-            final String value = (String) node.body();
+            final String value = (String) nodeAndResponse.getKey().body();
             final String streamName = value.substring(value.indexOf(NOTIFICATION_STREAM + '/'));
             delegRestconfSubscrService.subscribeToStream(streamName, uriInfo);
         }
+
+        return nodeAndResponse.getValue();
+    }
+
+    private Entry<NormalizedNode, Response> readData(final InstanceIdentifierContext reqPath,
+            final ReadDataParams readParams) {
+        final var queryParams = QueryParams.newQueryParameters(readParams, reqPath);
+        final var fieldPaths = queryParams.fieldPaths();
+        final var strategy = server.getRestconfStrategy(reqPath.getSchemaContext(), reqPath.getMountPoint());
+        final NormalizedNode node;
+        if (fieldPaths != null && !fieldPaths.isEmpty()) {
+            node = strategy.readData(readParams.content(), reqPath.getInstanceIdentifier(),
+                readParams.withDefaults(), fieldPaths);
+        } else {
+            node = strategy.readData(readParams.content(), reqPath.getInstanceIdentifier(),
+                readParams.withDefaults());
+        }
         if (node == null) {
             throw new RestconfDocumentedException(
                     "Request could not be completed because the relevant data model content does not exist",
                     ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
         }
 
-        return switch (readParams.content()) {
+        return Map.entry(node, switch (readParams.content()) {
             case ALL, CONFIG -> {
                 final QName type = node.name().getNodeType();
                 yield Response.status(Status.OK)
-                    .entity(new NormalizedNodePayload(instanceIdentifier.inference(), node, queryParams))
+                    .entity(new NormalizedNodePayload(reqPath.inference(), node, queryParams))
                     .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null) + "-"
                         + type.getLocalName() + '"')
                     .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC())))
                     .build();
             }
             case NONCONFIG -> Response.status(Status.OK)
-                .entity(new NormalizedNodePayload(instanceIdentifier.inference(), node, queryParams))
+                .entity(new NormalizedNodePayload(reqPath.inference(), node, queryParams))
                 .build();
-        };
+        });
     }
 
     private void createAllYangNotificationStreams(final EffectiveModelContext schemaContext, final UriInfo uriInfo) {
@@ -382,10 +372,9 @@ public final class RestconfDataServiceImpl {
     }
 
     private Response putData(final @Nullable String identifier, final UriInfo uriInfo, final ResourceBody body) {
-        final var localModel = databindProvider.currentContext().modelContext();
-        final var context = ParserIdentifier.toInstanceIdentifier(identifier, localModel, mountPointService);
-        final var insert = QueryParams.parseInsert(context.getSchemaContext(), uriInfo);
-        final var req = bindResourceRequest(context, body);
+        final var reqPath = server.bindRequestPath(databindProvider.currentContext(), identifier);
+        final var insert = QueryParams.parseInsert(reqPath.getSchemaContext(), uriInfo);
+        final var req = bindResourceRequest(reqPath, body);
 
         return switch (
             req.strategy().putData(req.path(), req.data(), insert)) {
@@ -430,17 +419,16 @@ public final class RestconfDataServiceImpl {
     })
     public Response postDataJSON(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
             @Context final UriInfo uriInfo) {
-        final var instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
-            databindProvider.currentContext().modelContext(), mountPointService);
-        if (instanceIdentifier.getSchemaNode() instanceof ActionDefinition) {
+        final var reqPath = server.bindRequestPath(databindProvider.currentContext(), identifier);
+        if (reqPath.getSchemaNode() instanceof ActionDefinition) {
             try (var jsonBody = new JsonOperationInputBody(body)) {
-                return invokeAction(instanceIdentifier, jsonBody);
+                return invokeAction(reqPath, jsonBody);
             }
         }
 
         try (var jsonBody = new JsonChildBody(body)) {
-            return postData(instanceIdentifier.inference(), instanceIdentifier.getInstanceIdentifier(), jsonBody,
-                uriInfo, instanceIdentifier.getMountPoint());
+            return postData(reqPath.inference(), reqPath.getInstanceIdentifier(), jsonBody, uriInfo,
+                reqPath.getMountPoint());
         }
     }
 
@@ -481,16 +469,16 @@ public final class RestconfDataServiceImpl {
     })
     public Response postDataXML(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
             @Context final UriInfo uriInfo) {
-        final var iid = ParserIdentifier.toInstanceIdentifier(identifier,
-            databindProvider.currentContext().modelContext(), mountPointService);
-        if (iid.getSchemaNode() instanceof ActionDefinition) {
+        final var reqPath = server.bindRequestPath(databindProvider.currentContext(), identifier);
+        if (reqPath.getSchemaNode() instanceof ActionDefinition) {
             try (var xmlBody = new XmlOperationInputBody(body)) {
-                return invokeAction(iid, xmlBody);
+                return invokeAction(reqPath, xmlBody);
             }
         }
 
         try (var xmlBody = new XmlChildBody(body)) {
-            return postData(iid.inference(), iid.getInstanceIdentifier(), xmlBody, uriInfo, iid.getMountPoint());
+            return postData(reqPath.inference(), reqPath.getInstanceIdentifier(), xmlBody, uriInfo,
+                reqPath.getMountPoint());
         }
     }
 
@@ -503,7 +491,7 @@ public final class RestconfDataServiceImpl {
             final UriInfo uriInfo, final @Nullable DOMMountPoint mountPoint) {
         final var modelContext = inference.getEffectiveModelContext();
         final var insert = QueryParams.parseInsert(modelContext, uriInfo);
-        final var strategy = getRestconfStrategy(modelContext, mountPoint);
+        final var strategy = server.getRestconfStrategy(modelContext, mountPoint);
         var path = parentPath;
         final var payload = body.toPayload(path, inference);
         final var data = payload.body();
@@ -547,12 +535,10 @@ public final class RestconfDataServiceImpl {
     @Path("/data/{identifier:.+}")
     public void deleteData(@Encoded @PathParam("identifier") final String identifier,
             @Suspended final AsyncResponse ar) {
-        final var instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
-            databindProvider.currentContext().modelContext(), mountPointService);
-        final var strategy = getRestconfStrategy(instanceIdentifier.getSchemaContext(),
-            instanceIdentifier.getMountPoint());
+        final var reqPath = server.bindRequestPath(databindProvider.currentContext(), identifier);
+        final var strategy = server.getRestconfStrategy(reqPath.getSchemaContext(), reqPath.getMountPoint());
 
-        Futures.addCallback(strategy.delete(instanceIdentifier.getInstanceIdentifier()), new FutureCallback<>() {
+        Futures.addCallback(strategy.delete(reqPath.getInstanceIdentifier()), new FutureCallback<>() {
             @Override
             public void onSuccess(final Empty result) {
                 ar.resume(Response.noContent().build());
@@ -581,7 +567,7 @@ public final class RestconfDataServiceImpl {
     })
     public void plainPatchDataXML(final InputStream body, @Suspended final AsyncResponse ar) {
         try (var xmlBody = new XmlResourceBody(body)) {
-            plainPatchData(null, xmlBody, ar);
+            plainPatchData(xmlBody, ar);
         }
     }
 
@@ -622,7 +608,7 @@ public final class RestconfDataServiceImpl {
     })
     public void plainPatchDataJSON(final InputStream body, @Suspended final AsyncResponse ar) {
         try (var jsonBody = new JsonResourceBody(body)) {
-            plainPatchData(null, jsonBody, ar);
+            plainPatchData(jsonBody, ar);
         }
     }
 
@@ -647,6 +633,17 @@ public final class RestconfDataServiceImpl {
         }
     }
 
+    /**
+     * Partially modify the target data resource, as defined in
+     * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
+     *
+     * @param body data node for put to config DS
+     * @param ar {@link AsyncResponse} which needs to be completed
+     */
+    private void plainPatchData(final ResourceBody body, final AsyncResponse ar) {
+        plainPatchData(server.bindRequestRoot(databindProvider.currentContext()), body, ar);
+    }
+
     /**
      * Partially modify the target data resource, as defined in
      * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
@@ -655,11 +652,21 @@ public final class RestconfDataServiceImpl {
      * @param body data node for put to config DS
      * @param ar {@link AsyncResponse} which needs to be completed
      */
-    private void plainPatchData(final @Nullable String identifier, final ResourceBody body, final AsyncResponse ar) {
-        final var req = bindResourceRequest(
-            ParserIdentifier.toInstanceIdentifier(identifier, databindProvider.currentContext().modelContext(),
-                mountPointService),
-            body);
+    private void plainPatchData(final String identifier, final ResourceBody body, final AsyncResponse ar) {
+        plainPatchData(server.bindRequestPath(databindProvider.currentContext(), identifier), body, ar);
+    }
+
+    /**
+     * Partially modify the target data resource, as defined in
+     * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
+     *
+     * @param reqPath path to target
+     * @param body data node for put to config DS
+     * @param ar {@link AsyncResponse} which needs to be completed
+     */
+    private void plainPatchData(final InstanceIdentifierContext reqPath, final ResourceBody body,
+            final AsyncResponse ar) {
+        final var req = bindResourceRequest(reqPath, body);
         final var future = req.strategy().merge(req.path(), req.data());
 
         Futures.addCallback(future, new FutureCallback<>() {
@@ -675,13 +682,14 @@ public final class RestconfDataServiceImpl {
         }, MoreExecutors.directExecutor());
     }
 
-    private @NonNull ResourceRequest bindResourceRequest(final InstanceIdentifierContext context,
+    private @NonNull ResourceRequest bindResourceRequest(final InstanceIdentifierContext reqPath,
             final ResourceBody body) {
-        final var inference = context.inference();
-        final var path = context.getInstanceIdentifier();
-        final var data = body.toNormalizedNode(path, inference, context.getSchemaNode());
+        final var inference = reqPath.inference();
+        final var path = reqPath.getInstanceIdentifier();
+        final var data = body.toNormalizedNode(path, inference, reqPath.getSchemaNode());
 
-        return new ResourceRequest(getRestconfStrategy(inference.getEffectiveModelContext(), context.getMountPoint()),
+        return new ResourceRequest(
+            server.getRestconfStrategy(inference.getEffectiveModelContext(), reqPath.getMountPoint()),
             path, data);
     }
 
@@ -775,17 +783,17 @@ public final class RestconfDataServiceImpl {
     }
 
     private PatchStatusContext yangPatchData(final String identifier, final @NonNull PatchBody body) {
-        final var iid = ParserIdentifier.toInstanceIdentifier(requireNonNull(identifier),
-            databindProvider.currentContext().modelContext(), mountPointService);
-        final var context = iid.getSchemaContext();
-        return yangPatchData(context, parsePatchBody(context, iid.getInstanceIdentifier(), body), iid.getMountPoint());
+        final var reqPath = server.bindRequestPath(databindProvider.currentContext(), identifier);
+        final var modelContext = reqPath.getSchemaContext();
+        return yangPatchData(modelContext, parsePatchBody(modelContext, reqPath.getInstanceIdentifier(), body),
+            reqPath.getMountPoint());
     }
 
 
     @VisibleForTesting
     @NonNull PatchStatusContext yangPatchData(final @NonNull EffectiveModelContext modelContext,
             final @NonNull PatchContext patch, final @Nullable DOMMountPoint mountPoint) {
-        return getRestconfStrategy(modelContext, mountPoint).patchData(patch);
+        return server.getRestconfStrategy(modelContext, mountPoint).patchData(patch);
     }
 
     private static @NonNull PatchContext parsePatchBody(final @NonNull EffectiveModelContext context,
@@ -799,53 +807,25 @@ public final class RestconfDataServiceImpl {
         }
     }
 
-    @VisibleForTesting
-    @NonNull RestconfStrategy getRestconfStrategy(final EffectiveModelContext modelContext,
-            final @Nullable DOMMountPoint mountPoint) {
-        if (mountPoint == null) {
-            return localStrategy(modelContext);
-        }
-
-        final var ret = RestconfStrategy.forMountPoint(modelContext, mountPoint);
-        if (ret == null) {
-            final var mountId = mountPoint.getIdentifier();
-            LOG.warn("Mount point {} does not expose a suitable access interface", mountId);
-            throw new RestconfDocumentedException("Could not find a supported access interface in mount point "
-                + mountId);
-        }
-        return ret;
-    }
-
-    private @NonNull RestconfStrategy localStrategy(final EffectiveModelContext modelContext) {
-        final var local = (RestconfStrategy) LOCAL_STRATEGY.getAcquire(this);
-        if (local != null && modelContext.equals(local.modelContext())) {
-            return local;
-        }
-
-        final var created = new MdsalRestconfStrategy(modelContext, dataBroker);
-        LOCAL_STRATEGY.setRelease(this, created);
-        return created;
-    }
-
     /**
      * Invoke Action operation.
      *
      * @param payload {@link NormalizedNodePayload} - the body of the operation
      * @return {@link NormalizedNodePayload} wrapped in {@link Response}
      */
-    private Response invokeAction(final InstanceIdentifierContext context, final OperationInputBody body) {
-        final var yangIIdContext = context.getInstanceIdentifier();
+    private Response invokeAction(final InstanceIdentifierContext reqPath, final OperationInputBody body) {
+        final var yangIIdContext = reqPath.getInstanceIdentifier();
         final ContainerNode input;
         try {
-            input = body.toContainerNode(context.inference());
+            input = body.toContainerNode(reqPath.inference());
         } catch (IOException e) {
             LOG.debug("Error reading input", e);
             throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
                     ErrorTag.MALFORMED_MESSAGE, e);
         }
 
-        final var mountPoint = context.getMountPoint();
-        final var inference = context.inference();
+        final var mountPoint = reqPath.getMountPoint();
+        final var inference = reqPath.inference();
         final var schemaPath = inference.toSchemaInferenceStack().toSchemaNodeIdentifier();
         final var response = mountPoint != null ? invokeAction(input, schemaPath, yangIIdContext, mountPoint)
             : invokeAction(input, schemaPath, yangIIdContext, actionService);
index cbd309685573490555203d39dd176db939bad968..0fc3d26c115d91a00c54cb97031880f8e682befa 100644 (file)
@@ -9,14 +9,12 @@ package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
 
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.List;
+import java.util.Optional;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.Encoded;
 import javax.ws.rs.POST;
@@ -29,31 +27,23 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.mdsal.dom.api.DOMRpcException;
-import org.opendaylight.mdsal.dom.api.DOMRpcResult;
-import org.opendaylight.mdsal.dom.api.DOMRpcService;
-import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfFuture;
 import org.opendaylight.restconf.nb.rfc8040.MediaTypes;
+import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
 import org.opendaylight.restconf.nb.rfc8040.databind.JsonOperationInputBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.XmlOperationInputBody;
+import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.device.notification.rev221106.SubscribeDeviceNotification;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscription;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,14 +56,16 @@ public final class RestconfInvokeOperationsServiceImpl {
     private static final Logger LOG = LoggerFactory.getLogger(RestconfInvokeOperationsServiceImpl.class);
 
     private final DatabindProvider databindProvider;
-    private final DOMRpcService rpcService;
+    private final MdsalRestconfServer server;
+    @Deprecated(forRemoval = true)
     private final DOMMountPointService mountPointService;
     private final SubscribeToStreamUtil streamUtils;
 
-    public RestconfInvokeOperationsServiceImpl(final DatabindProvider databindProvider, final DOMRpcService rpcService,
-            final DOMMountPointService mountPointService, final StreamsConfiguration configuration) {
+    public RestconfInvokeOperationsServiceImpl(final DatabindProvider databindProvider,
+            final MdsalRestconfServer server, final DOMMountPointService mountPointService,
+            final StreamsConfiguration configuration) {
         this.databindProvider = requireNonNull(databindProvider);
-        this.rpcService = requireNonNull(rpcService);
+        this.server = requireNonNull(server);
         this.mountPointService = requireNonNull(mountPointService);
         streamUtils = configuration.useSSE() ? SubscribeToStreamUtil.serverSentEvents()
             : SubscribeToStreamUtil.webSockets();
@@ -140,53 +132,29 @@ public final class RestconfInvokeOperationsServiceImpl {
 
     private void invokeRpc(final String identifier, final UriInfo uriInfo, final AsyncResponse ar,
             final OperationInputBody body) {
-        final var dataBind = databindProvider.currentContext();
-        final var schemaContext = dataBind.modelContext();
-        final var context = ParserIdentifier.toInstanceIdentifier(identifier, schemaContext, mountPointService);
+        final var databind = databindProvider.currentContext();
+        final var reqPath = server.bindRequestPath(databind, identifier);
 
         final ContainerNode input;
         try {
-            input = body.toContainerNode(context.inference());
+            input = body.toContainerNode(reqPath.inference());
         } catch (IOException e) {
             LOG.debug("Error reading input", e);
             throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
                     ErrorTag.MALFORMED_MESSAGE, e);
         }
-        final var rpcName = context.getSchemaNode().getQName();
 
-        final ListenableFuture<? extends DOMRpcResult> future;
-        final var mountPoint = context.getMountPoint();
-        if (mountPoint == null) {
-            if (CreateDataChangeEventSubscription.QNAME.equals(rpcName)) {
-                future = Futures.immediateFuture(CreateStreamUtil.createDataChangeNotifiStream(
-                    streamUtils.listenersBroker(), input, schemaContext));
-            } else if (SubscribeDeviceNotification.QNAME.equals(rpcName)) {
-                final String baseUrl = streamUtils.prepareUriByStreamName(uriInfo, "").toString();
-                future = Futures.immediateFuture(CreateStreamUtil.createDeviceNotificationListener(baseUrl, input,
-                    streamUtils, mountPointService));
-            } else {
-                future = invokeRpc(input, rpcName, rpcService);
-            }
-        } else {
-            future = invokeRpc(input, rpcName, mountPoint);
-        }
-
-        Futures.addCallback(future, new FutureCallback<DOMRpcResult>() {
+        Futures.addCallback(hackInvokeRpc(databind, reqPath, uriInfo, input), new FutureCallback<>() {
             @Override
-            public void onSuccess(final DOMRpcResult response) {
-                final var errors = response.errors();
-                if (!errors.isEmpty()) {
-                    LOG.debug("RpcError message {}", response.errors());
-                    ar.resume(new RestconfDocumentedException("RPCerror message ", null, response.errors()));
-                    return;
-                }
-
-                final ContainerNode resultData = response.value();
-                if (resultData == null || resultData.isEmpty()) {
-                    ar.resume(Response.noContent().build());
-                } else {
-                    ar.resume(Response.ok().entity(new NormalizedNodePayload(context.inference(), resultData)).build());
+            public void onSuccess(final Optional<ContainerNode> result) {
+                if (result.isPresent()) {
+                    final var output = result.orElseThrow();
+                    if (!output.isEmpty()) {
+                        ar.resume(Response.ok().entity(new NormalizedNodePayload(reqPath.inference(), output)).build());
+                        return;
+                    }
                 }
+                ar.resume(Response.noContent().build());
             }
 
             @Override
@@ -196,43 +164,23 @@ public final class RestconfInvokeOperationsServiceImpl {
         }, MoreExecutors.directExecutor());
     }
 
-    /**
-     * Invoking rpc via mount point.
-     *
-     * @param mountPoint mount point
-     * @param data input data
-     * @param rpc RPC type
-     * @return {@link DOMRpcResult}
-     */
-    @VisibleForTesting
-    static ListenableFuture<? extends DOMRpcResult> invokeRpc(final ContainerNode data, final QName rpc,
-            final DOMMountPoint mountPoint) {
-        return invokeRpc(data, rpc, mountPoint.getService(DOMRpcService.class).orElseThrow(() -> {
-            final String errmsg = "RPC service is missing.";
-            LOG.debug(errmsg);
-            return new RestconfDocumentedException(errmsg);
-        }));
-    }
-
-    /**
-     * Invoke rpc.
-     *
-     * @param input input data
-     * @param rpc RPC type
-     * @param rpcService rpc service to invoke rpc
-     * @return {@link DOMRpcResult}
-     */
-    @VisibleForTesting
-    static ListenableFuture<? extends DOMRpcResult> invokeRpc(final ContainerNode input, final QName rpc,
-            final DOMRpcService rpcService) {
-        return Futures.catching(rpcService.invokeRpc(rpc, nonnullInput(rpc, input)), DOMRpcException.class,
-            cause -> new DefaultDOMRpcResult(List.of(RpcResultBuilder.newError(ErrorType.RPC, ErrorTag.OPERATION_FAILED,
-                cause.getMessage()))),
-            MoreExecutors.directExecutor());
-    }
+    private RestconfFuture<Optional<ContainerNode>> hackInvokeRpc(final DatabindContext localDatabind,
+            final InstanceIdentifierContext reqPath, final UriInfo uriInfo, final ContainerNode input) {
+        // RPC type
+        final var type = reqPath.getSchemaNode().getQName();
+        final var mountPoint = reqPath.getMountPoint();
+        if (mountPoint == null) {
+            // Hacked-up integration of streams
+            if (CreateDataChangeEventSubscription.QNAME.equals(type)) {
+                return RestconfFuture.of(Optional.of(CreateStreamUtil.createDataChangeNotifiStream(
+                    streamUtils.listenersBroker(), input, localDatabind.modelContext())));
+            } else if (SubscribeDeviceNotification.QNAME.equals(type)) {
+                final var baseUrl = streamUtils.prepareUriByStreamName(uriInfo, "").toString();
+                return RestconfFuture.of(Optional.of(CreateStreamUtil.createDeviceNotificationListener(baseUrl, input,
+                    streamUtils, mountPointService)));
+            }
+        }
 
-    private static @NonNull ContainerNode nonnullInput(final QName type, final ContainerNode input) {
-        return input != null ? input
-                : ImmutableNodes.containerNode(YangConstants.operationInputQName(type.getModule()));
+        return server.getRestconfStrategy(reqPath.getSchemaContext(), mountPoint).invokeRpc(type, input);
     }
 }
index 84e2c78b9d365c628bdcec1d1788a4d2ce1e5881..7368688f148d05520a9d4270f874649c621aa0e4 100644 (file)
@@ -14,10 +14,8 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
-import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.restconf.nb.rfc8040.MediaTypes;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 /**
@@ -26,18 +24,17 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 @Path("/")
 public final class RestconfOperationsServiceImpl {
     private final DatabindProvider databindProvider;
-    private final DOMMountPointService mountPointService;
+    private final MdsalRestconfServer server;
 
     /**
      * Set {@link DatabindProvider} for getting actual {@link EffectiveModelContext}.
      *
      * @param databindProvider a {@link DatabindProvider}
-     * @param mountPointService a {@link DOMMountPointService}
+     * @param server a {@link MdsalRestconfServer}
      */
-    public RestconfOperationsServiceImpl(final DatabindProvider databindProvider,
-            final DOMMountPointService mountPointService) {
+    public RestconfOperationsServiceImpl(final DatabindProvider databindProvider, final MdsalRestconfServer server) {
         this.databindProvider = requireNonNull(databindProvider);
-        this.mountPointService = requireNonNull(mountPointService);
+        this.server = requireNonNull(server);
     }
 
     /**
@@ -49,7 +46,8 @@ public final class RestconfOperationsServiceImpl {
     @Path("/operations")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
     public String getOperationsJSON() {
-        return OperationsContent.JSON.bodyFor(databindProvider.currentContext().modelContext());
+        return OperationsContent.JSON.bodyFor(
+            server.bindRequestRoot(databindProvider.currentContext()).inference());
     }
 
     /**
@@ -62,8 +60,8 @@ public final class RestconfOperationsServiceImpl {
     @Path("/operations/{identifier:.+}")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
     public String getOperationJSON(@PathParam("identifier") final String identifier) {
-        return OperationsContent.JSON.bodyFor(ParserIdentifier.toInstanceIdentifier(identifier,
-            databindProvider.currentContext().modelContext(), mountPointService).inference());
+        return OperationsContent.JSON.bodyFor(
+            server.bindRequestPath(databindProvider.currentContext(), identifier).inference());
     }
 
     /**
@@ -75,7 +73,8 @@ public final class RestconfOperationsServiceImpl {
     @Path("/operations")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public String getOperationsXML() {
-        return OperationsContent.XML.bodyFor(databindProvider.currentContext().modelContext());
+        return OperationsContent.XML.bodyFor(
+            server.bindRequestRoot(databindProvider.currentContext()).inference());
     }
 
     /**
@@ -88,7 +87,7 @@ public final class RestconfOperationsServiceImpl {
     @Path("/operations/{identifier:.+}")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public String getOperationXML(@PathParam("identifier") final String identifier) {
-        return OperationsContent.XML.bodyFor(ParserIdentifier.toInstanceIdentifier(identifier,
-            databindProvider.currentContext().modelContext(), mountPointService).inference());
+        return OperationsContent.XML.bodyFor(
+            server.bindRequestPath(databindProvider.currentContext(), identifier).inference());
     }
 }
index 30aca2def9da2ac7bf386a2cafd1f80265396cb2..5400c52b363922f598a4ba8de59cd9a51894c7dd 100644 (file)
@@ -16,10 +16,12 @@ import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.util.List;
 import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.SettableRestconfFuture;
@@ -39,8 +41,9 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 public final class MdsalRestconfStrategy extends RestconfStrategy {
     private final DOMDataBroker dataBroker;
 
-    public MdsalRestconfStrategy(final EffectiveModelContext modelContext, final DOMDataBroker dataBroker) {
-        super(modelContext);
+    public MdsalRestconfStrategy(final EffectiveModelContext modelContext, final DOMDataBroker dataBroker,
+            final @Nullable DOMRpcService rpcService) {
+        super(modelContext, rpcService);
         this.dataBroker = requireNonNull(dataBroker);
     }
 
index 60fd463c04336dbcf9885a5d396391a627deff57..a3f567ed200ae4a9fa824fef636dd354c6f8a4f3 100644 (file)
@@ -16,9 +16,11 @@ import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
 import java.util.List;
 import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.common.api.ReadFailedException;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.errors.SettableRestconfFuture;
 import org.opendaylight.yangtools.yang.common.Empty;
@@ -35,8 +37,8 @@ public final class NetconfRestconfStrategy extends RestconfStrategy {
     private final NetconfDataTreeService netconfService;
 
     public NetconfRestconfStrategy(final EffectiveModelContext modelContext,
-            final NetconfDataTreeService netconfService) {
-        super(modelContext);
+            final NetconfDataTreeService netconfService, final @Nullable DOMRpcService rpcService) {
+        super(modelContext, rpcService);
         this.netconfService = requireNonNull(netconfService);
     }
 
index 1b33afda3a2933d769af42febb61a62b8d778ff3..90d0d3729092255c2a59e35b58ccea6caf7ef96e 100644 (file)
@@ -28,6 +28,8 @@ import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.api.query.ContentParam;
@@ -109,9 +111,11 @@ public abstract class RestconfStrategy {
     private static final Logger LOG = LoggerFactory.getLogger(RestconfStrategy.class);
 
     private final @NonNull EffectiveModelContext modelContext;
+    private final @Nullable DOMRpcService rpcService;
 
-    RestconfStrategy(final EffectiveModelContext modelContext) {
+    RestconfStrategy(final EffectiveModelContext modelContext, final @Nullable DOMRpcService rpcService) {
         this.modelContext = requireNonNull(modelContext);
+        this.rpcService = rpcService;
     }
 
     /**
@@ -124,13 +128,15 @@ public abstract class RestconfStrategy {
      */
     public static @Nullable RestconfStrategy forMountPoint(final EffectiveModelContext modelContext,
             final DOMMountPoint mountPoint) {
+        final var rpcService = mountPoint.getService(DOMRpcService.class).orElse(null);
+
         final var netconfService = mountPoint.getService(NetconfDataTreeService.class);
         if (netconfService.isPresent()) {
-            return new NetconfRestconfStrategy(modelContext, netconfService.orElseThrow());
+            return new NetconfRestconfStrategy(modelContext, netconfService.orElseThrow(), rpcService);
         }
         final var dataBroker = mountPoint.getService(DOMDataBroker.class);
         if (dataBroker.isPresent()) {
-            return new MdsalRestconfStrategy(modelContext, dataBroker.orElseThrow());
+            return new MdsalRestconfStrategy(modelContext, dataBroker.orElseThrow(), rpcService);
         }
         return null;
     }
@@ -995,4 +1001,43 @@ public abstract class RestconfStrategy {
         configMap.entrySet().stream().filter(x -> stateMap.containsKey(x.getKey())).forEach(
             y -> builder.addChild((T) prepareData(y.getValue(), stateMap.get(y.getKey()))));
     }
+
+    public @NonNull RestconfFuture<Optional<ContainerNode>> invokeRpc(final QName type, final ContainerNode input) {
+        final var ret = new SettableRestconfFuture<Optional<ContainerNode>>();
+
+        final var local = rpcService;
+        if (local != null) {
+            Futures.addCallback(local.invokeRpc(requireNonNull(type), requireNonNull(input)),
+                new FutureCallback<DOMRpcResult>() {
+                    @Override
+                    public void onSuccess(final DOMRpcResult response) {
+                        final var errors = response.errors();
+                        if (errors.isEmpty()) {
+                            ret.set(Optional.ofNullable(response.value()));
+                        } else {
+                            LOG.debug("RPC invocation reported {}", response.errors());
+                            ret.setFailure(new RestconfDocumentedException("RPC implementation reported errors", null,
+                                response.errors()));
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(final Throwable cause) {
+                        LOG.debug("RPC invocation failed, cause");
+                        if (cause instanceof RestconfDocumentedException ex) {
+                            ret.setFailure(ex);
+                        } else {
+                            // TODO: YangNetconfErrorAware if we ever get into a broader invocation scope
+                            ret.setFailure(new RestconfDocumentedException(cause,
+                                new RestconfError(ErrorType.RPC, ErrorTag.OPERATION_FAILED, cause.getMessage())));
+                        }
+                    }
+                }, MoreExecutors.directExecutor());
+        } else {
+            LOG.debug("RPC invocation is not available");
+            ret.setFailure(new RestconfDocumentedException("RPC invocation is not available",
+                ErrorType.PROTOCOL, ErrorTag.OPERATION_NOT_SUPPORTED));
+        }
+        return ret;
+    }
 }
index 153af880c955f7fb991e59b5b6928af9d8b9ba2a..804e0149d98441bba353f82eb3cadb469f4e8e95 100644 (file)
@@ -13,13 +13,11 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 
-import java.util.List;
 import java.util.function.Function;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenersBroker;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
@@ -51,14 +49,12 @@ public class CreateStreamUtilTest {
 
     @Test
     public void createStreamTest() {
-        final DOMRpcResult result = CreateStreamUtil.createDataChangeNotifiStream(listenersBroker,
-            prepareDomPayload("create-data-change-event-subscription", RpcDefinition::getInput, "toaster", "path"),
-            SCHEMA_CTX);
-        assertEquals(List.of(), result.errors());
         assertEquals(prepareDomPayload("create-data-change-event-subscription",
             RpcDefinition::getOutput,
             "data-change-event-subscription/toaster:toaster/datastore=CONFIGURATION/scope=BASE", "stream-name"),
-            result.value());
+            CreateStreamUtil.createDataChangeNotifiStream(listenersBroker,
+                prepareDomPayload("create-data-change-event-subscription", RpcDefinition::getInput, "toaster", "path"),
+                SCHEMA_CTX));
     }
 
     @Test
diff --git a/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/MdsalRestconfServerTest.java b/restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/MdsalRestconfServerTest.java
new file mode 100644 (file)
index 0000000..1b3f633
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.doReturn;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.mdsal.dom.api.DOMMountPoint;
+import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.AbstractJukeboxTest;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.NetconfRestconfStrategy;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class MdsalRestconfServerTest extends AbstractJukeboxTest {
+    @Mock
+    private DOMMountPointService mountPointService;
+    @Mock
+    private DOMMountPoint mountPoint;
+    @Mock
+    private DOMDataBroker dataBroker;
+    @Mock
+    private NetconfDataTreeService netconfService;
+    @Mock
+    private DOMRpcService rpcService;
+
+    private MdsalRestconfServer server;
+
+    @Before
+    public void before() {
+        server = new MdsalRestconfServer(dataBroker, rpcService, mountPointService);
+        doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
+    }
+
+    @Test
+    public void testGetRestconfStrategyLocal() {
+        assertInstanceOf(MdsalRestconfStrategy.class, server.getRestconfStrategy(JUKEBOX_SCHEMA, null));
+    }
+
+    @Test
+    public void testGetRestconfStrategyMountDataBroker() {
+        doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
+        doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
+        assertInstanceOf(MdsalRestconfStrategy.class, server.getRestconfStrategy(JUKEBOX_SCHEMA, mountPoint));
+    }
+
+    @Test
+    public void testGetRestconfStrategyMountNetconfService() {
+        doReturn(Optional.of(netconfService)).when(mountPoint).getService(NetconfDataTreeService.class);
+        assertInstanceOf(NetconfRestconfStrategy.class, server.getRestconfStrategy(JUKEBOX_SCHEMA, mountPoint));
+    }
+
+    @Test
+    public void testGetRestconfStrategyMountNone() {
+        doReturn(JUKEBOX_IID).when(mountPoint).getIdentifier();
+        doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
+        doReturn(Optional.empty()).when(mountPoint).getService(DOMDataBroker.class);
+        final var ex = assertThrows(RestconfDocumentedException.class,
+            () -> server.getRestconfStrategy(JUKEBOX_SCHEMA, mountPoint));
+        final var errors = ex.getErrors();
+        assertEquals(1, errors.size());
+        final var error = errors.get(0);
+        assertEquals(ErrorType.APPLICATION, error.getErrorType());
+        assertEquals(ErrorTag.OPERATION_FAILED, error.getErrorTag());
+        assertEquals("Could not find a supported access interface in mount point "
+            + "/(http://example.com/ns/example-jukebox?revision=2015-04-04)jukebox", error.getErrorMessage());
+        // FIXME: should be JUKEBOX_IID
+        assertNull(error.getErrorPath());
+    }
+}
index 452614e4c5943368afcfdb413710b0ce8a683ff8..8c0c252b5fc673386faf2da62ad356ec4941d5a5 100644 (file)
@@ -21,6 +21,7 @@ import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
 import org.opendaylight.restconf.nb.rfc8040.AbstractInstanceIdentifierTest;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
@@ -40,6 +41,8 @@ public class Netconf799Test extends AbstractInstanceIdentifierTest {
     @Mock
     private DOMActionService actionService;
     @Mock
+    private DOMRpcService rpcService;
+    @Mock
     private DOMMountPointService mountPointService;
     @Mock
     private RestconfStreamsSubscriptionService restconfStreamSubService;
@@ -50,8 +53,8 @@ public class Netconf799Test extends AbstractInstanceIdentifierTest {
             Builders.containerBuilder().withNodeIdentifier(NodeIdentifier.create(OUTPUT_QNAME)).build())))
             .when(actionService).invokeAction(eq(Absolute.of(CONT_QNAME, CONT1_QNAME, RESET_QNAME)), any(), any());
 
-        final var dataService = new RestconfDataServiceImpl(
-            () -> DatabindContext.ofModel(IID_SCHEMA), dataBroker, mountPointService, restconfStreamSubService,
+        final var dataService = new RestconfDataServiceImpl(() -> DatabindContext.ofModel(IID_SCHEMA),
+            new MdsalRestconfServer(dataBroker, rpcService, mountPointService), dataBroker, restconfStreamSubService,
             actionService, new StreamsConfiguration(0, 1, 0, false));
 
         final var response = dataService.postDataJSON("instance-identifier-module:cont/cont1/reset",
index a7cccb1e10256f772caada1fbaa7d09087237b1a..7c8c9bbf4506f30ec98704d5d511cd0dbd2ab767 100644 (file)
@@ -12,20 +12,19 @@ import static org.junit.Assert.assertEquals;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
-import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class Netconf822Test {
-    private static Inference INFERENCE;
+    private static final Absolute NEW1 = Absolute.of(QName.create("foo", "2021-09-30", "new1"));
+
+    private static EffectiveModelContext SCHEMA;
 
     @BeforeClass
     public static void beforeClass() {
-        final var context = YangParserTestUtils.parseYangResourceDirectory("/nc822");
-        INFERENCE = SchemaInferenceStack.of(context,
-            Absolute.of(QName.create("foo", "new1", Revision.of("2021-09-30")))).toInference();
+        SCHEMA = YangParserTestUtils.parseYangResourceDirectory("/nc822");
     }
 
     @Test
@@ -36,7 +35,7 @@ public class Netconf822Test {
                 "foo:new": [null],
                 "foo:new1": [null]
               }
-            }""", OperationsContent.JSON.bodyFor(INFERENCE.getEffectiveModelContext()));
+            }""", OperationsContent.JSON.bodyFor(SchemaInferenceStack.of(SCHEMA).toInference()));
     }
 
     @Test
@@ -46,7 +45,7 @@ public class Netconf822Test {
               "ietf-restconf:operations" : {
                 "foo:new1": [null]
               }
-            }""", OperationsContent.JSON.bodyFor(INFERENCE));
+            }""", OperationsContent.JSON.bodyFor(SchemaInferenceStack.of(SCHEMA, NEW1).toInference()));
     }
 
     @Test
@@ -57,7 +56,7 @@ public class Netconf822Test {
                         xmlns:ns0="foo" >
               <ns0:new/>
               <ns0:new1/>
-            </operations>""", OperationsContent.XML.bodyFor(INFERENCE.getEffectiveModelContext()));
+            </operations>""", OperationsContent.XML.bodyFor(SchemaInferenceStack.of(SCHEMA).toInference()));
     }
 
     @Test
@@ -67,6 +66,6 @@ public class Netconf822Test {
             <operations xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"
                         xmlns:ns0="foo" >
               <ns0:new1/>
-            </operations>""", OperationsContent.XML.bodyFor(INFERENCE));
+            </operations>""", OperationsContent.XML.bodyFor(SchemaInferenceStack.of(SCHEMA, NEW1).toInference()));
     }
 }
index e4e83a1cdb521fc7dc67a16243390bec5d7e69bb..fdd288311092c063ec871722cd43bcfd53af27af 100644 (file)
@@ -49,6 +49,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService;
 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
@@ -60,8 +61,6 @@ import org.opendaylight.restconf.nb.rfc8040.AbstractJukeboxTest;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 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.transactions.NetconfRestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.patch.rev170222.yang.patch.yang.patch.Edit.Operation;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
@@ -111,6 +110,8 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
     @Mock
     private DOMActionService actionService;
     @Mock
+    private DOMRpcService rpcService;
+    @Mock
     private RestconfStreamsSubscriptionService delegRestconfSubscrService;
     @Mock
     private MultivaluedMap<String, String> queryParamenters;
@@ -128,13 +129,15 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         doReturn(read).when(dataBroker).newReadOnlyTransaction();
         doReturn(readWrite).when(dataBroker).newReadWriteTransaction();
 
-        dataService = new RestconfDataServiceImpl(() -> DatabindContext.ofModel(JUKEBOX_SCHEMA), dataBroker,
-                mountPointService, delegRestconfSubscrService, actionService, new StreamsConfiguration(0, 1, 0, false));
+        dataService = new RestconfDataServiceImpl(() -> DatabindContext.ofModel(JUKEBOX_SCHEMA),
+            new MdsalRestconfServer(dataBroker, rpcService, mountPointService), dataBroker, delegRestconfSubscrService,
+            actionService, new StreamsConfiguration(0, 1, 0, false));
         doReturn(Optional.of(mountPoint)).when(mountPointService)
                 .getMountPoint(any(YangInstanceIdentifier.class));
         doReturn(Optional.of(FixedDOMSchemaService.of(JUKEBOX_SCHEMA))).when(mountPoint)
                 .getService(DOMSchemaService.class);
         doReturn(Optional.of(mountDataBroker)).when(mountPoint).getService(DOMDataBroker.class);
+        doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
         doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
         doReturn(read).when(mountDataBroker).newReadOnlyTransaction();
         doReturn(readWrite).when(mountDataBroker).newReadWriteTransaction();
@@ -460,14 +463,4 @@ public class RestconfDataServiceImplTest extends AbstractJukeboxTest {
         final String errorMessage = status.editCollection().get(2).getEditErrors().get(0).getErrorMessage();
         assertEquals("Data does not exist", errorMessage);
     }
-
-    @Test
-    public void testGetRestconfStrategy() {
-        var restconfStrategy = dataService.getRestconfStrategy(JUKEBOX_SCHEMA, mountPoint);
-        assertTrue(restconfStrategy instanceof MdsalRestconfStrategy);
-
-        doReturn(Optional.of(netconfService)).when(mountPoint).getService(NetconfDataTreeService.class);
-        restconfStrategy = dataService.getRestconfStrategy(JUKEBOX_SCHEMA, mountPoint);
-        assertTrue(restconfStrategy instanceof NetconfRestconfStrategy);
-    }
 }
index ea8b545a5398c119419b6790de82e0dd9558e5e7..ba9af579faacffae79a68b92e64e65cbd745ef96 100644 (file)
@@ -7,22 +7,19 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
-import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 
+import com.google.common.util.concurrent.Futures;
 import java.io.ByteArrayInputStream;
 import java.nio.charset.StandardCharsets;
-import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
@@ -36,13 +33,14 @@ import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.mdsal.dom.api.DOMRpcException;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
@@ -50,7 +48,6 @@ import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
@@ -71,13 +68,17 @@ public class RestconfInvokeOperationsServiceImplTest {
 
     private static DatabindContext CONTEXT;
 
+    @Mock
+    private DOMDataBroker dataBroker;
     @Mock
     private DOMRpcService rpcService;
     @Mock
     private DOMMountPoint mountPoint;
     @Mock
     private DOMMountPointService mountPointService;
+
     private RestconfInvokeOperationsServiceImpl invokeOperationsService;
+    private MdsalRestconfServer server;
 
     @BeforeClass
     public static void beforeClass() {
@@ -86,7 +87,8 @@ public class RestconfInvokeOperationsServiceImplTest {
 
     @Before
     public void setup() {
-        invokeOperationsService = new RestconfInvokeOperationsServiceImpl(() -> CONTEXT, rpcService, mountPointService,
+        server = new MdsalRestconfServer(dataBroker, rpcService, mountPointService);
+        invokeOperationsService = new RestconfInvokeOperationsServiceImpl(() -> CONTEXT, server, mountPointService,
             new StreamsConfiguration(0, 1, 0, false));
     }
 
@@ -116,74 +118,82 @@ public class RestconfInvokeOperationsServiceImplTest {
 
         prepNNC(result);
         final var ar = mock(AsyncResponse.class);
-        final var captor = ArgumentCaptor.forClass(Response.class);
+        final var response = ArgumentCaptor.forClass(Response.class);
         invokeOperationsService.invokeRpcJSON("invoke-rpc-module:rpc-test", new ByteArrayInputStream("""
             {
               "invoke-rpc-module:input" : {
               }
             }
             """.getBytes(StandardCharsets.UTF_8)), mock(UriInfo.class), ar);
-        verify(ar).resume(captor.capture());
+        verify(ar).resume(response.capture());
 
-        final var response = captor.getValue();
-        assertEquals(204, response.getStatus());
+        assertEquals(204, response.getValue().getStatus());
     }
 
     @Test
-    public void invokeRpcTest() throws InterruptedException, ExecutionException {
-        final DOMRpcResult mockResult = new DefaultDOMRpcResult(OUTPUT, List.of());
-        doReturn(immediateFluentFuture(mockResult)).when(rpcService).invokeRpc(RPC, INPUT);
-        final DOMRpcResult rpcResult = RestconfInvokeOperationsServiceImpl.invokeRpc(INPUT, RPC, rpcService).get();
-        assertTrue(rpcResult.errors().isEmpty());
-        assertEquals(OUTPUT, rpcResult.value());
+    public void invokeRpcTest() throws Exception {
+        doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
+            .invokeRpc(RPC, INPUT);
+        assertEquals(Optional.of(OUTPUT), Futures.getDone(server.getRestconfStrategy(CONTEXT.modelContext(), null)
+            .invokeRpc(RPC, INPUT)));
     }
 
     @Test
-    public void invokeRpcErrorsAndCheckTestTest() throws InterruptedException, ExecutionException {
-        final QName errorRpc = QName.create(RPC, "error-rpc");
-        final DOMRpcException exception = new DOMRpcImplementationNotAvailableException(
+    public void invokeRpcErrorsAndCheckTestTest() throws Exception {
+        final var errorRpc = QName.create(RPC, "error-rpc");
+        final var exception = new DOMRpcImplementationNotAvailableException(
                 "No implementation of RPC " + errorRpc + " available.");
-        doReturn(immediateFailedFluentFuture(exception)).when(rpcService).invokeRpc(errorRpc, INPUT);
-        final DOMRpcResult rpcResult = RestconfInvokeOperationsServiceImpl.invokeRpc(INPUT, errorRpc, rpcService).get();
-        assertNull(rpcResult.value());
-        final Collection<? extends RpcError> errorList = rpcResult.errors();
+        doReturn(Futures.immediateFailedFuture(exception)).when(rpcService).invokeRpc(errorRpc, INPUT);
+        final var ex = assertInstanceOf(RestconfDocumentedException.class,
+            assertThrows(ExecutionException.class, () -> Futures.getDone(
+                server.getRestconfStrategy(CONTEXT.modelContext(), null).invokeRpc(errorRpc, INPUT))).getCause());
+        final var errorList = ex.getErrors();
         assertEquals(1, errorList.size());
-        final RpcError actual = errorList.iterator().next();
-        assertEquals("No implementation of RPC " + errorRpc + " available.", actual.getMessage());
-        assertEquals(ErrorTag.OPERATION_FAILED, actual.getTag());
+        final var actual = errorList.iterator().next();
+        assertEquals("No implementation of RPC " + errorRpc + " available.", actual.getErrorMessage());
         assertEquals(ErrorType.RPC, actual.getErrorType());
+        assertEquals(ErrorTag.OPERATION_FAILED, actual.getErrorTag());
     }
 
     @Test
-    public void invokeRpcViaMountPointTest() throws InterruptedException, ExecutionException {
-        doReturn(Optional.ofNullable(rpcService)).when(mountPoint).getService(DOMRpcService.class);
-        final DOMRpcResult mockResult = new DefaultDOMRpcResult(OUTPUT, List.of());
-        doReturn(immediateFluentFuture(mockResult)).when(rpcService).invokeRpc(RPC, INPUT);
-        final DOMRpcResult rpcResult = RestconfInvokeOperationsServiceImpl.invokeRpc(INPUT, RPC, mountPoint).get();
-        assertTrue(rpcResult.errors().isEmpty());
-        assertEquals(OUTPUT, rpcResult.value());
+    public void invokeRpcViaMountPointTest() throws Exception {
+        doReturn(Optional.of(rpcService)).when(mountPoint).getService(DOMRpcService.class);
+        doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
+        doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
+        doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of()))).when(rpcService)
+            .invokeRpc(RPC, INPUT);
+        assertEquals(Optional.of(OUTPUT), Futures.getDone(
+            server.getRestconfStrategy(CONTEXT.modelContext(), mountPoint).invokeRpc(RPC, INPUT)));
     }
 
     @Test
     public void invokeRpcMissingMountPointServiceTest() {
         doReturn(Optional.empty()).when(mountPoint).getService(DOMRpcService.class);
-        assertThrows(RestconfDocumentedException.class,
-            () -> RestconfInvokeOperationsServiceImpl.invokeRpc(INPUT, RPC, mountPoint));
+        doReturn(Optional.empty()).when(mountPoint).getService(NetconfDataTreeService.class);
+        doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
+        final var strategy = server.getRestconfStrategy(CONTEXT.modelContext(), mountPoint);
+        final var ex = assertInstanceOf(RestconfDocumentedException.class,
+            assertThrows(ExecutionException.class, () -> Futures.getDone(strategy.invokeRpc(RPC, INPUT))).getCause());
+        final var errors = ex.getErrors();
+        assertEquals(1, errors.size());
+        final var error = errors.get(0);
+        assertEquals(ErrorType.PROTOCOL, error.getErrorType());
+        assertEquals(ErrorTag.OPERATION_NOT_SUPPORTED, error.getErrorTag());
+        assertEquals("RPC invocation is not available", error.getErrorMessage());
     }
 
     @Test
-    public void checkResponseTest() throws InterruptedException, ExecutionException {
-        doReturn(immediateFluentFuture(new DefaultDOMRpcResult(OUTPUT, List.of())))
+    public void checkResponseTest() throws Exception {
+        doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(OUTPUT, List.of())))
             .when(rpcService).invokeRpc(RPC, INPUT);
-        final DOMRpcResult rpcResult = RestconfInvokeOperationsServiceImpl.invokeRpc(INPUT, RPC, rpcService).get();
-        assertTrue(rpcResult.errors().isEmpty());
-        assertEquals(OUTPUT, rpcResult.value());
+        assertEquals(Optional.of(OUTPUT), Futures.getDone(
+            server.getRestconfStrategy(CONTEXT.modelContext(), null).invokeRpc(RPC, INPUT)));
     }
 
     private void prepNNC(final ContainerNode result) {
-        final QName qname = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
-        final DOMRpcResult domRpcResult = mock(DOMRpcResult.class);
-        doReturn(immediateFluentFuture(domRpcResult)).when(rpcService).invokeRpc(eq(qname), any(ContainerNode.class));
+        final var qname = QName.create("invoke:rpc:module", "2013-12-03", "rpc-test");
+        final var domRpcResult = mock(DOMRpcResult.class);
+        doReturn(Futures.immediateFuture(domRpcResult)).when(rpcService).invokeRpc(eq(qname), any(ContainerNode.class));
         doReturn(result).when(domRpcResult).value();
     }
 }
index 4ed44d9396c247084f4e2feadf900733b50124b6..79768ec77ed229e59cb376ec037ea7c01968cd21 100644 (file)
@@ -19,8 +19,10 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.mdsal.binding.runtime.spi.BindingRuntimeHelpers;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
 import org.opendaylight.yang.gen.v1.module._1.rev140101.Module1Data;
@@ -61,6 +63,10 @@ public class RestconfOperationsServiceImplTest {
     private DOMMountPoint mountPoint;
     @Mock
     private DOMSchemaService schemaService;
+    @Mock
+    private DOMDataBroker dataBroker;
+    @Mock
+    private DOMRpcService rpcService;
 
     private RestconfOperationsServiceImpl opService;
 
@@ -75,7 +81,9 @@ public class RestconfOperationsServiceImplTest {
         doReturn(SCHEMA).when(schemaService).getGlobalContext();
         doReturn(Optional.of(schemaService)).when(mountPoint).getService(DOMSchemaService.class);
         doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any());
-        opService = new RestconfOperationsServiceImpl(() -> DatabindContext.ofModel(SCHEMA), mountPointService);
+
+        opService = new RestconfOperationsServiceImpl(() -> DatabindContext.ofModel(SCHEMA),
+            new MdsalRestconfServer(dataBroker, rpcService, mountPointService));
     }
 
     @Test
index 945f2ff6701e39ee63394845de6745a37d6cac65..f5a7fd786f7859441b77471281ae977a9e48d3f5 100644 (file)
@@ -30,6 +30,7 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.restconf.api.query.ContentParam;
 import org.opendaylight.restconf.api.query.WithDefaultsParam;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
@@ -55,6 +56,8 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
     private DOMDataBroker mockDataBroker;
     @Mock
     private DOMDataTreeReadTransaction read;
+    @Mock
+    private DOMRpcService rpcService;
 
     @BeforeClass
     public static void setupModulesSchema() {
@@ -77,7 +80,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         // assert that data to delete exists
         when(readWrite.exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.of()))
             .thenReturn(immediateTrueFluentFuture());
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
@@ -85,7 +88,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         // assert that data to delete does NOT exist
         when(readWrite.exists(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.of()))
             .thenReturn(immediateFalseFluentFuture());
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
@@ -93,7 +96,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, entryNode);
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
@@ -101,7 +104,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doReturn(immediateFailedFluentFuture(domException)).when(readWrite).commit();
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX);
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Test
@@ -112,7 +115,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX);
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
 
-        new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker).putData(JUKEBOX_IID, EMPTY_JUKEBOX, null);
+        new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null).putData(JUKEBOX_IID, EMPTY_JUKEBOX, null);
         verify(read).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         verify(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX);
     }
@@ -125,7 +128,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, GAP_IID, GAP_LEAF);
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
 
-        new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker).putData(GAP_IID, GAP_LEAF, null);
+        new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null).putData(GAP_IID, GAP_LEAF, null);
         verify(read).exists(LogicalDatastoreType.CONFIGURATION, GAP_IID);
         verify(readWrite).put(LogicalDatastoreType.CONFIGURATION, GAP_IID, GAP_LEAF);
     }
@@ -140,7 +143,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, JUKEBOX_WITH_BANDS);
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
 
-        new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker).putData(JUKEBOX_IID, JUKEBOX_WITH_BANDS, null);
+        new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null).putData(JUKEBOX_IID, JUKEBOX_WITH_BANDS, null);
         verify(read).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         verify(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, JUKEBOX_WITH_BANDS);
     }
@@ -150,33 +153,33 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID);
         doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX);
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
     RestconfStrategy testPatchContainerDataStrategy() {
         doReturn(readWrite).when(mockDataBroker).newReadWriteTransaction();
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
     RestconfStrategy testPatchLeafDataStrategy() {
         doReturn(readWrite).when(mockDataBroker).newReadWriteTransaction();
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
     RestconfStrategy testPatchListDataStrategy() {
         doReturn(readWrite).when(mockDataBroker).newReadWriteTransaction();
         doReturn(CommitInfo.emptyFluentFuture()).when(readWrite).commit();
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
     RestconfStrategy testPatchDataReplaceMergeAndRemoveStrategy() {
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
@@ -184,19 +187,19 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, PLAYER_IID);
         doReturn(immediateTrueFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION,
             CREATE_AND_DELETE_TARGET);
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
     RestconfStrategy testPatchMergePutContainerStrategy() {
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
     RestconfStrategy deleteNonexistentDataTestStrategy() {
         doReturn(immediateFalseFluentFuture()).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION,
             CREATE_AND_DELETE_TARGET);
-        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker);
+        return new MdsalRestconfStrategy(JUKEBOX_SCHEMA, mockDataBroker, null);
     }
 
     @Override
@@ -215,7 +218,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(read).when(mockDataBroker).newReadOnlyTransaction();
         doReturn(immediateFluentFuture(Optional.of(DATA_3))).when(read)
             .read(LogicalDatastoreType.CONFIGURATION, PATH);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -225,7 +228,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.CONFIGURATION, PATH);
         doReturn(immediateFluentFuture(Optional.empty())).when(read)
             .read(LogicalDatastoreType.OPERATIONAL, PATH);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -235,7 +238,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.OPERATIONAL, PATH_2);
         doReturn(immediateFluentFuture(Optional.empty())).when(read)
             .read(LogicalDatastoreType.CONFIGURATION, PATH_2);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -243,7 +246,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(read).when(mockDataBroker).newReadOnlyTransaction();
         doReturn(immediateFluentFuture(Optional.of(DATA_2))).when(read)
             .read(LogicalDatastoreType.OPERATIONAL, PATH_2);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -253,7 +256,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.CONFIGURATION, PATH);
         doReturn(immediateFluentFuture(Optional.of(DATA_4))).when(read)
             .read(LogicalDatastoreType.OPERATIONAL, PATH);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -263,7 +266,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.CONFIGURATION, PATH);
         doReturn(immediateFluentFuture(Optional.of(DATA_4))).when(read)
             .read(LogicalDatastoreType.OPERATIONAL, PATH);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -273,7 +276,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.OPERATIONAL, PATH_3);
         doReturn(immediateFluentFuture(Optional.of(LIST_DATA_2))).when(read)
             .read(LogicalDatastoreType.CONFIGURATION, PATH_3);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -283,7 +286,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.OPERATIONAL, PATH_3);
         doReturn(immediateFluentFuture(Optional.of(ORDERED_MAP_NODE_2))).when(read)
             .read(LogicalDatastoreType.CONFIGURATION, PATH_3);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -293,7 +296,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.OPERATIONAL, PATH_3);
         doReturn(immediateFluentFuture(Optional.of(UNKEYED_LIST_NODE_2))).when(read)
             .read(LogicalDatastoreType.CONFIGURATION, PATH_3);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -303,7 +306,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.OPERATIONAL, LEAF_SET_NODE_PATH);
         doReturn(immediateFluentFuture(Optional.of(LEAF_SET_NODE_2))).when(read)
             .read(LogicalDatastoreType.CONFIGURATION, LEAF_SET_NODE_PATH);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
@@ -313,14 +316,14 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
             .read(LogicalDatastoreType.OPERATIONAL, LEAF_SET_NODE_PATH);
         doReturn(immediateFluentFuture(Optional.of(ORDERED_LEAF_SET_NODE_2))).when(read)
             .read(LogicalDatastoreType.CONFIGURATION, LEAF_SET_NODE_PATH);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Override
     RestconfStrategy readDataWrongPathOrNoContentTestStrategy() {
         doReturn(read).when(mockDataBroker).newReadOnlyTransaction();
         doReturn(immediateFluentFuture(Optional.empty())).when(read).read(LogicalDatastoreType.CONFIGURATION, PATH_2);
-        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker);
+        return new MdsalRestconfStrategy(mockSchemaContext, mockDataBroker, null);
     }
 
     @Test
@@ -336,8 +339,8 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(immediateFluentFuture(Optional.of(data))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, path);
 
-        assertEquals(data, new MdsalRestconfStrategy(MODULES_SCHEMA, mockDataBroker).readData(ContentParam.ALL, path,
-            WithDefaultsParam.TRIM));
+        assertEquals(data, new MdsalRestconfStrategy(MODULES_SCHEMA, mockDataBroker, null)
+            .readData(ContentParam.ALL, path, WithDefaultsParam.TRIM));
     }
 
     @Test
@@ -367,8 +370,8 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(immediateFluentFuture(Optional.of(data))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, path);
 
-        assertEquals(data, new MdsalRestconfStrategy(MODULES_SCHEMA, mockDataBroker).readData(ContentParam.ALL, path,
-            WithDefaultsParam.TRIM));
+        assertEquals(data, new MdsalRestconfStrategy(MODULES_SCHEMA, mockDataBroker, null)
+            .readData(ContentParam.ALL, path, WithDefaultsParam.TRIM));
     }
 
     @Test
@@ -391,7 +394,7 @@ public final class MdsalRestconfStrategyTest extends AbstractRestconfStrategyTes
         doReturn(immediateFluentFuture(Optional.of(content))).when(read)
                 .read(LogicalDatastoreType.OPERATIONAL, path);
 
-        assertEquals(content, new MdsalRestconfStrategy(MODULES_SCHEMA, mockDataBroker).readData(ContentParam.ALL, path,
-            WithDefaultsParam.TRIM));
+        assertEquals(content, new MdsalRestconfStrategy(MODULES_SCHEMA, mockDataBroker, null)
+            .readData(ContentParam.ALL, path, WithDefaultsParam.TRIM));
     }
 }
index 380273e63520e3b7078c771bb8a6a8c0f54067ad..b9c6494a557b66994bdec81980a6330b22aa8422 100644 (file)
@@ -55,7 +55,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
 
     @Override
     RestconfStrategy testDeleteDataStrategy() {
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -63,7 +63,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFailedFuture(new TransactionCommitFailedException(
             "Commit of transaction " + this + " failed", new NetconfDocumentedException("id",
                 ErrorType.RPC, ErrorTag.DATA_MISSING, ErrorSeverity.ERROR)))).when(netconfService).commit();
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -71,7 +71,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).commit();
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .create(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX, Optional.empty());
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -82,7 +82,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).commit();
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).create(
             LogicalDatastoreType.CONFIGURATION, node, entryNode, Optional.empty());
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -92,14 +92,14 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
             .create(any(), any(), any(), any());
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).discardChanges();
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).unlock();
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
     RestconfStrategy testPatchContainerDataStrategy() {
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).merge(any(), any(),any(),
             any());
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -107,7 +107,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .merge(any(), any(), any(), any());
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).commit();
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -115,7 +115,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService).commit();
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .merge(any(), any(),any(),any());
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Test
@@ -125,7 +125,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX, Optional.empty());
 
-        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService).putData(JUKEBOX_IID, EMPTY_JUKEBOX, null);
+        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null).putData(JUKEBOX_IID, EMPTY_JUKEBOX, null);
         verify(netconfService).lock();
         verify(netconfService).getConfig(JUKEBOX_IID);
         verify(netconfService).replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX,
@@ -140,7 +140,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX, Optional.empty());
 
-        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService).putData(JUKEBOX_IID, EMPTY_JUKEBOX, null);
+        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null).putData(JUKEBOX_IID, EMPTY_JUKEBOX, null);
         verify(netconfService).getConfig(JUKEBOX_IID);
         verify(netconfService).replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, EMPTY_JUKEBOX,
             Optional.empty());
@@ -153,7 +153,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .replace(LogicalDatastoreType.CONFIGURATION, GAP_IID, GAP_LEAF, Optional.empty());
 
-        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService).putData(GAP_IID, GAP_LEAF, null);
+        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null).putData(GAP_IID, GAP_LEAF, null);
         verify(netconfService).getConfig(GAP_IID);
         verify(netconfService).replace(LogicalDatastoreType.CONFIGURATION, GAP_IID, GAP_LEAF, Optional.empty());
     }
@@ -166,7 +166,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .replace(LogicalDatastoreType.CONFIGURATION, GAP_IID, GAP_LEAF, Optional.empty());
 
-        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService).putData(GAP_IID, GAP_LEAF, null);
+        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null).putData(GAP_IID, GAP_LEAF, null);
         verify(netconfService).getConfig(GAP_IID);
         verify(netconfService).replace(LogicalDatastoreType.CONFIGURATION, GAP_IID, GAP_LEAF, Optional.empty());
     }
@@ -178,7 +178,8 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, JUKEBOX_WITH_BANDS, Optional.empty());
 
-        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService).putData(JUKEBOX_IID, JUKEBOX_WITH_BANDS, null);
+        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null)
+            .putData(JUKEBOX_IID, JUKEBOX_WITH_BANDS, null);
         verify(netconfService).getConfig(JUKEBOX_IID);
         verify(netconfService).replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, JUKEBOX_WITH_BANDS,
             Optional.empty());
@@ -192,7 +193,8 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, JUKEBOX_WITH_BANDS, Optional.empty());
 
-        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService).putData(JUKEBOX_IID, JUKEBOX_WITH_BANDS, null);
+        new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null)
+            .putData(JUKEBOX_IID, JUKEBOX_WITH_BANDS, null);
         verify(netconfService).getConfig(JUKEBOX_IID);
         verify(netconfService).replace(LogicalDatastoreType.CONFIGURATION, JUKEBOX_IID, JUKEBOX_WITH_BANDS,
             Optional.empty());
@@ -205,7 +207,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             // FIXME: exact match
             .replace(any(), any(), any(), any());
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -214,12 +216,12 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
             .create(LogicalDatastoreType.CONFIGURATION, PLAYER_IID, EMPTY_JUKEBOX, Optional.empty());
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .delete(LogicalDatastoreType.CONFIGURATION, CREATE_AND_DELETE_TARGET);
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
     RestconfStrategy testPatchMergePutContainerStrategy() {
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -230,7 +232,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
             .when(netconfService).commit();
         doReturn(Futures.immediateFuture(new DefaultDOMRpcResult())).when(netconfService)
             .delete(LogicalDatastoreType.CONFIGURATION, CREATE_AND_DELETE_TARGET);
-        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService);
+        return new NetconfRestconfStrategy(JUKEBOX_SCHEMA, netconfService, null);
     }
 
     @Override
@@ -245,62 +247,62 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
     @Override
     RestconfStrategy readDataConfigTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(DATA_3))).when(netconfService).getConfig(PATH);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readAllHavingOnlyConfigTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(DATA_3))).when(netconfService).getConfig(PATH);
         doReturn(immediateFluentFuture(Optional.empty())).when(netconfService).get(PATH);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readAllHavingOnlyNonConfigTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(DATA_2))).when(netconfService).get(PATH_2);
         doReturn(immediateFluentFuture(Optional.empty())).when(netconfService).getConfig(PATH_2);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readDataNonConfigTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(DATA_2))).when(netconfService).get(PATH_2);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readContainerDataAllTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(DATA_3))).when(netconfService).getConfig(PATH);
         doReturn(immediateFluentFuture(Optional.of(DATA_4))).when(netconfService).get(PATH);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readContainerDataConfigNoValueOfContentTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(DATA_3))).when(netconfService).getConfig(PATH);
         doReturn(immediateFluentFuture(Optional.of(DATA_4))).when(netconfService).get(PATH);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readListDataAllTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(LIST_DATA))).when(netconfService).get(PATH_3);
         doReturn(immediateFluentFuture(Optional.of(LIST_DATA_2))).when(netconfService).getConfig(PATH_3);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readOrderedListDataAllTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(ORDERED_MAP_NODE_1))).when(netconfService).get(PATH_3);
         doReturn(immediateFluentFuture(Optional.of(ORDERED_MAP_NODE_2))).when(netconfService).getConfig(PATH_3);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readUnkeyedListDataAllTestStrategy() {
         doReturn(immediateFluentFuture(Optional.of(UNKEYED_LIST_NODE_1))).when(netconfService).get(PATH_3);
         doReturn(immediateFluentFuture(Optional.of(UNKEYED_LIST_NODE_2))).when(netconfService).getConfig(PATH_3);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
@@ -309,7 +311,7 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
             .get(LEAF_SET_NODE_PATH);
         doReturn(immediateFluentFuture(Optional.of(LEAF_SET_NODE_2))).when(netconfService)
             .getConfig(LEAF_SET_NODE_PATH);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
@@ -318,12 +320,12 @@ public final class NetconfRestconfStrategyTest extends AbstractRestconfStrategyT
             .get(LEAF_SET_NODE_PATH);
         doReturn(immediateFluentFuture(Optional.of(ORDERED_LEAF_SET_NODE_2))).when(netconfService)
             .getConfig(LEAF_SET_NODE_PATH);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 
     @Override
     RestconfStrategy readDataWrongPathOrNoContentTestStrategy() {
         doReturn(immediateFluentFuture(Optional.empty())).when(netconfService).getConfig(PATH_2);
-        return new NetconfRestconfStrategy(mockSchemaContext, netconfService);
+        return new NetconfRestconfStrategy(mockSchemaContext, netconfService, null);
     }
 }