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;
* - 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<Void, TransactionCommitFailedException> 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();
}
* - 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<Void, TransactionCommitFailedException> 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<? extends PathArgument, ?> child : ((AugmentationNode) data).getValue()) {
- putChild(child, transaction, path);
- }
- } else if (data instanceof ChoiceNode) {
- for (final DataContainerChild<? extends PathArgument, ?> 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<? extends PathArgument, ?> 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<Boolean> 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);
}
}
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);
+ }
+}