Use AsyncResponse in deleteData/patchData
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / services / impl / RestconfDataServiceImpl.java
index 96c31f2ae7466b94939c35b8bdadc405b2f80277..3986659fbafcf8fd502c2a75cf9232579233da12 100644 (file)
@@ -16,6 +16,7 @@ import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsCo
 import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART;
 
 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.MoreExecutors;
 import java.net.URI;
@@ -25,10 +26,10 @@ import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
 import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
@@ -47,7 +48,6 @@ 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.ReadDataParams;
-import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
 import org.opendaylight.restconf.nb.rfc8040.WriteDataParams;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
@@ -58,9 +58,7 @@ import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataServi
 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.DeleteDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.PatchDataTransactionUtil;
-import org.opendaylight.restconf.nb.rfc8040.rests.utils.PlainPatchDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.PostDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.PutDataTransactionUtil;
 import org.opendaylight.restconf.nb.rfc8040.rests.utils.ReadDataTransactionUtil;
@@ -69,7 +67,9 @@ 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.ParserIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev170126.restconf.restconf.Data;
 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;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -84,7 +84,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
@@ -98,7 +97,6 @@ import org.slf4j.LoggerFactory;
 public class RestconfDataServiceImpl implements RestconfDataService {
     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 QName NETCONF_BASE_QNAME = SchemaContext.NAME;
 
     private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
     private final DatabindProvider databindProvider;
@@ -134,7 +132,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final EffectiveModelContext schemaContextRef = databindProvider.currentContext().modelContext();
         // FIXME: go through
         final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
-                identifier, schemaContextRef, Optional.of(mountPointService));
+                identifier, schemaContextRef, mountPointService);
         final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
 
         // FIXME: this looks quite crazy, why do we even have it?
@@ -231,12 +229,11 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     private void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext,
             final UriInfo uriInfo, final DOMDataTreeWriteOperations tx, final NotificationListenerAdapter listener) {
         final URI uri = streamUtils.prepareUriByStreamName(uriInfo, listener.getStreamName());
-        final MapEntryNode mapToStreams = RestconfStateStreams.notificationStreamEntry(
-                listener.getSchemaPath().lastNodeIdentifier(), schemaContext.getNotifications(), null,
-                listener.getOutputType(), uri);
+        final MapEntryNode mapToStreams = RestconfStateStreams.notificationStreamEntry(schemaContext,
+                listener.getSchemaPath().lastNodeIdentifier(), null, listener.getOutputType(), uri);
 
         tx.merge(LogicalDatastoreType.OPERATIONAL,
-            Rfc8040.restconfStateStreamPath(mapToStreams.name()), mapToStreams);
+            RestconfStateStreams.restconfStateStreamPath(mapToStreams.name()), mapToStreams);
     }
 
     @Override
@@ -246,13 +243,14 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final WriteDataParams params = QueryParams.newWriteDataParams(uriInfo);
 
         final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext();
+        final YangInstanceIdentifier path = iid.getInstanceIdentifier();
 
         validInputData(iid.getSchemaNode() != null, payload);
-        validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
+        validTopLevelNodeName(path, payload);
         validateListKeysEqualityInPayloadAndUri(payload);
 
         final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint());
-        return PutDataTransactionUtil.putData(payload, iid.getSchemaContext(), strategy, params);
+        return PutDataTransactionUtil.putData(path, payload.getData(), iid.getSchemaContext(), strategy, params);
     }
 
     @Override
@@ -270,17 +268,27 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
         final WriteDataParams params = QueryParams.newWriteDataParams(uriInfo);
         final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint());
-        return PostDataTransactionUtil.postData(uriInfo, payload, strategy, iid.getSchemaContext(), params);
+        return PostDataTransactionUtil.postData(uriInfo, iid.getInstanceIdentifier(), payload.getData(), strategy,
+            iid.getSchemaContext(), params);
     }
 
     @Override
