X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Frestconf%2Frestful%2Futils%2FPostDataTransactionUtil.java;h=23ce4f406a0f3d13a175f88ab0219adc5832b217;hb=6c7dc98ecbf088276bdff9b9fadb0e1236711f8a;hp=2d9c246ac256af6300f4f44436d792e1fb23f91c;hpb=96fa29a30499d4ae0ad21cb9a5c8fe9d401512a4;p=netconf.git diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtil.java index 2d9c246ac2..23ce4f406a 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtil.java @@ -8,34 +8,31 @@ package org.opendaylight.restconf.restful.utils; import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.ListenableFuture; import java.net.URI; -import java.util.concurrent.ExecutionException; import javax.ws.rs.core.Response; 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.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext; import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; -import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag; -import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.restconf.common.references.SchemaContextRef; import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper; import org.opendaylight.restconf.utils.parser.ParserIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; -import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; 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.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.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,19 +60,19 @@ public final class PostDataTransactionUtil { * - wrapper for transaction data * @param schemaContextRef * - reference to actual {@link SchemaContext} + * @param point + * @param insert * @return {@link CheckedFuture} */ public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload, - final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef) { + final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert, + final String point) { final CheckedFuture future = submitData( payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), - transactionNode, schemaContextRef.get()); + transactionNode, schemaContextRef.get(), insert, point); final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef); - final ResponseFactory dataFactory = new ResponseFactory( - ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode), - location); - FutureCallbackTx.addCallback(future, transactionNode.getTransaction(), - RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory); + final ResponseFactory dataFactory = new ResponseFactory(null, location); + FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory); return dataFactory.build(); } @@ -90,76 +87,223 @@ public final class PostDataTransactionUtil { * - wrapper for data to transaction * @param schemaContext * - schema context of data + * @param point + * - query parameter + * @param insert + * - query parameter * @return {@link CheckedFuture} */ private static CheckedFuture submitData(final YangInstanceIdentifier path, final NormalizedNode data, final TransactionVarsWrapper transactionNode, - final SchemaContext schemaContext) { - final DOMDataReadWriteTransaction transaction = transactionNode.getTransaction(); - final NormalizedNode node = ImmutableNodes.fromInstanceId(schemaContext, path); - transaction.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(node.getIdentifier()), node); - TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction); + final SchemaContext schemaContext, final String insert, final String point) { + final DOMTransactionChain domTransactionChain = transactionNode.getTransactionChain(); + final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction(); + 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); - if (data instanceof MapNode) { - for (final MapEntryNode child : ((MapNode) data).getValue()) { - putChild(child, transaction, path); - } - } else if (data instanceof AugmentationNode) { - for (final DataContainerChild child : ((AugmentationNode) data).getValue()) { - putChild(child, transaction, path); - } - } else if (data instanceof ChoiceNode) { - for (final DataContainerChild child : ((ChoiceNode) data).getValue()) { - putChild(child, transaction, path); + 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); } - } else if (data instanceof LeafSetNode) { - for (final LeafSetEntryNode child : ((LeafSetNode) data).getValue()) { - putChild(child, transaction, path); + } + } + + 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 = + ControllerContext.getInstance().toInstanceIdentifier(point); + int p = 0; + for (final LeafSetEntryNode nodeChild : readLeafList.getValue()) { + if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { + break; } - } else if (data instanceof ContainerNode) { - for (final DataContainerChild child : ((ContainerNode) data).getValue()) { - putChild(child, transaction, path); + p++; + } + if (!before) { + p++; + } + int h = 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 (h == p) { + 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); + h++; } - return transaction.submit(); } - /** - * Prepare data for submit - * - * @param child - * - data - * @param readWriteTx - * - transaction - * @param path - * - path to data - */ - private static void putChild(final NormalizedNode child, final DOMDataReadWriteTransaction readWriteTx, - final YangInstanceIdentifier path) { - final YangInstanceIdentifier childPath = path.node(child.getIdentifier()); - checkItemDesNotExits(childPath, readWriteTx); - readWriteTx.put(LogicalDatastoreType.CONFIGURATION, childPath, child); + 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 = + ControllerContext.getInstance().toInstanceIdentifier(point); + int p = 0; + for (final MapEntryNode mapEntryNode : readList.getValue()) { + if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { + break; + } + p++; + } + if (!before) { + p++; + } + int h = 0; + final NormalizedNode emptySubtree = + ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent()); + rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + for (final MapEntryNode mapEntryNode : readList.getValue()) { + if (h == p) { + TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, path, + RestconfDataServiceConstant.PostData.POST_TX_TYPE); + rWTransaction.put(datastore, path, payload); + } + 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); + h++; + } } - /** - * Check if data posted to create doesn't exits. - * - * @param path - * - path to data - * @param readWriteTx - * - read write transaction - */ - private static void checkItemDesNotExits(final YangInstanceIdentifier path, - final DOMDataReadWriteTransaction readWriteTx) { - final ListenableFuture existData = readWriteTx.exists(LogicalDatastoreType.CONFIGURATION, path); - try { - if (existData.get()) { - readWriteTx.cancel(); - throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL, - ErrorTag.DATA_EXISTS); + 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); } - } catch (InterruptedException | ExecutionException e) { - LOG.warn("It wasn't possible to get data loaded from datastore at path {}", path, e); + } else { + TransactionUtil.checkItemDoesNotExists( + transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path, + RestconfDataServiceConstant.PostData.POST_TX_TYPE); + + TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction); + transaction.put(LogicalDatastoreType.CONFIGURATION, path, data); } } @@ -187,5 +331,13 @@ public final class PostDataTransactionUtil { return uriBuilder.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); + } +}