From: Jakub Toth Date: Sun, 29 May 2016 08:55:37 +0000 (+0200) Subject: Bug 5528 - Impl Post data X-Git-Tag: release/boron~50 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=96fa29a30499d4ae0ad21cb9a5c8fe9d401512a4;p=netconf.git Bug 5528 - Impl Post data *added new constant for post data *added location to response factory *moved ensureParentsByMerge to common util class of Put and Post Change-Id: I2d525a1e602170ef41fd332bb8db9dfabd59e188 Signed-off-by: Jakub Toth --- diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java index 68f9652063..efde43a83d 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java @@ -24,6 +24,7 @@ import org.opendaylight.restconf.handlers.SchemaContextHandler; import org.opendaylight.restconf.handlers.TransactionChainHandler; import org.opendaylight.restconf.restful.services.api.RestconfDataService; import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper; +import org.opendaylight.restconf.restful.utils.PostDataTransactionUtil; import org.opendaylight.restconf.restful.utils.PutDataTransactionUtil; import org.opendaylight.restconf.restful.utils.ReadDataTransactionUtil; import org.opendaylight.restconf.restful.utils.RestconfDataServiceConstant; @@ -95,12 +96,26 @@ public class RestconfDataServiceImpl implements RestconfDataService { @Override public Response postData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { - throw new UnsupportedOperationException("Not yet implemented."); + return postData(payload, uriInfo); } @Override public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) { - throw new UnsupportedOperationException("Not yet implemented."); + Preconditions.checkNotNull(payload); + + final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); + DOMDataReadWriteTransaction transaction = null; + SchemaContextRef ref = null; + if (mountPoint == null) { + transaction = this.transactionChainHandler.get().newReadWriteTransaction(); + ref = new SchemaContextRef(this.schemaContextHandler.get()); + } else { + transaction = transactionOfMountPoint(mountPoint); + ref = new SchemaContextRef(mountPoint.getSchemaContext()); + } + final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper( + payload.getInstanceIdentifierContext(), mountPoint, transaction); + return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref); } @Override 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 new file mode 100644 index 0000000000..2d9c246ac2 --- /dev/null +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtil.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +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.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.impl.schema.ImmutableNodes; +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 PostDataTransactionUtil() { + throw new UnsupportedOperationException("Util class."); + } + + /** + * Check mount point and prepare variables for post data + * + * @param uriInfo + * + * @param payload + * - data + * @param transactionNode + * - wrapper for transaction data + * @param schemaContextRef + * - reference to actual {@link SchemaContext} + * @return {@link CheckedFuture} + */ + public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload, + final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef) { + final CheckedFuture future = submitData( + payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(), + transactionNode, schemaContextRef.get()); + 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); + 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 + * @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); + + 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); + } + } else if (data instanceof LeafSetNode) { + for (final LeafSetEntryNode child : ((LeafSetNode) data).getValue()) { + putChild(child, transaction, path); + } + } else if (data instanceof ContainerNode) { + for (final DataContainerChild child : ((ContainerNode) data).getValue()) { + putChild(child, transaction, path); + } + } + 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); + } + + /** + * 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); + } + } catch (InterruptedException | ExecutionException e) { + LOG.warn("It wasn't possible to get data loaded from datastore at path {}", path, 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} + * @return {@link URI} + */ + private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode, + final SchemaContextRef schemaContextRef) { + if (uriInfo == null) { + return null; + } + + final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder(); + uriBuilder.path("data"); + uriBuilder.path(ParserIdentifier.stringFromYangInstanceIdentifier(transactionNode.getInstanceIdentifier().getInstanceIdentifier(), + schemaContextRef.get())); + + return uriBuilder.build(); + } +} + diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java index 9b919f762e..698a4787cb 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java @@ -7,11 +7,8 @@ */ package org.opendaylight.restconf.restful.utils; -import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.util.concurrent.CheckedFuture; -import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; import javax.ws.rs.core.Response; @@ -32,9 +29,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaNode; @@ -166,52 +161,8 @@ public final class PutDataTransactionUtil { private static CheckedFuture submitData(final YangInstanceIdentifier path, final SchemaContext schemaContext, final DOMDataWriteTransaction writeTx, final NormalizedNode data) { - ensureParentsByMerge(path, schemaContext, writeTx); + TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx); writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); return writeTx.submit(); } - - /** - * Merged parents of data - * - * @param path - * - path of data - * @param schemaContext - * - {@link SchemaContext} - * @param writeTx - * - write transaction - */ - private static void ensureParentsByMerge(final YangInstanceIdentifier path, final SchemaContext schemaContext, - final DOMDataWriteTransaction writeTx) { - final List normalizedPathWithoutChildArgs = new ArrayList<>(); - boolean hasList = false; - YangInstanceIdentifier rootNormalizedPath = null; - - final Iterator it = path.getPathArguments().iterator(); - final Module module = schemaContext.findModuleByNamespaceAndRevision( - path.getLastPathArgument().getNodeType().getModule().getNamespace(), - path.getLastPathArgument().getNodeType().getModule().getRevision()); - - while (it.hasNext()) { - final PathArgument pathArgument = it.next(); - if (rootNormalizedPath == null) { - rootNormalizedPath = YangInstanceIdentifier.create(pathArgument); - } - if (it.hasNext()) { - normalizedPathWithoutChildArgs.add(pathArgument); - if (module.getDataChildByName(pathArgument.getNodeType()) instanceof ListSchemaNode) { - hasList = true; - } - } - } - if (normalizedPathWithoutChildArgs.isEmpty()) { - return; - } - if (hasList) { - Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received"); - final NormalizedNode parentStructure = ImmutableNodes.fromInstanceId(schemaContext, - YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)); - writeTx.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure); - } - } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java index a8545568f0..d3f9a68453 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java @@ -7,7 +7,9 @@ */ package org.opendaylight.restconf.restful.utils; +import java.net.URI; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import org.apache.commons.lang3.builder.Builder; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -15,14 +17,25 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; final class ResponseFactory extends FutureDataFactory implements Builder { private final NormalizedNode readData; + private final URI location; ResponseFactory(final NormalizedNode readData) { this.readData = readData; + this.location = null; + } + + ResponseFactory(final NormalizedNode readData, final URI location) { + this.readData = readData; + this.location = location; } @Override public Response build() { final Status status = this.readData != null ? Status.OK : Status.CREATED; - return Response.status(status).build(); + final ResponseBuilder responseBuilder = Response.status(status); + if (this.location != null) { + responseBuilder.location(this.location); + } + return responseBuilder.build(); } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfDataServiceConstant.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfDataServiceConstant.java index ca39e56457..19bb87d863 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfDataServiceConstant.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfDataServiceConstant.java @@ -67,4 +67,16 @@ public final class RestconfDataServiceConstant { throw new UnsupportedOperationException("Util class."); } } + + /** + * Constants for data to post + * + */ + public final class PostData { + public static final String POST_TX_TYPE = "POST"; + + private PostData() { + throw new UnsupportedOperationException("Util class."); + } + } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/TransactionUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/TransactionUtil.java new file mode 100644 index 0000000000..a05aea1938 --- /dev/null +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/TransactionUtil.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.restconf.restful.utils; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +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.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Util class for common methods of transactions + * + */ +public final class TransactionUtil { + + private TransactionUtil() { + throw new UnsupportedOperationException("Util class"); + } + + /** + * Merged parents of data + * + * @param path + * - path of data + * @param schemaContext + * - {@link SchemaContext} + * @param writeTx + * - write transaction + */ + public static void ensureParentsByMerge(final YangInstanceIdentifier path, final SchemaContext schemaContext, + final DOMDataWriteTransaction writeTx) { + final List normalizedPathWithoutChildArgs = new ArrayList<>(); + boolean hasList = false; + YangInstanceIdentifier rootNormalizedPath = null; + + final Iterator it = path.getPathArguments().iterator(); + final Module module = schemaContext.findModuleByNamespaceAndRevision( + path.getLastPathArgument().getNodeType().getModule().getNamespace(), + path.getLastPathArgument().getNodeType().getModule().getRevision()); + + while (it.hasNext()) { + final PathArgument pathArgument = it.next(); + if (rootNormalizedPath == null) { + rootNormalizedPath = YangInstanceIdentifier.create(pathArgument); + } + if (it.hasNext()) { + normalizedPathWithoutChildArgs.add(pathArgument); + if (module.getDataChildByName(pathArgument.getNodeType()) instanceof ListSchemaNode) { + hasList = true; + } + } + } + if (normalizedPathWithoutChildArgs.isEmpty()) { + return; + } + if (hasList) { + Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received"); + final NormalizedNode parentStructure = ImmutableNodes.fromInstanceId(schemaContext, + YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)); + writeTx.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure); + } + } +} diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java index 891784a299..6fb58f5a2b 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java @@ -64,6 +64,18 @@ public final class ParserIdentifier { return new InstanceIdentifierContext(deserialize, child.getDataSchemaNode(), null, schemaContext); } + /** + * Make {@link String} from {@link YangInstanceIdentifier} + * + * @param instanceIdentifier + * @param schemaContext + * @return + */ + public static String stringFromYangInstanceIdentifier(final YangInstanceIdentifier instanceIdentifier, + final SchemaContext schemaContext) { + return IdentifierCodec.serialize(instanceIdentifier, schemaContext); + } + /** * Make a {@link QName} from identifier * diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/impl/services/RestconfSchemaServiceTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/impl/services/RestconfSchemaServiceTest.java index 712e89f83a..a33c5149e9 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/impl/services/RestconfSchemaServiceTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/impl/services/RestconfSchemaServiceTest.java @@ -58,8 +58,10 @@ public class RestconfSchemaServiceTest { private RestconfSchemaService schemaService; // handlers - @Mock private SchemaContextHandler mockContextHandler; - @Mock private DOMMountPointServiceHandler mockMountPointHandler; + @Mock + private SchemaContextHandler mockContextHandler; + @Mock + private DOMMountPointServiceHandler mockMountPointHandler; // schema context with modules private SchemaContext schemaContext;