X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Frestconf-nb-rfc8040%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Frestconf%2Fnb%2Frfc8040%2Frests%2Fservices%2Fimpl%2FRestconfDataServiceImpl.java;h=8257b6394e0cc986552805cadb9b8211ff8e2582;hb=365a4ee24f4760a4e09b72d4959f618479dccf2a;hp=74e118885855b285160656216058304147ade1a0;hpb=cd6157a02dc35ccdc37a3929cfc891e99d1bf29b;p=netconf.git diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java index 74e1188858..8257b6394e 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java @@ -8,8 +8,6 @@ package org.opendaylight.restconf.nb.rfc8040.rests.services.impl; import static java.util.Objects.requireNonNull; -import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.INSERT; -import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.POINT; import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.NOTIFICATION_STREAM; import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAMS_PATH; import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_ACCESS_PATH_PART; @@ -18,7 +16,8 @@ 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.collect.ImmutableList; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; import java.net.URI; import java.time.Clock; import java.time.LocalDateTime; @@ -26,31 +25,35 @@ import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Optional; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import javax.ws.rs.Path; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.dom.api.DOMActionException; import org.opendaylight.mdsal.dom.api.DOMActionResult; import org.opendaylight.mdsal.dom.api.DOMActionService; import org.opendaylight.mdsal.dom.api.DOMDataBroker; +import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteOperations; import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction; import org.opendaylight.mdsal.dom.api.DOMMountPoint; import org.opendaylight.mdsal.dom.api.DOMMountPointService; -import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; -import org.opendaylight.restconf.common.context.NormalizedNodeContext; -import org.opendaylight.restconf.common.context.WriterParameters; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.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.jaxrs.QueryParams; import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler; +import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload; +import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters; import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataService; import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService; import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy; @@ -61,19 +64,16 @@ import org.opendaylight.restconf.nb.rfc8040.rests.utils.PlainPatchDataTransactio 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; -import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant; -import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.Insert; -import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfInvokeOperationsUtil; import org.opendaylight.restconf.nb.rfc8040.streams.Configuration; import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter; import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil; import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; -import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.yang.common.ErrorTag; import org.opendaylight.yangtools.yang.common.ErrorType; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; @@ -84,6 +84,7 @@ import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; import org.slf4j.Logger; @@ -94,19 +95,9 @@ import org.slf4j.LoggerFactory; */ @Path("/") public class RestconfDataServiceImpl implements RestconfDataService { - // FIXME: we should be able to interpret 'point' and refactor this class into a behavior - private static final class QueryParams implements Immutable { - final @Nullable String point; - final @Nullable Insert insert; - - QueryParams(final @Nullable Insert insert, final @Nullable String point) { - this.insert = insert; - this.point = point; - } - } - 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 SchemaContextHandler schemaContextHandler; @@ -122,7 +113,7 @@ public class RestconfDataServiceImpl implements RestconfDataService { final DOMActionService actionService, final Configuration configuration) { this.schemaContextHandler = requireNonNull(schemaContextHandler); this.dataBroker = requireNonNull(dataBroker); - this.restconfStrategy = new MdsalRestconfStrategy(dataBroker); + restconfStrategy = new MdsalRestconfStrategy(dataBroker); this.mountPointService = requireNonNull(mountPointService); this.delegRestconfSubscrService = requireNonNull(delegRestconfSubscrService); this.actionService = requireNonNull(actionService); @@ -137,11 +128,11 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public Response readData(final String identifier, final UriInfo uriInfo) { - final EffectiveModelContext schemaContextRef = this.schemaContextHandler.get(); - final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( - identifier, schemaContextRef, Optional.of(mountPointService)); - final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(instanceIdentifier, uriInfo); + final ReadDataParams readParams = QueryParams.newReadDataParams(uriInfo); + final EffectiveModelContext schemaContextRef = schemaContextHandler.get(); + final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( + identifier, schemaContextRef, Optional.of(mountPointService)); final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint(); // FIXME: this looks quite crazy, why do we even have it? @@ -150,14 +141,16 @@ public class RestconfDataServiceImpl implements RestconfDataService { createAllYangNotificationStreams(schemaContextRef, uriInfo); } + final QueryParameters queryParams = QueryParams.newQueryParameters(readParams, instanceIdentifier); + final List fieldPaths = queryParams.fieldPaths(); final RestconfStrategy strategy = getRestconfStrategy(mountPoint); final NormalizedNode node; - if (parameters.getFieldPaths() != null && !parameters.getFieldPaths().isEmpty()) { - node = ReadDataTransactionUtil.readData(parameters.getContent(), instanceIdentifier.getInstanceIdentifier(), - strategy, parameters.getWithDefault(), schemaContextRef, parameters.getFieldPaths()); + if (fieldPaths != null && !fieldPaths.isEmpty()) { + node = ReadDataTransactionUtil.readData(readParams.content(), instanceIdentifier.getInstanceIdentifier(), + strategy, readParams.withDefaults(), schemaContextRef, fieldPaths); } else { - node = ReadDataTransactionUtil.readData(parameters.getContent(), instanceIdentifier.getInstanceIdentifier(), - strategy, parameters.getWithDefault(), schemaContextRef); + node = ReadDataTransactionUtil.readData(readParams.content(), instanceIdentifier.getInstanceIdentifier(), + strategy, readParams.withDefaults(), schemaContextRef); } // FIXME: this is utter craziness, refactor it properly! @@ -165,7 +158,7 @@ public class RestconfDataServiceImpl implements RestconfDataService { && identifier.contains(STREAM_LOCATION_PATH_PART)) { final String value = (String) node.body(); final String streamName = value.substring(value.indexOf(NOTIFICATION_STREAM + '/')); - this.delegRestconfSubscrService.subscribeToStream(streamName, uriInfo); + delegRestconfSubscrService.subscribeToStream(streamName, uriInfo); } if (node == null) { throw new RestconfDocumentedException( @@ -173,20 +166,21 @@ public class RestconfDataServiceImpl implements RestconfDataService { ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); } - if (parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL) - || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) { - final QName type = node.getIdentifier().getNodeType(); - return Response.status(Status.OK) - .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)) + switch (readParams.content()) { + case ALL: + case CONFIG: + final QName type = node.getIdentifier().getNodeType(); + return Response.status(Status.OK) + .entity(NormalizedNodePayload.ofReadData(instanceIdentifier, node, queryParams)) .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null) + "-" + type.getLocalName() + '"') .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC()))) .build(); + default: + return Response.status(Status.OK) + .entity(NormalizedNodePayload.ofReadData(instanceIdentifier, node, queryParams)) + .build(); } - - return Response.status(Status.OK) - .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)) - .build(); } private void createAllYangNotificationStreams(final EffectiveModelContext schemaContext, final UriInfo uriInfo) { @@ -218,104 +212,43 @@ public class RestconfDataServiceImpl implements RestconfDataService { } @Override - public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { + public Response putData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo) { requireNonNull(payload); - final QueryParams checkedParms = checkQueryParameters(uriInfo); + final WriteDataParams params = QueryParams.newWriteDataParams(uriInfo); - final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); - validInputData(iid.getSchemaNode(), payload); + validInputData(iid.getSchemaNode() != null, payload); validTopLevelNodeName(iid.getInstanceIdentifier(), payload); validateListKeysEqualityInPayloadAndUri(payload); - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final EffectiveModelContext ref = mountPoint == null - ? this.schemaContextHandler.get() : modelContext(mountPoint); - - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - return PutDataTransactionUtil.putData(payload, ref, strategy, checkedParms.insert, checkedParms.point); - } - - private static QueryParams checkQueryParameters(final UriInfo uriInfo) { - boolean insertUsed = false; - boolean pointUsed = false; - Insert insert = null; - String point = null; - - for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { - switch (entry.getKey()) { - case INSERT: - if (insertUsed) { - throw new RestconfDocumentedException("Insert parameter can be used only once.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - - insertUsed = true; - final String str = entry.getValue().get(0); - insert = Insert.forValue(str); - if (insert == null) { - throw new RestconfDocumentedException("Unrecognized insert parameter value '" + str + "'", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - break; - case POINT: - if (pointUsed) { - throw new RestconfDocumentedException("Point parameter can be used only once.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - - pointUsed = true; - point = entry.getValue().get(0); - break; - default: - throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey(), - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - } - - checkQueryParams(insertUsed, pointUsed, insert); - return new QueryParams(insert, point); - } - - private static void checkQueryParams(final boolean insertUsed, final boolean pointUsed, final Insert insert) { - if (pointUsed) { - if (!insertUsed) { - throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - - if (insert != Insert.BEFORE && insert != Insert.AFTER) { - throw new RestconfDocumentedException( - "Point parameter can be used only with 'after' or 'before' values of Insert parameter.", - ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT); - } - } + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PutDataTransactionUtil.putData(payload, iid.getSchemaContext(), strategy, params); } @Override - public Response postData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { + public Response postData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo) { return postData(payload, uriInfo); } @Override - public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) { + public Response postData(final NormalizedNodePayload payload, final UriInfo uriInfo) { requireNonNull(payload); - if (payload.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition) { + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); + if (iid.getSchemaNode() instanceof ActionDefinition) { return invokeAction(payload); } - final QueryParams checkedParms = checkQueryParameters(uriInfo); - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - return PostDataTransactionUtil.postData(uriInfo, payload, strategy, - getSchemaContext(mountPoint), checkedParms.insert, checkedParms.point); + final WriteDataParams params = QueryParams.newWriteDataParams(uriInfo); + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PostDataTransactionUtil.postData(uriInfo, payload, strategy, iid.getSchemaContext(), params); } @Override public Response deleteData(final String identifier) { - final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( - identifier, this.schemaContextHandler.get(), Optional.of(mountPointService)); + final InstanceIdentifierContext instanceIdentifier = ParserIdentifier.toInstanceIdentifier( + identifier, schemaContextHandler.get(), Optional.of(mountPointService)); final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint(); final RestconfStrategy strategy = getRestconfStrategy(mountPoint); @@ -329,32 +262,24 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public PatchStatusContext patchData(final PatchContext context, final UriInfo uriInfo) { - final DOMMountPoint mountPoint = RestconfDocumentedException.throwIfNull(context, + final InstanceIdentifierContext iid = RestconfDocumentedException.throwIfNull(context, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "No patch documented provided") - .getInstanceIdentifierContext().getMountPoint(); - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - return PatchDataTransactionUtil.patchData(context, strategy, getSchemaContext(mountPoint)); + .getInstanceIdentifierContext(); + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PatchDataTransactionUtil.patchData(context, strategy, iid.getSchemaContext()); } @Override - public Response patchData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { + public Response patchData(final String identifier, final NormalizedNodePayload payload, final UriInfo uriInfo) { requireNonNull(payload); - final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); - validInputData(iid.getSchemaNode(), payload); + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); + validInputData(iid.getSchemaNode() != null, payload); validTopLevelNodeName(iid.getInstanceIdentifier(), payload); validateListKeysEqualityInPayloadAndUri(payload); - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final EffectiveModelContext ref = mountPoint == null - ? this.schemaContextHandler.get() : modelContext(mountPoint); - final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - - return PlainPatchDataTransactionUtil.patchData(payload, strategy, ref); - } - - private EffectiveModelContext getSchemaContext(final DOMMountPoint mountPoint) { - return mountPoint == null ? schemaContextHandler.get() : modelContext(mountPoint); + final RestconfStrategy strategy = getRestconfStrategy(iid.getMountPoint()); + return PlainPatchDataTransactionUtil.patchData(payload, strategy, iid.getSchemaContext()); } // FIXME: why is this synchronized? @@ -373,44 +298,32 @@ public class RestconfDataServiceImpl implements RestconfDataService { /** * Invoke Action operation. * - * @param payload {@link NormalizedNodeContext} - the body of the operation - * @return {@link NormalizedNodeContext} wrapped in {@link Response} + * @param payload {@link NormalizedNodePayload} - the body of the operation + * @return {@link NormalizedNodePayload} wrapped in {@link Response} */ - public Response invokeAction(final NormalizedNodeContext payload) { - final InstanceIdentifierContext context = payload.getInstanceIdentifierContext(); - final DOMMountPoint mountPoint = context.getMountPoint(); - final Absolute schemaPath = Absolute.of(ImmutableList.copyOf(context.getSchemaNode().getPath() - .getPathFromRoot())); + public Response invokeAction(final NormalizedNodePayload payload) { + final InstanceIdentifierContext context = payload.getInstanceIdentifierContext(); final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier(); final NormalizedNode data = payload.getData(); - if (yangIIdContext.isEmpty() - && !RestconfDataServiceConstant.NETCONF_BASE_QNAME.equals(data.getIdentifier().getNodeType())) { + if (yangIIdContext.isEmpty() && !NETCONF_BASE_QNAME.equals(data.getIdentifier().getNodeType())) { throw new RestconfDocumentedException("Instance identifier need to contain at least one path argument", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } + final DOMMountPoint mountPoint = context.getMountPoint(); + final Absolute schemaPath = context.inference().toSchemaInferenceStack().toSchemaNodeIdentifier(); final DOMActionResult response; - final EffectiveModelContext schemaContextRef; if (mountPoint != null) { - response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath, yangIIdContext, - mountPoint); - schemaContextRef = modelContext(mountPoint); + response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, mountPoint); } else { - response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath, yangIIdContext, - actionService); - schemaContextRef = this.schemaContextHandler.get(); + response = invokeAction((ContainerNode) data, schemaPath, yangIIdContext, actionService); } - final DOMActionResult result = RestconfInvokeOperationsUtil.checkActionResponse(response); + final DOMActionResult result = checkActionResponse(response); - ActionDefinition resultNodeSchema = null; ContainerNode resultData = null; if (result != null) { - final Optional optOutput = result.getOutput(); - if (optOutput.isPresent()) { - resultData = optOutput.get(); - resultNodeSchema = (ActionDefinition) context.getSchemaNode(); - } + resultData = result.getOutput().orElse(null); } if (resultData != null && resultData.isEmpty()) { @@ -418,23 +331,83 @@ public class RestconfDataServiceImpl implements RestconfDataService { } return Response.status(Status.OK) - .entity(new NormalizedNodeContext( - new InstanceIdentifierContext<>(yangIIdContext, resultNodeSchema, mountPoint, schemaContextRef), - resultData)) + .entity(NormalizedNodePayload.ofNullable(context, resultData)) .build(); } /** - * Valid input data with {@link SchemaNode}. + * Invoking Action via mount point. + * + * @param mountPoint mount point + * @param data input data + * @param schemaPath schema path of data + * @return {@link DOMActionResult} + */ + private static DOMActionResult invokeAction(final ContainerNode data, + final Absolute schemaPath, final YangInstanceIdentifier yangIId, final DOMMountPoint mountPoint) { + return invokeAction(data, schemaPath, yangIId, mountPoint.getService(DOMActionService.class) + .orElseThrow(() -> new RestconfDocumentedException("DomAction service is missing."))); + } + + /** + * Invoke Action via ActionServiceHandler. + * + * @param data input data + * @param yangIId invocation context + * @param schemaPath schema path of data + * @param actionService action service to invoke action + * @return {@link DOMActionResult} + */ + // FIXME: NETCONF-718: we should be returning a future here + private static DOMActionResult invokeAction(final ContainerNode data, final Absolute schemaPath, + final YangInstanceIdentifier yangIId, final DOMActionService actionService) { + return RestconfInvokeOperationsServiceImpl.checkedGet(Futures.catching(actionService.invokeAction( + schemaPath, new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, yangIId.getParent()), data), + DOMActionException.class, + cause -> new SimpleDOMActionResult(List.of(RpcResultBuilder.newError( + ErrorType.RPC, ErrorTag.OPERATION_FAILED, cause.getMessage()))), + MoreExecutors.directExecutor())); + } + + /** + * Check the validity of the result. * - * @param schemaNode {@link SchemaNode} + * @param response response of Action + * @return {@link DOMActionResult} result + */ + private static DOMActionResult checkActionResponse(final DOMActionResult response) { + if (response == null) { + return null; + } + + try { + if (response.getErrors().isEmpty()) { + return response; + } + LOG.debug("InvokeAction Error Message {}", response.getErrors()); + throw new RestconfDocumentedException("InvokeAction Error Message ", null, response.getErrors()); + } catch (final CancellationException e) { + final String errMsg = "The Action Operation was cancelled while executing."; + LOG.debug("Cancel Execution: {}", errMsg, e); + throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION, e); + } + } + + /** + * Valid input data based on presence of a schema node. + * + * @param haveSchemaNode true if there is an underlying schema node * @param payload input data */ @VisibleForTesting - public static void validInputData(final SchemaNode schemaNode, final NormalizedNodeContext payload) { - if (schemaNode != null && payload.getData() == null) { - throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); - } else if (schemaNode == null && payload.getData() != null) { + public static void validInputData(final boolean haveSchemaNode, final NormalizedNodePayload payload) { + final boolean haveData = payload.getData() != null; + if (haveSchemaNode) { + if (!haveData) { + throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE); + } + } else if (haveData) { throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } } @@ -446,17 +419,16 @@ public class RestconfDataServiceImpl implements RestconfDataService { * @param payload data */ @VisibleForTesting - public static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodeContext payload) { - final String payloadName = payload.getData().getIdentifier().getNodeType().getLocalName(); - + public static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodePayload payload) { + final QName dataNodeType = payload.getData().getIdentifier().getNodeType(); if (path.isEmpty()) { - if (!payload.getData().getIdentifier().getNodeType().equals( - RestconfDataServiceConstant.NETCONF_BASE_QNAME)) { + if (!NETCONF_BASE_QNAME.equals(dataNodeType)) { throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } } else { final String identifierName = path.getLastPathArgument().getNodeType().getLocalName(); + final String payloadName = dataNodeType.getLocalName(); if (!payloadName.equals(identifierName)) { throw new RestconfDocumentedException( "Payload name (" + payloadName + ") is different from identifier name (" + identifierName + ")", @@ -473,8 +445,8 @@ 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 NormalizedNodeContext payload) { - final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); + public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodePayload payload) { + final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument(); final SchemaNode schemaNode = iiWithData.getSchemaNode(); final NormalizedNode data = payload.getData(); @@ -505,10 +477,4 @@ public class RestconfDataServiceImpl implements RestconfDataService { } } } - - private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) { - return mountPoint.getService(DOMSchemaService.class) - .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) - .orElse(null); - } }