2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.sal.restconf.impl;
10 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Throwables;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.util.concurrent.FluentFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import java.io.Closeable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map.Entry;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.concurrent.CountDownLatch;
28 import java.util.concurrent.ExecutionException;
29 import javax.annotation.PreDestroy;
30 import javax.inject.Inject;
31 import javax.inject.Singleton;
32 import javax.ws.rs.core.Response.Status;
33 import org.apache.aries.blueprint.annotation.service.Reference;
34 import org.opendaylight.mdsal.common.api.CommitInfo;
35 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
36 import org.opendaylight.mdsal.common.api.ReadFailedException;
37 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
43 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
44 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
45 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
46 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
47 import org.opendaylight.mdsal.dom.api.DOMRpcService;
48 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
49 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
50 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
51 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
52 import org.opendaylight.restconf.common.errors.RestconfError;
53 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
54 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
55 import org.opendaylight.restconf.common.patch.PatchContext;
56 import org.opendaylight.restconf.common.patch.PatchEditOperation;
57 import org.opendaylight.restconf.common.patch.PatchEntity;
58 import org.opendaylight.restconf.common.patch.PatchStatusContext;
59 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
60 import org.opendaylight.restconf.common.util.DataChangeScope;
61 import org.opendaylight.yangtools.concepts.ListenerRegistration;
62 import org.opendaylight.yangtools.yang.common.QName;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
67 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
69 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
77 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
78 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
79 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
80 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
81 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
82 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
83 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
84 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
85 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
86 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
87 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
88 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
89 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
90 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
91 import org.slf4j.Logger;
92 import org.slf4j.LoggerFactory;
95 @SuppressWarnings("checkstyle:FinalClass")
96 public class BrokerFacade implements Closeable {
97 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
99 private volatile DOMRpcService rpcService;
101 private final DOMDataBroker domDataBroker;
102 private final DOMNotificationService domNotification;
103 private final ControllerContext controllerContext;
106 public BrokerFacade(final @Reference DOMRpcService rpcService, final DOMDataBroker domDataBroker,
107 final @Reference DOMNotificationService domNotification, final ControllerContext controllerContext) {
108 this.rpcService = Objects.requireNonNull(rpcService);
109 this.domDataBroker = Objects.requireNonNull(domDataBroker);
110 this.domNotification = Objects.requireNonNull(domNotification);
111 this.controllerContext = Objects.requireNonNull(controllerContext);
117 * @deprecated Just use
118 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
119 * constructor instead.
122 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
123 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
124 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
129 public void close() {
133 * Read config data by path.
139 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
140 return readConfigurationData(path, null);
144 * Read config data by path.
149 * value of with-defaults parameter
152 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
153 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
154 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
159 * Read config data from mount point by path.
162 * mount point for reading data
167 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
168 final YangInstanceIdentifier path) {
169 return readConfigurationData(mountPoint, path, null);
173 * Read config data from mount point by path.
176 * mount point for reading data
180 * value of with-defaults parameter
183 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
184 final String withDefa) {
185 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
186 if (domDataBrokerService.isPresent()) {
187 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
188 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
191 throw dataBrokerUnavailable(path);
195 * Read operational data by path.
201 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
202 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
203 return readDataViaTransaction(tx, OPERATIONAL, path);
208 * Read operational data from mount point by path.
211 * mount point for reading data
216 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
217 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
218 if (domDataBrokerService.isPresent()) {
219 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
220 return readDataViaTransaction(tx, OPERATIONAL, path);
223 throw dataBrokerUnavailable(path);
227 * <b>PUT configuration data</b>
230 * Prepare result(status) for PUT operation and PUT data via transaction.
231 * Return wrapped status and future from PUT.
233 * @param globalSchema
234 * used by merge parents (if contains list)
243 * @return wrapper of status and future of PUT
245 public PutResult commitConfigurationDataPut(
246 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
247 final String insert, final String point) {
248 Preconditions.checkNotNull(globalSchema);
249 Preconditions.checkNotNull(path);
250 Preconditions.checkNotNull(payload);
252 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
253 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
255 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
256 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
257 return new PutResult(status, future);
261 * <b>PUT configuration data (Mount point)</b>
264 * Prepare result(status) for PUT operation and PUT data via transaction.
265 * Return wrapped status and future from PUT.
268 * mount point for getting transaction for operation and schema
269 * context for merging parents(if contains list)
278 * @return wrapper of status and future of PUT
280 public PutResult commitMountPointDataPut(
281 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
282 final String insert, final String point) {
283 Preconditions.checkNotNull(mountPoint);
284 Preconditions.checkNotNull(path);
285 Preconditions.checkNotNull(payload);
287 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
288 if (domDataBrokerService.isPresent()) {
289 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
290 domDataBrokerService.get().newReadWriteTransaction();
291 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
292 ? Status.OK : Status.CREATED;
293 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
294 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
296 return new PutResult(status, future);
298 throw dataBrokerUnavailable(path);
301 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
303 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
305 // get new transaction and schema context on server or on mounted device
306 final SchemaContext schemaContext;
307 final DOMDataTreeReadWriteTransaction patchTransaction;
308 if (mountPoint == null) {
309 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
310 patchTransaction = this.domDataBroker.newReadWriteTransaction();
312 schemaContext = mountPoint.getSchemaContext();
314 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
316 if (optional.isPresent()) {
317 patchTransaction = optional.get().newReadWriteTransaction();
319 // if mount point does not have broker it is not possible to continue and global error is reported
320 LOG.error("Http Patch {} has failed - device {} does not support broker service",
321 patchContext.getPatchId(), mountPoint.getIdentifier());
322 return new PatchStatusContext(
323 patchContext.getPatchId(),
326 ImmutableList.of(new RestconfError(
327 ErrorType.APPLICATION,
328 ErrorTag.OPERATION_FAILED,
329 "DOM data broker service isn't available for mount point "
330 + mountPoint.getIdentifier()))
335 final List<PatchStatusEntity> editCollection = new ArrayList<>();
336 List<RestconfError> editErrors;
337 boolean withoutError = true;
339 for (final PatchEntity patchEntity : patchContext.getData()) {
340 final PatchEditOperation operation = patchEntity.getOperation();
345 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
346 patchEntity.getNode(), schemaContext);
347 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
348 } catch (final RestconfDocumentedException e) {
349 LOG.error("Error call http Patch operation {} on target {}",
351 patchEntity.getTargetNode().toString());
353 editErrors = new ArrayList<>();
354 editErrors.addAll(e.getErrors());
355 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
356 withoutError = false;
363 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
364 .getTargetNode(), patchEntity.getNode(), schemaContext);
365 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
366 } catch (final RestconfDocumentedException e) {
367 LOG.error("Error call http Patch operation {} on target {}",
369 patchEntity.getTargetNode().toString());
371 editErrors = new ArrayList<>();
372 editErrors.addAll(e.getErrors());
373 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
374 withoutError = false;
382 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
384 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
385 } catch (final RestconfDocumentedException e) {
386 LOG.error("Error call http Patch operation {} on target {}",
388 patchEntity.getTargetNode().toString());
390 editErrors = new ArrayList<>();
391 editErrors.addAll(e.getErrors());
392 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
393 withoutError = false;
400 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
401 patchEntity.getNode(), schemaContext);
402 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
403 } catch (final RestconfDocumentedException e) {
404 LOG.error("Error call http Patch operation {} on target {}",
406 patchEntity.getTargetNode().toString());
408 editErrors = new ArrayList<>();
409 editErrors.addAll(e.getErrors());
410 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
411 withoutError = false;
416 LOG.error("Unsupported http Patch operation {} on target {}",
418 patchEntity.getTargetNode().toString());
423 // if errors then cancel transaction and return error status
425 patchTransaction.cancel();
426 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
429 // if no errors commit transaction
430 final CountDownLatch waiter = new CountDownLatch(1);
431 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
432 final PatchStatusContextHelper status = new PatchStatusContextHelper();
434 future.addCallback(new FutureCallback<CommitInfo>() {
436 public void onSuccess(final CommitInfo result) {
437 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
443 public void onFailure(final Throwable throwable) {
444 // if commit failed it is global error
445 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
446 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
447 false, ImmutableList.of(
448 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
451 }, MoreExecutors.directExecutor());
454 return status.getStatus();
457 // POST configuration
458 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
459 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
460 final String insert, final String point) {
461 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
462 globalSchema, insert, point);
465 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
466 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
467 final String insert, final String point) {
468 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
469 if (domDataBrokerService.isPresent()) {
470 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
471 payload, mountPoint.getSchemaContext(), insert, point);
473 throw dataBrokerUnavailable(path);
476 // DELETE configuration
477 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
478 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
481 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
482 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
483 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
484 if (domDataBrokerService.isPresent()) {
485 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
487 throw dataBrokerUnavailable(path);
491 public FluentFuture<DOMRpcResult> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
492 if (this.rpcService == null) {
493 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
495 LOG.trace("Invoke RPC {} with input: {}", type, input);
496 return this.rpcService.invokeRpc(type, input);
499 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
500 final ListenerAdapter listener) {
501 if (listener.isListening()) {
505 final YangInstanceIdentifier path = listener.getPath();
506 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
507 .getInstance(DOMDataTreeChangeService.class);
508 if (changeService == null) {
509 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
510 + this.domDataBroker);
512 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
513 ListenerRegistration<ListenerAdapter> registration =
514 changeService.registerDataTreeChangeListener(root, listener);
515 listener.setRegistration(registration);
518 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadTransaction transaction,
519 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
520 return readDataViaTransaction(transaction, datastore, path, null);
523 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadTransaction transaction,
524 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
525 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
528 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).get();
529 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
530 prepareDataByParamWithDef(optional.get(), path, withDefa);
531 } catch (InterruptedException e) {
532 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
533 throw new RestconfDocumentedException("Error reading data.", e);
534 } catch (ExecutionException e) {
535 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
536 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
537 ReadFailedException.class));
541 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
542 final YangInstanceIdentifier path, final String withDefa) {
552 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
555 final SchemaContext ctx = controllerContext.getGlobalSchema();
556 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
557 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
558 if (result instanceof ContainerNode) {
559 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
560 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
561 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
562 return builder.build();
565 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
566 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
567 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
568 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
569 return builder.build();
572 private void buildMapEntryBuilder(
573 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
574 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
575 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
576 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
577 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
578 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
579 if (child instanceof ContainerNode) {
580 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
581 Builders.containerBuilder((ContainerSchemaNode) childSchema);
582 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
583 builder.withChild(childBuilder.build());
584 } else if (child instanceof MapNode) {
585 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
586 Builders.mapBuilder((ListSchemaNode) childSchema);
587 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
588 ((ListSchemaNode) childSchema).getKeyDefinition());
589 builder.withChild(childBuilder.build());
590 } else if (child instanceof LeafNode) {
591 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
592 final Object nodeVal = child.getValue();
593 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
594 Builders.leafBuilder((LeafSchemaNode) childSchema);
595 if (keys.contains(child.getNodeType())) {
596 leafBuilder.withValue(child.getValue());
597 builder.withChild(leafBuilder.build());
600 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
601 leafBuilder.withValue(child.getValue());
602 builder.withChild(leafBuilder.build());
605 if (defaultVal != null && defaultVal.equals(nodeVal)) {
606 leafBuilder.withValue(child.getValue());
607 builder.withChild(leafBuilder.build());
615 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
616 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
617 final List<QName> keys) {
618 for (final MapEntryNode mapEntryNode : result.getValue()) {
619 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
620 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
621 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
622 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
623 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
624 builder.withChild(mapEntryBuilder.build());
628 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
629 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
630 final YangInstanceIdentifier actualPath, final boolean trim) {
631 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
632 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
633 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
634 if (child instanceof ContainerNode) {
635 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
636 Builders.containerBuilder((ContainerSchemaNode) childSchema);
637 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
638 builder.withChild(builderChild.build());
639 } else if (child instanceof MapNode) {
640 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
641 Builders.mapBuilder((ListSchemaNode) childSchema);
642 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
643 ((ListSchemaNode) childSchema).getKeyDefinition());
644 builder.withChild(childBuilder.build());
645 } else if (child instanceof LeafNode) {
646 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
647 final Object nodeVal = child.getValue();
648 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
649 Builders.leafBuilder((LeafSchemaNode) childSchema);
651 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
652 leafBuilder.withValue(child.getValue());
653 builder.withChild(leafBuilder.build());
656 if (defaultVal != null && defaultVal.equals(nodeVal)) {
657 leafBuilder.withValue(child.getValue());
658 builder.withChild(leafBuilder.build());
666 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
668 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
669 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
670 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
671 final String insert, final String point) {
672 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
673 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
674 return rwTransaction.commit();
678 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
680 private void postDataWithinTransaction(
681 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
682 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
683 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
684 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
687 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
688 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
689 final SchemaContext schemaContext, final String insert, final String point) {
690 if (insert == null) {
691 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
695 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
696 checkItemDoesNotExists(rwTransaction, datastore, path);
699 if (schemaNode instanceof ListSchemaNode) {
700 final OrderedMapNode readList =
701 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
702 if (readList == null || readList.getValue().isEmpty()) {
703 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
705 rwTransaction.delete(datastore, path.getParent().getParent());
706 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
707 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
711 final OrderedLeafSetNode<?> readLeafList =
712 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
713 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
714 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
716 rwTransaction.delete(datastore, path.getParent());
717 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
718 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
724 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
727 if (schemaNode instanceof ListSchemaNode) {
728 final OrderedMapNode readList =
729 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
730 if (readList == null || readList.getValue().isEmpty()) {
731 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
733 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
738 final OrderedLeafSetNode<?> readLeafList =
739 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
740 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
741 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
743 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
749 if (schemaNode instanceof ListSchemaNode) {
750 final OrderedMapNode readList =
751 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
752 if (readList == null || readList.getValue().isEmpty()) {
753 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
755 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
760 final OrderedLeafSetNode<?> readLeafList =
761 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
762 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
763 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
765 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
766 readLeafList, false);
771 throw new RestconfDocumentedException(
772 "Used bad value of insert parameter. Possible values are first, last, before or after, "
773 + "but was: " + insert);
777 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
778 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
779 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
780 final boolean before) {
781 rwTransaction.delete(datastore, path.getParent().getParent());
782 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
783 int lastItemPosition = 0;
784 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
785 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
793 int lastInsertedPosition = 0;
794 final NormalizedNode<?, ?> emptySubtree =
795 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
796 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
797 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
798 if (lastInsertedPosition == lastItemPosition) {
799 checkItemDoesNotExists(rwTransaction, datastore, path);
800 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
802 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
803 checkItemDoesNotExists(rwTransaction, datastore, childPath);
804 rwTransaction.put(datastore, childPath, nodeChild);
805 lastInsertedPosition++;
809 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
810 final LogicalDatastoreType datastore,
811 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
812 final String point, final MapNode readList, final boolean before) {
813 rwTransaction.delete(datastore, path.getParent().getParent());
814 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
815 int lastItemPosition = 0;
816 for (final MapEntryNode mapEntryNode : readList.getValue()) {
817 if (mapEntryNode.getIdentifier()
818 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
826 int lastInsertedPosition = 0;
827 final NormalizedNode<?, ?> emptySubtree =
828 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
829 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
830 for (final MapEntryNode mapEntryNode : readList.getValue()) {
831 if (lastInsertedPosition == lastItemPosition) {
832 checkItemDoesNotExists(rwTransaction, datastore, path);
833 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
835 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
836 checkItemDoesNotExists(rwTransaction, datastore, childPath);
837 rwTransaction.put(datastore, childPath, mapEntryNode);
838 lastInsertedPosition++;
842 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
843 final YangInstanceIdentifier parent = path.getParent();
844 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
845 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
847 if (dataSchemaNode instanceof ListSchemaNode) {
848 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
849 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
851 return dataSchemaNode;
853 if (dataSchemaNode instanceof LeafListSchemaNode) {
854 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
855 throw new RestconfDocumentedException(
856 "Insert parameter can be used only with ordered-by user leaf-list.");
858 return dataSchemaNode;
860 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
863 private static void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
864 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
865 final SchemaContext schemaContext) {
866 final Collection<? extends NormalizedNode<?, ?>> children;
867 if (payload instanceof MapNode) {
868 children = ((MapNode) payload).getValue();
869 } else if (payload instanceof LeafSetNode) {
870 children = ((LeafSetNode<?>) payload).getValue();
872 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
876 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
877 if (children.isEmpty()) {
878 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
879 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
883 // Kick off batch existence check first...
884 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
886 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
887 // result of the existence checks...
888 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
889 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
890 for (final NormalizedNode<?, ?> child : children) {
891 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
892 // as that would allow us to skip the existence checks
893 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
896 // ... finally collect existence checks and abort the transaction if any of them failed.
897 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
899 failure = check.getFailure();
900 } catch (InterruptedException e) {
901 rwTransaction.cancel();
902 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
905 if (failure != null) {
906 rwTransaction.cancel();
907 final ReadFailedException e = failure.getValue();
909 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
910 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
913 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
918 private static void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
919 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
920 final SchemaContext schemaContext) {
921 checkItemDoesNotExists(rwTransaction, datastore, path);
922 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
923 rwTransaction.put(datastore, path, payload);
926 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
927 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
929 return rwTransaction.exists(store, path).get();
930 } catch (InterruptedException e) {
931 rwTransaction.cancel();
932 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
933 } catch (ExecutionException e) {
934 rwTransaction.cancel();
935 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
936 Throwables.getCauseAs(e, ReadFailedException.class));
941 * Check if item already exists. Throws error if it does NOT already exist.
942 * @param rwTransaction Current transaction
943 * @param store Used datastore
944 * @param path Path to item to verify its existence
946 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
947 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
948 if (!doesItemExist(rwTransaction, store, path)) {
949 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
950 rwTransaction.cancel();
951 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
952 ErrorTag.DATA_MISSING);
957 * Check if item does NOT already exist. Throws error if it already exists.
958 * @param rwTransaction Current transaction
959 * @param store Used datastore
960 * @param path Path to item to verify its existence
962 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
963 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
964 if (doesItemExist(rwTransaction, store, path)) {
965 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
966 rwTransaction.cancel();
967 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
968 ErrorTag.DATA_EXISTS);
973 * PUT data and submit {@link DOMDataReadWriteTransaction}.
980 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
981 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
982 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
983 final String insert, final String point) {
984 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
985 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
986 return readWriteTransaction.commit();
990 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
992 private void putDataWithinTransaction(
993 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
994 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
995 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
996 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
999 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1000 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1001 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1002 final String insert, final String point) {
1003 if (insert == null) {
1004 makePut(rwTransaction, datastore, path, payload, schemaContext);
1008 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1009 checkItemDoesNotExists(rwTransaction, datastore, path);
1012 if (schemaNode instanceof ListSchemaNode) {
1013 final OrderedMapNode readList =
1014 (OrderedMapNode) this.readConfigurationData(path.getParent());
1015 if (readList == null || readList.getValue().isEmpty()) {
1016 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1018 rwTransaction.delete(datastore, path.getParent());
1019 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1020 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1023 final OrderedLeafSetNode<?> readLeafList =
1024 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1025 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1026 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1028 rwTransaction.delete(datastore, path.getParent());
1029 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1030 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1036 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1039 if (schemaNode instanceof ListSchemaNode) {
1040 final OrderedMapNode readList =
1041 (OrderedMapNode) this.readConfigurationData(path.getParent());
1042 if (readList == null || readList.getValue().isEmpty()) {
1043 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1045 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1049 final OrderedLeafSetNode<?> readLeafList =
1050 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1051 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1052 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1054 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1055 readLeafList, true);
1060 if (schemaNode instanceof ListSchemaNode) {
1061 final OrderedMapNode readList =
1062 (OrderedMapNode) this.readConfigurationData(path.getParent());
1063 if (readList == null || readList.getValue().isEmpty()) {
1064 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1066 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1070 final OrderedLeafSetNode<?> readLeafList =
1071 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1072 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1073 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1075 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1076 readLeafList, false);
1081 throw new RestconfDocumentedException(
1082 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1087 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1088 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1089 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1090 final boolean before) {
1091 tx.delete(datastore, path.getParent());
1092 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1094 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1095 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1104 final NormalizedNode<?, ?> emptySubtree =
1105 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1106 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1107 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1108 if (index2 == index1) {
1109 simplePut(datastore, path, tx, schemaContext, payload);
1111 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1112 tx.put(datastore, childPath, nodeChild);
1117 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1118 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1119 final String point, final OrderedMapNode readList, final boolean before) {
1120 tx.delete(datastore, path.getParent());
1121 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1123 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1124 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1133 final NormalizedNode<?, ?> emptySubtree =
1134 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1135 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1136 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1137 if (index2 == index1) {
1138 simplePut(datastore, path, tx, schemaContext, payload);
1140 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1141 tx.put(datastore, childPath, mapEntryNode);
1146 private static void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1147 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1148 if (payload instanceof MapNode) {
1149 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1150 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1151 ensureParentsByMerge(datastore, path, tx, schemaContext);
1152 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1153 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1154 tx.put(datastore, childPath, child);
1157 simplePut(datastore, path, tx, schemaContext, payload);
1161 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1162 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext,
1163 final NormalizedNode<?, ?> payload) {
1164 ensureParentsByMerge(datastore, path, tx, schemaContext);
1165 tx.put(datastore, path, payload);
1168 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1169 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1170 final YangInstanceIdentifier path) {
1171 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1172 checkItemExists(readWriteTransaction, datastore, path);
1173 readWriteTransaction.delete(datastore, path);
1174 return readWriteTransaction.commit();
1177 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1178 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1179 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1180 tx.delete(datastore, path);
1183 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1184 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1185 final SchemaContext schemaContext) {
1186 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1187 ensureParentsByMerge(datastore, path, tx, schemaContext);
1189 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1190 // OpenDaylight should not change it.
1191 tx.merge(datastore, path, payload);
1194 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1195 if (listener.isListening()) {
1199 final SchemaPath path = listener.getSchemaPath();
1200 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1201 .registerNotificationListener(listener, path);
1203 listener.setRegistration(registration);
1206 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1207 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1208 final SchemaContext schemaContext) {
1209 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1210 YangInstanceIdentifier rootNormalizedPath = null;
1212 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1214 while (it.hasNext()) {
1215 final PathArgument pathArgument = it.next();
1216 if (rootNormalizedPath == null) {
1217 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1221 normalizedPathWithoutChildArgs.add(pathArgument);
1225 if (normalizedPathWithoutChildArgs.isEmpty()) {
1229 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1231 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1232 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1233 tx.merge(store, rootNormalizedPath, parentStructure);
1236 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1237 LOG.warn("DOM data broker service is not available for mount point {}", path);
1238 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1241 private static final class PatchStatusContextHelper {
1242 PatchStatusContext status;
1244 public PatchStatusContext getStatus() {
1248 public void setStatus(final PatchStatusContext status) {
1249 this.status = status;