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=63b30ed30388d91c8b8040a6fc6f95b5701561ee;hb=926d2bb83d9d281a40570b75bc136af623f0585d;hp=6dfa63ad38f9ed0c9d886bfd6f68e6fd3a18f669;hpb=bb9a6c650d4f193fff17f113bea87239d8ab203f;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 6dfa63ad38..63b30ed303 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 @@ -17,6 +17,7 @@ import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsCo import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH; import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART; +import com.google.common.collect.ImmutableList; import java.net.URI; import java.time.Clock; import java.time.LocalDateTime; @@ -24,16 +25,16 @@ import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map.Entry; import java.util.Optional; +import java.util.concurrent.ExecutionException; import javax.ws.rs.Path; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.dom.api.DOMActionResult; -import org.opendaylight.mdsal.dom.api.DOMDataBroker; -import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction; import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.common.context.NormalizedNodeContext; import org.opendaylight.restconf.common.context.WriterParameters; @@ -43,13 +44,16 @@ import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag; import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.nb.rfc8040.Rfc8040; import org.opendaylight.restconf.nb.rfc8040.handlers.ActionServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler; import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler; import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataService; import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService; -import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper; +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.transactions.RestconfTransaction; 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; @@ -57,7 +61,9 @@ 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; @@ -67,12 +73,13 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,11 +88,12 @@ 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 String insert; + final @Nullable Insert insert; - QueryParams(final @Nullable String insert, final @Nullable String point) { + QueryParams(final @Nullable Insert insert, final @Nullable String point) { this.insert = insert; this.point = point; } @@ -95,6 +103,7 @@ public class RestconfDataServiceImpl implements RestconfDataService { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss"); private final RestconfStreamsSubscriptionService delegRestconfSubscrService; + private final SubscribeToStreamUtil streamUtils; // FIXME: evaluate thread-safety of updates (synchronized) vs. access (mostly unsynchronized) here private SchemaContextHandler schemaContextHandler; @@ -106,12 +115,15 @@ public class RestconfDataServiceImpl implements RestconfDataService { final TransactionChainHandler transactionChainHandler, final DOMMountPointServiceHandler mountPointServiceHandler, final RestconfStreamsSubscriptionService delegRestconfSubscrService, - final ActionServiceHandler actionServiceHandler) { + final ActionServiceHandler actionServiceHandler, + final Configuration configuration) { this.actionServiceHandler = requireNonNull(actionServiceHandler); this.schemaContextHandler = requireNonNull(schemaContextHandler); this.transactionChainHandler = requireNonNull(transactionChainHandler); this.mountPointServiceHandler = requireNonNull(mountPointServiceHandler); this.delegRestconfSubscrService = requireNonNull(delegRestconfSubscrService); + streamUtils = configuration.isUseSSE() ? SubscribeToStreamUtil.serverSentEvents() + : SubscribeToStreamUtil.webSockets(); } @Override @@ -142,10 +154,15 @@ public class RestconfDataServiceImpl implements RestconfDataService { final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(instanceIdentifier, uriInfo); final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint(); - final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper( - instanceIdentifier, mountPoint, getTransactionChainHandler(mountPoint)); - final NormalizedNode node = readData(identifier, parameters.getContent(), - transactionNode, parameters.getWithDefault(), schemaContextRef, uriInfo); + 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()); + } else { + node = readData(identifier, parameters.getContent(), instanceIdentifier.getInstanceIdentifier(), strategy, + parameters.getWithDefault(), schemaContextRef, uriInfo); + } if (identifier != null && identifier.contains(STREAM_PATH) && identifier.contains(STREAM_ACCESS_PATH_PART) && identifier.contains(STREAM_LOCATION_PATH_PART)) { final String value = (String) node.getValue(); @@ -162,73 +179,70 @@ public class RestconfDataServiceImpl implements RestconfDataService { if (parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL) || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) { final QName type = node.getNodeType(); - return Response.status(200) + return Response.status(Status.OK) .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)) .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null) - + type.getLocalName() + '"') + + "-" + type.getLocalName() + '"') .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC()))) .build(); } - return Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)).build(); + return Response.status(Status.OK) + .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)) + .build(); } - /** * Read specific type of data from data store via transaction and if identifier read data from * streams then put streams from actual schema context to datastore. * - * @param identifier - * identifier of data to read - * @param content - * type of data to read (config, state, all) - * @param transactionNode - * {@link TransactionVarsWrapper} - wrapper for variables - * @param withDefa - * vaule of with-defaults parameter - * @param schemaContext - * schema context - * @param uriInfo - * uri info + * @param identifier identifier of data to read + * @param content type of data to read (config, state, all) + * @param strategy {@link RestconfStrategy} - object that perform the actual DS operations + * @param withDefa value of with-defaults parameter + * @param schemaContext schema context + * @param uriInfo uri info * @return {@link NormalizedNode} */ - private static NormalizedNode readData(final String identifier, final String content, - final TransactionVarsWrapper transactionNode, final String withDefa, - final EffectiveModelContext schemaContext, final UriInfo uriInfo) { + private NormalizedNode readData(final String identifier, final String content, + final YangInstanceIdentifier path, final RestconfStrategy strategy, final String withDefa, + final EffectiveModelContext schemaContext, final UriInfo uriInfo) { if (identifier != null && identifier.contains(STREAMS_PATH) && !identifier.contains(STREAM_PATH_PART)) { - createAllYangNotificationStreams(transactionNode, schemaContext, uriInfo); + createAllYangNotificationStreams(strategy, schemaContext, uriInfo); } - return ReadDataTransactionUtil.readData(content, transactionNode, withDefa, schemaContext); + return ReadDataTransactionUtil.readData(content, path, strategy, withDefa, schemaContext); } - private static void createAllYangNotificationStreams(final TransactionVarsWrapper transactionNode, + private void createAllYangNotificationStreams(final RestconfStrategy strategy, final EffectiveModelContext schemaContext, final UriInfo uriInfo) { - final DOMDataTreeReadWriteTransaction wTx = transactionNode.getTransactionChain().newReadWriteTransaction(); - final boolean exist = SubscribeToStreamUtil.checkExist(schemaContext, wTx); + final RestconfTransaction transaction = strategy.prepareWriteExecution(); for (final NotificationDefinition notificationDefinition : schemaContext.getNotifications()) { final NotificationListenerAdapter notifiStreamXML = - CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext, - NotificationOutputType.XML); + CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext, + NotificationOutputType.XML); final NotificationListenerAdapter notifiStreamJSON = - CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext, - NotificationOutputType.JSON); - writeNotificationStreamToDatastore(schemaContext, uriInfo, wTx, exist, notifiStreamXML); - writeNotificationStreamToDatastore(schemaContext, uriInfo, wTx, exist, notifiStreamJSON); + CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContext, + NotificationOutputType.JSON); + writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction, notifiStreamXML); + writeNotificationStreamToDatastore(schemaContext, uriInfo, transaction, notifiStreamJSON); + } + try { + transaction.commit().get(); + } catch (final InterruptedException | ExecutionException e) { + throw new RestconfDocumentedException("Problem while putting data to DS.", e); } - SubscribeToStreamUtil.submitData(wTx); } - private static void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext, - final UriInfo uriInfo, final DOMDataTreeReadWriteTransaction readWriteTransaction, final boolean exist, - final NotificationListenerAdapter listener) { - final URI uri = SubscribeToStreamUtil.prepareUriByStreamName(uriInfo, listener.getStreamName()); - final NormalizedNode mapToStreams = - RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring( - listener.getSchemaPath().getLastComponent(), schemaContext.getNotifications(), null, - listener.getOutputType(), uri, SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist); - SubscribeToStreamUtil.writeDataToDS(schemaContext, - listener.getSchemaPath().getLastComponent().getLocalName(), readWriteTransaction, exist, mapToStreams); + private void writeNotificationStreamToDatastore(final EffectiveModelContext schemaContext, + final UriInfo uriInfo, final RestconfTransaction transaction, final NotificationListenerAdapter listener) { + final URI uri = streamUtils.prepareUriByStreamName(uriInfo, listener.getStreamName()); + final MapEntryNode mapToStreams = RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring( + listener.getSchemaPath().lastNodeIdentifier(), schemaContext.getNotifications(), null, + listener.getOutputType(), uri); + + transaction.merge(LogicalDatastoreType.OPERATIONAL, + Rfc8040.MonitoringModule.restconfStateStreamPath(mapToStreams.getIdentifier()), mapToStreams); } @Override @@ -237,54 +251,50 @@ public class RestconfDataServiceImpl implements RestconfDataService { final QueryParams checkedParms = checkQueryParameters(uriInfo); - final InstanceIdentifierContext iid = payload - .getInstanceIdentifierContext(); + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload); PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload); PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload); final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final TransactionChainHandler localTransactionChainHandler; - final EffectiveModelContext ref; - if (mountPoint == null) { - localTransactionChainHandler = this.transactionChainHandler; - ref = this.schemaContextHandler.get(); - } else { - localTransactionChainHandler = transactionChainOfMountPoint(mountPoint); - ref = mountPoint.getEffectiveModelContext(); - } + final EffectiveModelContext ref = mountPoint == null + ? this.schemaContextHandler.get() : modelContext(mountPoint); - final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper( - payload.getInstanceIdentifierContext(), mountPoint, localTransactionChainHandler); - return PutDataTransactionUtil.putData(payload, ref, transactionNode, checkedParms.insert, checkedParms.point); + 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; - String insert = null; + Insert insert = null; String point = null; for (final Entry> entry : uriInfo.getQueryParameters().entrySet()) { switch (entry.getKey()) { case INSERT: - if (!insertUsed) { - insertUsed = true; - insert = entry.getValue().get(0); - } else { + if (insertUsed) { throw new RestconfDocumentedException("Insert parameter can be used only once.", - RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT); + RestconfError.ErrorType.PROTOCOL, RestconfError.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 + "'", + RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT); } break; case POINT: - if (!pointUsed) { - pointUsed = true; - point = entry.getValue().get(0); - } else { + if (pointUsed) { throw new RestconfDocumentedException("Point parameter can be used only once.", - RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT); + RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT); } + + pointUsed = true; + point = entry.getValue().get(0); break; default: throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey(), @@ -296,15 +306,18 @@ public class RestconfDataServiceImpl implements RestconfDataService { return new QueryParams(insert, point); } - private static void checkQueryParams(final boolean insertUsed, final boolean pointUsed, final String insert) { - if (pointUsed && !insertUsed) { - throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.", + 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.", RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT); - } - if (pointUsed && (insert.equals("first") || insert.equals("last"))) { - throw new RestconfDocumentedException( + } + + if (insert != Insert.BEFORE && insert != Insert.AFTER) { + throw new RestconfDocumentedException( "Point parameter can be used only with 'after' or 'before' values of Insert parameter.", RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ELEMENT); + } } } @@ -317,15 +330,13 @@ public class RestconfDataServiceImpl implements RestconfDataService { public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) { requireNonNull(payload); if (payload.getInstanceIdentifierContext().getSchemaNode() instanceof ActionDefinition) { - return invokeAction(payload, uriInfo); + return invokeAction(payload); } final QueryParams checkedParms = checkQueryParameters(uriInfo); - final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper( - payload.getInstanceIdentifierContext(), mountPoint, getTransactionChainHandler(mountPoint)); - return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, + final RestconfStrategy strategy = getRestconfStrategy(mountPoint); + return PostDataTransactionUtil.postData(uriInfo, payload, strategy, getSchemaContext(mountPoint), checkedParms.insert, checkedParms.point); } @@ -335,16 +346,8 @@ public class RestconfDataServiceImpl implements RestconfDataService { identifier, this.schemaContextHandler.get(), Optional.of(this.mountPointServiceHandler.get())); final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint(); - final TransactionChainHandler localTransactionChainHandler; - if (mountPoint == null) { - localTransactionChainHandler = this.transactionChainHandler; - } else { - localTransactionChainHandler = transactionChainOfMountPoint(mountPoint); - } - - final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint, - localTransactionChainHandler); - return DeleteDataTransactionUtil.deleteData(transactionNode); + final RestconfStrategy strategy = getRestconfStrategy(mountPoint); + return DeleteDataTransactionUtil.deleteData(strategy, instanceIdentifier.getInstanceIdentifier()); } @Override @@ -354,78 +357,58 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public PatchStatusContext patchData(final PatchContext context, final UriInfo uriInfo) { - final DOMMountPoint mountPoint = requireNonNull(context).getInstanceIdentifierContext().getMountPoint(); - final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper( - context.getInstanceIdentifierContext(), mountPoint, getTransactionChainHandler(mountPoint)); - return PatchDataTransactionUtil.patchData(context, transactionNode, getSchemaContext(mountPoint)); + final DOMMountPoint mountPoint = 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)); } @Override public Response patchData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { requireNonNull(payload); - final InstanceIdentifierContext iid = payload - .getInstanceIdentifierContext(); - + final InstanceIdentifierContext iid = payload.getInstanceIdentifierContext(); PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload); PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload); PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload); final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final TransactionChainHandler localTransactionChainHandler; - final EffectiveModelContext ref; - if (mountPoint == null) { - localTransactionChainHandler = this.transactionChainHandler; - ref = this.schemaContextHandler.get(); - } else { - localTransactionChainHandler = transactionChainOfMountPoint(mountPoint); - ref = mountPoint.getEffectiveModelContext(); - } - - final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper( - payload.getInstanceIdentifierContext(), mountPoint, localTransactionChainHandler); + final EffectiveModelContext ref = mountPoint == null + ? this.schemaContextHandler.get() : modelContext(mountPoint); + final RestconfStrategy strategy = getRestconfStrategy(mountPoint); - return PlainPatchDataTransactionUtil.patchData(payload, transactionNode, ref); - } - - private TransactionChainHandler getTransactionChainHandler(final DOMMountPoint mountPoint) { - return mountPoint == null ? transactionChainHandler : transactionChainOfMountPoint(mountPoint); + return PlainPatchDataTransactionUtil.patchData(payload, strategy, ref); } private EffectiveModelContext getSchemaContext(final DOMMountPoint mountPoint) { - return mountPoint == null ? schemaContextHandler.get() : mountPoint.getEffectiveModelContext(); + return mountPoint == null ? schemaContextHandler.get() : modelContext(mountPoint); } - /** - * Prepare transaction chain to access data of mount point. - * @param mountPoint - * mount point reference - * @return {@link TransactionChainHandler} - */ - private static TransactionChainHandler transactionChainOfMountPoint(final @NonNull DOMMountPoint mountPoint) { - final Optional domDataBrokerService = mountPoint.getService(DOMDataBroker.class); - if (domDataBrokerService.isPresent()) { - return new TransactionChainHandler(domDataBrokerService.get()); + // FIXME: why is this synchronized? + public synchronized RestconfStrategy getRestconfStrategy(final DOMMountPoint mountPoint) { + if (mountPoint == null) { + return new MdsalRestconfStrategy(transactionChainHandler); } - final String errMsg = "DOM data broker service isn't available for mount point " + mountPoint.getIdentifier(); - LOG.warn(errMsg); - throw new RestconfDocumentedException(errMsg); + return RestconfStrategy.forMountPoint(mountPoint).orElseThrow(() -> { + LOG.warn("Mount point {} does not expose a suitable access interface", mountPoint.getIdentifier()); + return new RestconfDocumentedException("Could not find a supported access interface in mount point " + + mountPoint.getIdentifier()); + }); } /** * Invoke Action operation. * - * @param payload - * {@link NormalizedNodeContext} - the body of the operation - * @param uriInfo - * URI info + * @param payload {@link NormalizedNodeContext} - the body of the operation * @return {@link NormalizedNodeContext} wrapped in {@link Response} */ - public Response invokeAction(final NormalizedNodeContext payload, final UriInfo uriInfo) { + public Response invokeAction(final NormalizedNodeContext payload) { final InstanceIdentifierContext context = payload.getInstanceIdentifierContext(); final DOMMountPoint mountPoint = context.getMountPoint(); - final SchemaPath schemaPath = context.getSchemaNode().getPath(); + final Absolute schemaPath = Absolute.of(ImmutableList.copyOf(context.getSchemaNode().getPath() + .getPathFromRoot())); final YangInstanceIdentifier yangIIdContext = context.getInstanceIdentifier(); final NormalizedNode data = payload.getData(); @@ -439,7 +422,7 @@ public class RestconfDataServiceImpl implements RestconfDataService { if (mountPoint != null) { response = RestconfInvokeOperationsUtil.invokeActionViaMountPoint(mountPoint, (ContainerNode) data, schemaPath, yangIIdContext); - schemaContextRef = mountPoint.getEffectiveModelContext(); + schemaContextRef = modelContext(mountPoint); } else { response = RestconfInvokeOperationsUtil.invokeAction((ContainerNode) data, schemaPath, this.actionServiceHandler, yangIIdContext); @@ -458,10 +441,19 @@ public class RestconfDataServiceImpl implements RestconfDataService { } if (resultData != null && resultData.getValue().isEmpty()) { - throw new WebApplicationException(Response.Status.NO_CONTENT); + return Response.status(Status.NO_CONTENT).build(); } - return Response.status(200).entity(new NormalizedNodeContext(new InstanceIdentifierContext<>(yangIIdContext, - resultNodeSchema, mountPoint, schemaContextRef), resultData)).build(); + return Response.status(Status.OK) + .entity(new NormalizedNodeContext( + new InstanceIdentifierContext<>(yangIIdContext, resultNodeSchema, mountPoint, schemaContextRef), + resultData)) + .build(); + } + + private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) { + return mountPoint.getService(DOMSchemaService.class) + .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) + .orElse(null); } }