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=16386a2e856ab19eee2db69bb6d59752d7bfdd40;hb=29e06cbd92b039d501a004cabcf0eb19f63c427a;hp=9c7014ff471dea2aca377169bd10326cedc34d6e;hpb=232008a47d7ed50f062f5e11a416e5cf6f92e128;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 9c7014ff47..16386a2e85 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 @@ -7,255 +7,153 @@ */ package org.opendaylight.restconf.nb.rfc8040.rests.utils; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FluentFuture; import java.net.URI; +import java.util.Collection; +import java.util.Optional; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; -import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.dom.api.DOMTransactionChain; 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.nb.rfc8040.references.SchemaContextRef; -import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper; +import org.opendaylight.restconf.nb.rfc8040.InsertParam; +import org.opendaylight.restconf.nb.rfc8040.PointParam; +import org.opendaylight.restconf.nb.rfc8040.WriteDataParams; +import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload; +import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy; +import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction; +import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec; import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; 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.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; 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); + private static final String POST_TX_TYPE = "POST"; private PostDataTransactionUtil() { - throw new UnsupportedOperationException("Util class."); + // Hidden on purpose } /** - * Check mount point and prepare variables for post data. - * - * @param uriInfo + * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} if any inside of object + * {@link RestconfStrategy} provided as a parameter. * - * @param payload - * data - * @param transactionNode - * wrapper for transaction data - * @param schemaContextRef - * reference to actual {@link SchemaContext} - * @param point - * point - * @param insert - * insert - * @return {@link CheckedFuture} + * @param uriInfo uri info + * @param payload data + * @param strategy Object that perform the actual DS operations + * @param schemaContext reference to actual {@link EffectiveModelContext} + * @param params {@link WriteDataParams} + * @return {@link Response} */ - public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload, - final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert, - final String point) { - final CheckedFuture future = submitData( - payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), - transactionNode, schemaContextRef.get(), insert, point); - final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef); + public static Response postData(final UriInfo uriInfo, final NormalizedNodePayload payload, + final RestconfStrategy strategy, + final EffectiveModelContext schemaContext, final WriteDataParams params) { + final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier(); + final FluentFuture future = submitData(path, payload.getData(), + strategy, schemaContext, params); + final URI location = resolveLocation(uriInfo, path, schemaContext, payload.getData()); final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location); - FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory); + //This method will close transactionChain if any + FutureCallbackTx.addCallback(future, POST_TX_TYPE, dataFactory, path); return dataFactory.build(); } /** * Post data by type. * - * @param path - * path - * @param data - * data - * @param transactionNode - * wrapper for data to transaction - * @param schemaContext - * schema context of data - * @param point - * query parameter - * @param insert - * query parameter - * @return {@link CheckedFuture} + * @param path path + * @param data data + * @param strategy object that perform the actual DS operations + * @param schemaContext schema context of data + * @param point query parameter + * @param insert query parameter + * @return {@link FluentFuture} */ - private static CheckedFuture submitData(final YangInstanceIdentifier path, - final NormalizedNode data, final TransactionVarsWrapper transactionNode, - final SchemaContext schemaContext, final String insert, final String point) { - final DOMTransactionChain domTransactionChain = transactionNode.getTransactionChain(); - final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction(); + private static FluentFuture submitData(final YangInstanceIdentifier path, + final NormalizedNode data, + final RestconfStrategy strategy, + final EffectiveModelContext schemaContext, + final WriteDataParams params) { + final RestconfTransaction transaction = strategy.prepareWriteExecution(); + final InsertParam insert = params.insert(); if (insert == null) { - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } else { - final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path); - switch (insert) { - case "first": - if (schemaNode instanceof ListSchemaNode) { - final NormalizedNode readData = - PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain, - schemaNode); - final OrderedMapNode readList = (OrderedMapNode) readData; - if (readList == null || readList.getValue().isEmpty()) { - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } else { - newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, - path.getParent().getParent()); - simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, - schemaContext, domTransactionChain); - makePost(path, readData, schemaContext, domTransactionChain, - newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } - } else { - final NormalizedNode readData = PutDataTransactionUtil - .readList(path.getParent(), schemaContext, domTransactionChain, schemaNode); - - final OrderedLeafSetNode readLeafList = (OrderedLeafSetNode) readData; - if (readLeafList == null || readLeafList.getValue().isEmpty()) { - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } else { - newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, - path.getParent().getParent()); - simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data, - schemaContext, domTransactionChain); - makePost(path, readData, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } - } - case "last": - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - case "before": - if (schemaNode instanceof ListSchemaNode) { - final NormalizedNode readData = - PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain, - schemaNode); - final OrderedMapNode readList = (OrderedMapNode) readData; - if (readList == null || readList.getValue().isEmpty()) { - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } else { - insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, - data, schemaContext, point, readList, true, domTransactionChain); - return newReadWriteTransaction.submit(); - } - } else { - final NormalizedNode readData = - PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain, - schemaNode); - - final OrderedLeafSetNode readLeafList = (OrderedLeafSetNode) readData; - if (readLeafList == null || readLeafList.getValue().isEmpty()) { - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } else { - insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, - path, data, schemaContext, point, readLeafList, true, domTransactionChain); - return newReadWriteTransaction.submit(); - } - } - case "after": - if (schemaNode instanceof ListSchemaNode) { - final NormalizedNode readData = - PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain, - schemaNode); - final OrderedMapNode readList = (OrderedMapNode) readData; - if (readList == null || readList.getValue().isEmpty()) { - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } else { - insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, - data, schemaContext, point, readList, false, domTransactionChain); - return newReadWriteTransaction.submit(); - } - } else { - final NormalizedNode readData = - PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain, - schemaNode); - - final OrderedLeafSetNode readLeafList = (OrderedLeafSetNode) readData; - if (readLeafList == null || readLeafList.getValue().isEmpty()) { - makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction); - return newReadWriteTransaction.submit(); - } else { - insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, - path, data, schemaContext, point, readLeafList, true, domTransactionChain); - return newReadWriteTransaction.submit(); - } - } - default: - throw new RestconfDocumentedException( - "Used bad value of insert parameter. Possible values are first, last, before or after, " - + "but was: " + insert); - } + makePost(path, data, schemaContext, transaction); + return transaction.commit(); } - } - private static void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rwTransaction, - final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, - final SchemaContext schemaContext, final String point, final OrderedLeafSetNode readLeafList, - final boolean before, final DOMTransactionChain domTransactionChain) { - rwTransaction.delete(datastore, path.getParent().getParent()); - final InstanceIdentifierContext instanceIdentifier = - ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent()); - int lastItemPosition = 0; - for (final LeafSetEntryNode nodeChild : readLeafList.getValue()) { - if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { - break; - } - lastItemPosition++; - } - if (!before) { - lastItemPosition++; - } - int lastInsertedPosition = 0; - final NormalizedNode emptySubtree = - ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent()); - rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); - for (final LeafSetEntryNode nodeChild : readLeafList.getValue()) { - if (lastInsertedPosition == lastItemPosition) { - TransactionUtil.checkItemDoesNotExists(domTransactionChain, rwTransaction, datastore, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - rwTransaction.put(datastore, path, payload); - } - final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier()); - TransactionUtil.checkItemDoesNotExists(domTransactionChain, rwTransaction, datastore, childPath, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - rwTransaction.put(datastore, childPath, nodeChild); - lastInsertedPosition++; + PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path); + final NormalizedNode readData; + switch (insert) { + case FIRST: + 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); + 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(); + } + checkItemDoesNotExists(strategy.exists(LogicalDatastoreType.CONFIGURATION, path), path); + insertWithPointPost(path, data, schemaContext, params.getPoint(), + (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, params.getPoint(), + (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, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE); } } - private static void insertWithPointListPost(final DOMDataReadWriteTransaction rwTransaction, - final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, - final SchemaContext schemaContext, final String point, final MapNode readList, final boolean before, - final DOMTransactionChain domTransactionChain) { - rwTransaction.delete(datastore, path.getParent().getParent()); - final InstanceIdentifierContext instanceIdentifier = - ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent()); + private static void insertWithPointPost(final YangInstanceIdentifier path, final NormalizedNode data, + final EffectiveModelContext schemaContext, final PointParam point, + final NormalizedNodeContainer readList, final boolean before, + final RestconfTransaction transaction) { + final YangInstanceIdentifier parent = path.getParent().getParent(); + transaction.remove(parent); + final InstanceIdentifierContext instanceIdentifier = + // FIXME: Point should be able to give us this method + ParserIdentifier.toInstanceIdentifier(point.value(), schemaContext, Optional.empty()); int lastItemPosition = 0; - for (final MapEntryNode mapEntryNode : readList.getValue()) { - if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { + for (final NormalizedNode nodeChild : readList.body()) { + if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { break; } lastItemPosition++; @@ -264,84 +162,70 @@ public final class PostDataTransactionUtil { lastItemPosition++; } int lastInsertedPosition = 0; - final NormalizedNode emptySubtree = - ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent()); - rwTransaction.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(domTransactionChain, rwTransaction, datastore, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - rwTransaction.put(datastore, path, payload); + transaction.replace(path, data, schemaContext); } - final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier()); - TransactionUtil.checkItemDoesNotExists(domTransactionChain, rwTransaction, datastore, childPath, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - rwTransaction.put(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 DOMTransactionChain transactionChain, - final DOMDataReadWriteTransaction transaction) { - if (data instanceof MapNode) { - boolean merge = false; - for (final MapEntryNode child : ((MapNode) data).getValue()) { - final YangInstanceIdentifier childPath = path.node(child.getIdentifier()); - TransactionUtil.checkItemDoesNotExists( - transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - if (!merge) { - merge = true; - TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction); - final NormalizedNode emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path); - transaction.merge(LogicalDatastoreType.CONFIGURATION, - YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree); - } - transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child); - } - } else { - TransactionUtil.checkItemDoesNotExists( - transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - - TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction); - transaction.put(LogicalDatastoreType.CONFIGURATION, path, data); + private static void makePost(final YangInstanceIdentifier path, final NormalizedNode data, + final EffectiveModelContext 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 transactionNode - * wrapper for data of transaction - * @param schemaContextRef - * 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 TransactionVarsWrapper transactionNode, - final SchemaContextRef schemaContextRef) { + private static URI resolveLocation(final UriInfo uriInfo, final YangInstanceIdentifier initialPath, + final EffectiveModelContext schemaContext, final NormalizedNode data) { if (uriInfo == null) { return null; } - final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder(); - uriBuilder.path("data"); - uriBuilder.path(ParserIdentifier - .stringFromYangInstanceIdentifier(transactionNode.getInstanceIdentifier().getInstanceIdentifier(), - schemaContextRef.get())); + YangInstanceIdentifier path = initialPath; + if (data instanceof MapNode) { + final Collection children = ((MapNode) data).body(); + if (!children.isEmpty()) { + path = path.node(children.iterator().next().getIdentifier()); + } + } - return uriBuilder.build(); + return uriInfo.getBaseUriBuilder().path("data").path(IdentifierCodec.serialize(path, schemaContext)).build(); } - private static void simplePost(final DOMDataReadWriteTransaction rwTransaction, - final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, - final SchemaContext schemaContext, final DOMTransactionChain transactionChain) { - TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path, - RestconfDataServiceConstant.PostData.POST_TX_TYPE); - TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction); - rwTransaction.put(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); + } } }