-    public Response deleteData(final String identifier) {
-        final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
-            databindProvider.currentContext().modelContext(), Optional.of(mountPointService));
+    public void deleteData(final String identifier, final AsyncResponse ar) {
+        final var instanceIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
+            databindProvider.currentContext().modelContext(), mountPointService);
+        final var strategy = getRestconfStrategy(instanceIdentifier.getMountPoint());
+
+        Futures.addCallback(strategy.delete(instanceIdentifier.getInstanceIdentifier()), new FutureCallback<>() {
+            @Override
+            public void onSuccess(final Empty result) {
+                ar.resume(Response.noContent().build());
+            }
 
-        final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
-        final RestconfStrategy strategy = getRestconfStrategy(mountPoint);
-        return DeleteDataTransactionUtil.deleteData(strategy, instanceIdentifier.getInstanceIdentifier());
+            @Override
+            public void onFailure(final Throwable failure) {
+                ar.resume(failure);
+            }
+        }, MoreExecutors.directExecutor());
     }
 
     @Override
@@ -298,16 +306,26 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     }
 
     @Override
-    public Response patchData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo) {
-        requireNonNull(payload);
-
+    public void patchData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo,
+            final AsyncResponse ar) {
         final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext();
+        final YangInstanceIdentifier path = iid.getInstanceIdentifier();
         validInputData(iid.getSchemaNode() != null, payload);
-        validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
+        validTopLevelNodeName(path, payload);
         validateListKeysEqualityInPayloadAndUri(payload);
+        final var strategy = getRestconfStrategy(iid.getMountPoint());
 
-        final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint());
-        return PlainPatchDataTransactionUtil.patchData(payload, strategy, iid.getSchemaContext());
+        Futures.addCallback(strategy.merge(path, payload.getData(), iid.getSchemaContext()), new FutureCallback<>() {
+            @Override
+            public void onSuccess(final Empty result) {
+                ar.resume(Response.ok().build());
+            }
+
+            @Override
+            public void onFailure(final Throwable failure) {
+                ar.resume(failure);
+            }
+        }, MoreExecutors.directExecutor());
     }
 
     @VisibleForTesting
@@ -334,7 +352,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier();
         final NormalizedNode data = payload.getData();
 
-        if (yangIIdContext.isEmpty() && !NETCONF_BASE_QNAME.equals(data.name().getNodeType())) {
+        if (yangIIdContext.isEmpty() && !Data.QNAME.equals(data.name().getNodeType())) {
             throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument",
                 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
@@ -428,7 +446,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
      * @param payload    input data
      */
     @VisibleForTesting
-    public static void validInputData(final boolean haveSchemaNode, final NormalizedNodePayload payload) {
+    static void validInputData(final boolean haveSchemaNode, final NormalizedNodePayload payload) {
         final boolean haveData = payload.getData() != null;
         if (haveSchemaNode) {
             if (!haveData) {
@@ -447,10 +465,10 @@ public class RestconfDataServiceImpl implements RestconfDataService {
      * @param payload data
      */
     @VisibleForTesting
-    public static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodePayload payload) {
+    static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodePayload payload) {
         final QName dataNodeType = payload.getData().name().getNodeType();
         if (path.isEmpty()) {
-            if (!NETCONF_BASE_QNAME.equals(dataNodeType)) {
+            if (!Data.QNAME.equals(dataNodeType)) {
                 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
                         ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
             }
@@ -465,7 +483,6 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         }
     }
 
-
     /**
      * Validates whether keys in {@code payload} are equal to values of keys in
      * {@code iiWithData} for list schema node.
@@ -473,7 +490,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
      * @throws RestconfDocumentedException if key values or key count in payload and URI isn't equal
      */
     @VisibleForTesting
-    public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodePayload payload) {
+    static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodePayload payload) {
         final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext();
         final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
         final SchemaNode schemaNode = iiWithData.getSchemaNode();