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%2Futils%2FPostDataTransactionUtil.java;h=70008b7e135068613bc51d872383fc39c53a9437;hb=420eacdfeb8c643e79bf17c2b70da3fd644eb93e;hp=295640d01a6f78a515d32feaadad1ed1a91f67fa;hpb=14bd5e7ef6421c109248f4e750be330cb0337287;p=netconf.git diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java index 295640d01a..70008b7e13 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java @@ -21,26 +21,32 @@ import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.common.context.NormalizedNodeContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag; +import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy; +import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction; +import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PostPutQueryParameters.Insert; import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode; -import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; 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.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Util class to post data to DS. * */ public final class PostDataTransactionUtil { + private static final Logger LOG = LoggerFactory.getLogger(PostDataTransactionUtil.class); + // FIXME: why is this being reused from other places? + static final String POST_TX_TYPE = "POST"; + private PostDataTransactionUtil() { // Hidden on purpose } @@ -59,17 +65,15 @@ public final class PostDataTransactionUtil { */ public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload, final RestconfStrategy strategy, - final EffectiveModelContext schemaContext, final String insert, + final EffectiveModelContext schemaContext, final Insert insert, final String point) { - final FluentFuture future = submitData( - payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), + final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier(); + final FluentFuture future = submitData(path, payload.getData(), strategy, schemaContext, insert, point); - final URI location = resolveLocation(uriInfo, strategy.getInstanceIdentifier(), - schemaContext, payload.getData()); + final URI location = resolveLocation(uriInfo, path, schemaContext, payload.getData()); final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location); //This method will close transactionChain if any - FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory, - strategy.getTransactionChain()); + FutureCallbackTx.addCallback(future, POST_TX_TYPE, dataFactory, strategy, path); return dataFactory.build(); } @@ -85,122 +89,70 @@ public final class PostDataTransactionUtil { * @return {@link FluentFuture} */ private static FluentFuture submitData(final YangInstanceIdentifier path, - final NormalizedNode data, + final NormalizedNode data, final RestconfStrategy strategy, final EffectiveModelContext schemaContext, - final String insert, final String point) { - strategy.prepareReadWriteExecution(); + final Insert insert, final String point) { + final RestconfTransaction transaction = strategy.prepareWriteExecution(); if (insert == null) { - makePost(path, data, schemaContext, strategy); - return strategy.commit(); + makePost(path, data, schemaContext, transaction); + return transaction.commit(); } - final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path); + PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path); + final NormalizedNode readData; switch (insert) { - case "first": - if (schemaNode instanceof ListSchemaNode) { - final NormalizedNode readData = PutDataTransactionUtil.readList(path.getParent(), - schemaContext, strategy, schemaNode); - final OrderedMapNode readList = (OrderedMapNode) readData; - if (readList == null || readList.getValue().isEmpty()) { - makePost(path, data, schemaContext, strategy); - return strategy.commit(); - } - - strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent()); - simplePost(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext, strategy); - makePost(path, readData, schemaContext, strategy); - return strategy.commit(); - } else { - final NormalizedNode readData = PutDataTransactionUtil.readList(path.getParent(), - schemaContext, strategy, schemaNode); - - final OrderedLeafSetNode readLeafList = (OrderedLeafSetNode) readData; - if (readLeafList == null || readLeafList.getValue().isEmpty()) { - makePost(path, data, schemaContext, strategy); - return strategy.commit(); - } - - strategy.delete(LogicalDatastoreType.CONFIGURATION, path.getParent().getParent()); - simplePost(LogicalDatastoreType.CONFIGURATION, path, data, schemaContext, strategy); - makePost(path, readData, schemaContext, strategy); - return strategy.commit(); + case FIRST: + readData = PutDataTransactionUtil.readList(strategy, path.getParent().getParent()); + if (readData == null || ((NormalizedNodeContainer) readData).isEmpty()) { + transaction.replace(path, data, schemaContext); + return transaction.commit(); } - case "last": - makePost(path, data, schemaContext, strategy); - return strategy.commit(); - case "before": - if (schemaNode instanceof ListSchemaNode) { - final NormalizedNode readData = PutDataTransactionUtil.readList(path.getParent(), - schemaContext, strategy, schemaNode); - final OrderedMapNode readList = (OrderedMapNode) readData; - if (readList == null || readList.getValue().isEmpty()) { - makePost(path, data, schemaContext, strategy); - return strategy.commit(); - } - - insertWithPointListPost(LogicalDatastoreType.CONFIGURATION, path, - data, schemaContext, point, readList, true, strategy); - return strategy.commit(); - } else { - final NormalizedNode readData = PutDataTransactionUtil.readList(path.getParent(), - schemaContext, strategy, schemaNode); - - final OrderedLeafSetNode readLeafList = (OrderedLeafSetNode) readData; - if (readLeafList == null || readLeafList.getValue().isEmpty()) { - makePost(path, data, schemaContext, strategy); - return strategy.commit(); - } - - insertWithPointLeafListPost(LogicalDatastoreType.CONFIGURATION, - path, data, schemaContext, point, readLeafList, true, strategy); - return strategy.commit(); + checkItemDoesNotExists(strategy.exists(LogicalDatastoreType.CONFIGURATION, path), path); + transaction.remove(path.getParent().getParent()); + transaction.replace(path, data, schemaContext); + transaction.replace(path.getParent().getParent(), readData, schemaContext); + return transaction.commit(); + case LAST: + makePost(path, data, schemaContext, transaction); + return transaction.commit(); + case BEFORE: + readData = PutDataTransactionUtil.readList(strategy, path.getParent().getParent()); + if (readData == null || ((NormalizedNodeContainer) readData).isEmpty()) { + transaction.replace(path, data, schemaContext); + return transaction.commit(); } - case "after": - if (schemaNode instanceof ListSchemaNode) { - final NormalizedNode readData = PutDataTransactionUtil.readList(path.getParent(), - schemaContext, strategy, schemaNode); - final OrderedMapNode readList = (OrderedMapNode) readData; - if (readList == null || readList.getValue().isEmpty()) { - makePost(path, data, schemaContext, strategy); - return strategy.commit(); - } - - insertWithPointListPost(LogicalDatastoreType.CONFIGURATION, path, - data, schemaContext, point, readList, false, strategy); - return strategy.commit(); - } else { - final NormalizedNode readData = PutDataTransactionUtil.readList(path.getParent(), - schemaContext, strategy, schemaNode); - - final OrderedLeafSetNode readLeafList = (OrderedLeafSetNode) readData; - if (readLeafList == null || readLeafList.getValue().isEmpty()) { - makePost(path, data, schemaContext, strategy); - return strategy.commit(); - } - - insertWithPointLeafListPost(LogicalDatastoreType.CONFIGURATION, - path, data, schemaContext, point, readLeafList, true, strategy); - return strategy.commit(); + checkItemDoesNotExists(strategy.exists(LogicalDatastoreType.CONFIGURATION, path), path); + insertWithPointPost(path, data, schemaContext, point, + (NormalizedNodeContainer) readData, true, transaction); + return transaction.commit(); + case AFTER: + readData = PutDataTransactionUtil.readList(strategy, path.getParent().getParent()); + if (readData == null || ((NormalizedNodeContainer) readData).isEmpty()) { + transaction.replace(path, data, schemaContext); + return transaction.commit(); } + checkItemDoesNotExists(strategy.exists(LogicalDatastoreType.CONFIGURATION, path), path); + insertWithPointPost(path, data, schemaContext, point, + (NormalizedNodeContainer) readData, false, transaction); + return transaction.commit(); default: throw new RestconfDocumentedException( "Used bad value of insert parameter. Possible values are first, last, before or after, but was: " - + insert, RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ATTRIBUTE); + + insert, RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.BAD_ATTRIBUTE); } } - private static void insertWithPointLeafListPost(final LogicalDatastoreType datastore, - final YangInstanceIdentifier path, - final NormalizedNode payload, - final EffectiveModelContext schemaContext, final String point, - final OrderedLeafSetNode readLeafList, - final boolean before, final RestconfStrategy strategy) { - strategy.delete(datastore, path.getParent().getParent()); + private static void insertWithPointPost(final YangInstanceIdentifier path, final NormalizedNode data, + final EffectiveModelContext schemaContext, final String point, + final NormalizedNodeContainer readList, final boolean before, + final RestconfTransaction transaction) { + final YangInstanceIdentifier parent = path.getParent().getParent(); + transaction.remove(parent); final InstanceIdentifierContext instanceIdentifier = - ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty()); + ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty()); int lastItemPosition = 0; - for (final LeafSetEntryNode nodeChild : readLeafList.getValue()) { + for (final NormalizedNode nodeChild : readList.body()) { if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { break; } @@ -210,103 +162,46 @@ public final class PostDataTransactionUtil { lastItemPosition++; } int lastInsertedPosition = 0; - final NormalizedNode emptySubtree = - ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent()); - strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); - for (final LeafSetEntryNode nodeChild : readLeafList.getValue()) { - if (lastInsertedPosition == lastItemPosition) { - TransactionUtil.checkItemDoesNotExists(strategy, datastore, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - strategy.create(datastore, path, payload); - } - final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier()); - TransactionUtil.checkItemDoesNotExists(strategy, datastore, childPath, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - strategy.create(datastore, childPath, nodeChild); - lastInsertedPosition++; - } - } - - private static void insertWithPointListPost(final LogicalDatastoreType datastore, final YangInstanceIdentifier path, - final NormalizedNode payload, - final EffectiveModelContext schemaContext, final String point, - final MapNode readList, final boolean before, - final RestconfStrategy strategy) { - strategy.delete(datastore, path.getParent().getParent()); - final InstanceIdentifierContext instanceIdentifier = - ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty()); - int lastItemPosition = 0; - for (final MapEntryNode mapEntryNode : readList.getValue()) { - if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { - break; - } - lastItemPosition++; - } - if (!before) { - lastItemPosition++; - } - int lastInsertedPosition = 0; - final NormalizedNode emptySubtree = - ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent()); - strategy.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); - for (final MapEntryNode mapEntryNode : readList.getValue()) { + final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, parent); + transaction.merge(YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + for (final NormalizedNode nodeChild : readList.body()) { if (lastInsertedPosition == lastItemPosition) { - TransactionUtil.checkItemDoesNotExists(strategy, datastore, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - strategy.create(datastore, path, payload); + transaction.replace(path, data, schemaContext); } - final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier()); - TransactionUtil.checkItemDoesNotExists(strategy, datastore, childPath, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - strategy.create(datastore, childPath, mapEntryNode); + final YangInstanceIdentifier childPath = parent.node(nodeChild.getIdentifier()); + transaction.replace(childPath, nodeChild, schemaContext); lastInsertedPosition++; } } - private static void makePost(final YangInstanceIdentifier path, final NormalizedNode data, - final SchemaContext schemaContext, final RestconfStrategy strategy) { - if (data instanceof MapNode) { - boolean merge = false; - for (final MapEntryNode child : ((MapNode) data).getValue()) { - final YangInstanceIdentifier childPath = path.node(child.getIdentifier()); - TransactionUtil.checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, childPath, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - if (!merge) { - merge = true; - TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy); - final NormalizedNode emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path); - strategy.merge(LogicalDatastoreType.CONFIGURATION, - YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree); - } - strategy.create(LogicalDatastoreType.CONFIGURATION, childPath, child); - } - } else { - TransactionUtil.checkItemDoesNotExists(strategy, LogicalDatastoreType.CONFIGURATION, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - - TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy); - strategy.create(LogicalDatastoreType.CONFIGURATION, path, data); + private static void makePost(final YangInstanceIdentifier path, final NormalizedNode data, + final SchemaContext schemaContext, final RestconfTransaction transaction) { + try { + transaction.create(path, data, schemaContext); + } catch (RestconfDocumentedException e) { + // close transaction if any and pass exception further + transaction.cancel(); + throw e; } } /** * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}. * - * @param uriInfo uri info - * @param yangInstanceIdentifier reference to {@link InstanceIdentifierContext} - * @param schemaContext reference to {@link SchemaContext} + * @param uriInfo uri info + * @param initialPath data path + * @param schemaContext reference to {@link SchemaContext} * @return {@link URI} */ - private static URI resolveLocation(final UriInfo uriInfo, final InstanceIdentifierContext yangInstanceIdentifier, - final EffectiveModelContext schemaContext, final NormalizedNode data) { + private static URI resolveLocation(final UriInfo uriInfo, final YangInstanceIdentifier initialPath, + final EffectiveModelContext schemaContext, final NormalizedNode data) { if (uriInfo == null) { return null; } - YangInstanceIdentifier path = yangInstanceIdentifier.getInstanceIdentifier(); - + YangInstanceIdentifier path = initialPath; if (data instanceof MapNode) { - final Collection children = ((MapNode) data).getValue(); + final Collection children = ((MapNode) data).body(); if (!children.isEmpty()) { path = path.node(children.iterator().next().getIdentifier()); } @@ -318,12 +213,22 @@ public final class PostDataTransactionUtil { .build(); } - private static void simplePost(final LogicalDatastoreType datastore, final YangInstanceIdentifier path, - final NormalizedNode payload, - final SchemaContext schemaContext, final RestconfStrategy strategy) { - TransactionUtil.checkItemDoesNotExists(strategy, datastore, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - TransactionUtil.ensureParentsByMerge(path, schemaContext, strategy); - strategy.create(datastore, path, payload); + /** + * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if + * data already exists. + * + * @param isExistsFuture if checked data exists + * @param path Path to be checked + */ + public static void checkItemDoesNotExists(final FluentFuture isExistsFuture, + final YangInstanceIdentifier path) { + final FutureDataFactory response = new FutureDataFactory<>(); + FutureCallbackTx.addCallback(isExistsFuture, POST_TX_TYPE, response); + + if (response.result) { + LOG.trace("Operation via Restconf was not executed because data at {} already exists", path); + throw new RestconfDocumentedException( + "Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, path); + } } }