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.ListenableFuture;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import java.io.Closeable;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutionException;
30 import javax.annotation.PreDestroy;
31 import javax.inject.Inject;
32 import javax.inject.Singleton;
33 import javax.ws.rs.core.Response.Status;
34 import org.apache.aries.blueprint.annotation.service.Reference;
35 import org.opendaylight.mdsal.common.api.CommitInfo;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.mdsal.common.api.ReadFailedException;
38 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
44 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
45 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
46 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
47 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
48 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
49 import org.opendaylight.mdsal.dom.api.DOMRpcService;
50 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
51 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
52 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
53 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
54 import org.opendaylight.restconf.common.errors.RestconfError;
55 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
56 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
57 import org.opendaylight.restconf.common.patch.PatchContext;
58 import org.opendaylight.restconf.common.patch.PatchEditOperation;
59 import org.opendaylight.restconf.common.patch.PatchEntity;
60 import org.opendaylight.restconf.common.patch.PatchStatusContext;
61 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
62 import org.opendaylight.restconf.common.util.DataChangeScope;
63 import org.opendaylight.yangtools.concepts.ListenerRegistration;
64 import org.opendaylight.yangtools.yang.common.QName;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
69 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
71 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
79 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
80 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
81 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
82 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
83 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
84 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
85 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
86 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
87 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
88 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
89 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
91 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
92 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
93 import org.slf4j.Logger;
94 import org.slf4j.LoggerFactory;
97 @SuppressWarnings("checkstyle:FinalClass")
98 public class BrokerFacade implements Closeable {
99 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
101 private volatile DOMRpcService rpcService;
103 private final DOMDataBroker domDataBroker;
104 private final DOMNotificationService domNotification;
105 private final ControllerContext controllerContext;
108 public BrokerFacade(final @Reference DOMRpcService rpcService, final DOMDataBroker domDataBroker,
109 final @Reference DOMNotificationService domNotification, final ControllerContext controllerContext) {
110 this.rpcService = Objects.requireNonNull(rpcService);
111 this.domDataBroker = Objects.requireNonNull(domDataBroker);
112 this.domNotification = Objects.requireNonNull(domNotification);
113 this.controllerContext = Objects.requireNonNull(controllerContext);
119 * @deprecated Just use
120 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
121 * constructor instead.
124 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
125 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
126 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
131 public void close() {
135 * Read config data by path.
141 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
142 return readConfigurationData(path, null);
146 * Read config data by path.
151 * value of with-defaults parameter
154 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
155 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
156 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
161 * Read config data from mount point by path.
164 * mount point for reading data
169 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
170 final YangInstanceIdentifier path) {
171 return readConfigurationData(mountPoint, path, null);
175 * Read config data from mount point by path.
178 * mount point for reading data
182 * value of with-defaults parameter
185 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
186 final String withDefa) {
187 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
188 if (domDataBrokerService.isPresent()) {
189 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
190 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
193 throw dataBrokerUnavailable(path);
197 * Read operational data by path.
203 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
204 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
205 return readDataViaTransaction(tx, OPERATIONAL, path);
210 * Read operational data from mount point by path.
213 * mount point for reading data
218 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
219 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
220 if (domDataBrokerService.isPresent()) {
221 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
222 return readDataViaTransaction(tx, OPERATIONAL, path);
225 throw dataBrokerUnavailable(path);
229 * <b>PUT configuration data</b>
232 * Prepare result(status) for PUT operation and PUT data via transaction.
233 * Return wrapped status and future from PUT.
235 * @param globalSchema
236 * used by merge parents (if contains list)
245 * @return wrapper of status and future of PUT
247 public PutResult commitConfigurationDataPut(
248 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
249 final String insert, final String point) {
250 Preconditions.checkNotNull(globalSchema);
251 Preconditions.checkNotNull(path);
252 Preconditions.checkNotNull(payload);
254 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
255 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
257 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
258 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
259 return new PutResult(status, future);
263 * <b>PUT configuration data (Mount point)</b>
266 * Prepare result(status) for PUT operation and PUT data via transaction.
267 * Return wrapped status and future from PUT.
270 * mount point for getting transaction for operation and schema
271 * context for merging parents(if contains list)
280 * @return wrapper of status and future of PUT
282 public PutResult commitMountPointDataPut(
283 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
284 final String insert, final String point) {
285 Preconditions.checkNotNull(mountPoint);
286 Preconditions.checkNotNull(path);
287 Preconditions.checkNotNull(payload);
289 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
290 if (domDataBrokerService.isPresent()) {
291 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
292 domDataBrokerService.get().newReadWriteTransaction();
293 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
294 ? Status.OK : Status.CREATED;
295 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
296 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
298 return new PutResult(status, future);
300 throw dataBrokerUnavailable(path);
303 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
305 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
307 // get new transaction and schema context on server or on mounted device
308 final SchemaContext schemaContext;
309 final DOMDataTreeReadWriteTransaction patchTransaction;
310 if (mountPoint == null) {
311 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
312 patchTransaction = this.domDataBroker.newReadWriteTransaction();
314 schemaContext = mountPoint.getSchemaContext();
316 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
318 if (optional.isPresent()) {
319 patchTransaction = optional.get().newReadWriteTransaction();
321 // if mount point does not have broker it is not possible to continue and global error is reported
322 LOG.error("Http Patch {} has failed - device {} does not support broker service",
323 patchContext.getPatchId(), mountPoint.getIdentifier());
324 return new PatchStatusContext(
325 patchContext.getPatchId(),
328 ImmutableList.of(new RestconfError(
329 ErrorType.APPLICATION,
330 ErrorTag.OPERATION_FAILED,
331 "DOM data broker service isn't available for mount point "
332 + mountPoint.getIdentifier()))
337 final List<PatchStatusEntity> editCollection = new ArrayList<>();
338 List<RestconfError> editErrors;
339 boolean withoutError = true;
341 for (final PatchEntity patchEntity : patchContext.getData()) {
342 final PatchEditOperation operation = patchEntity.getOperation();
347 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
348 patchEntity.getNode(), schemaContext);
349 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
350 } catch (final RestconfDocumentedException e) {
351 LOG.error("Error call http Patch operation {} on target {}",
353 patchEntity.getTargetNode().toString());
355 editErrors = new ArrayList<>();
356 editErrors.addAll(e.getErrors());
357 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
358 withoutError = false;
365 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
366 .getTargetNode(), patchEntity.getNode(), schemaContext);
367 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
368 } catch (final RestconfDocumentedException e) {
369 LOG.error("Error call http Patch operation {} on target {}",
371 patchEntity.getTargetNode().toString());
373 editErrors = new ArrayList<>();
374 editErrors.addAll(e.getErrors());
375 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
376 withoutError = false;
384 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
386 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
387 } catch (final RestconfDocumentedException e) {
388 LOG.error("Error call http Patch operation {} on target {}",
390 patchEntity.getTargetNode().toString());
392 editErrors = new ArrayList<>();
393 editErrors.addAll(e.getErrors());
394 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
395 withoutError = false;
402 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
403 patchEntity.getNode(), schemaContext);
404 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
405 } catch (final RestconfDocumentedException e) {
406 LOG.error("Error call http Patch operation {} on target {}",
408 patchEntity.getTargetNode().toString());
410 editErrors = new ArrayList<>();
411 editErrors.addAll(e.getErrors());
412 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
413 withoutError = false;
418 LOG.error("Unsupported http Patch operation {} on target {}",
420 patchEntity.getTargetNode().toString());
425 // if errors then cancel transaction and return error status
427 patchTransaction.cancel();
428 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
431 // if no errors commit transaction
432 final CountDownLatch waiter = new CountDownLatch(1);
433 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
434 final PatchStatusContextHelper status = new PatchStatusContextHelper();
436 future.addCallback(new FutureCallback<CommitInfo>() {
438 public void onSuccess(final CommitInfo result) {
439 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
445 public void onFailure(final Throwable throwable) {
446 // if commit failed it is global error
447 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
448 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
449 false, ImmutableList.of(
450 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
453 }, MoreExecutors.directExecutor());
456 return status.getStatus();
459 // POST configuration
460 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
461 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
462 final String insert, final String point) {
463 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
464 globalSchema, insert, point);
467 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
468 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
469 final String insert, final String point) {
470 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
471 if (domDataBrokerService.isPresent()) {
472 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
473 payload, mountPoint.getSchemaContext(), insert, point);
475 throw dataBrokerUnavailable(path);
478 // DELETE configuration
479 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
480 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
483 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
484 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
485 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
486 if (domDataBrokerService.isPresent()) {
487 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
489 throw dataBrokerUnavailable(path);
493 public ListenableFuture<? extends DOMRpcResult> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
494 if (this.rpcService == null) {
495 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
497 LOG.trace("Invoke RPC {} with input: {}", type, input);
498 return this.rpcService.invokeRpc(type, input);
501 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
502 final ListenerAdapter listener) {
503 if (listener.isListening()) {
507 final YangInstanceIdentifier path = listener.getPath();
508 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
509 .getInstance(DOMDataTreeChangeService.class);
510 if (changeService == null) {
511 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
512 + this.domDataBroker);
514 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
515 ListenerRegistration<ListenerAdapter> registration =
516 changeService.registerDataTreeChangeListener(root, listener);
517 listener.setRegistration(registration);
520 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadOperations transaction,
521 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
522 return readDataViaTransaction(transaction, datastore, path, null);
525 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadOperations transaction,
526 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
527 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
530 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).get();
531 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
532 prepareDataByParamWithDef(optional.get(), path, withDefa);
533 } catch (InterruptedException e) {
534 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
535 throw new RestconfDocumentedException("Error reading data.", e);
536 } catch (ExecutionException e) {
537 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
538 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
539 ReadFailedException.class));
543 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
544 final YangInstanceIdentifier path, final String withDefa) {
554 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
557 final SchemaContext ctx = controllerContext.getGlobalSchema();
558 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
559 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
560 if (result instanceof ContainerNode) {
561 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
562 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
563 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
564 return builder.build();
567 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
568 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
569 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
570 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
571 return builder.build();
574 private void buildMapEntryBuilder(
575 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
576 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
577 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
578 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
579 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
580 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
581 if (child instanceof ContainerNode) {
582 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
583 Builders.containerBuilder((ContainerSchemaNode) childSchema);
584 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
585 builder.withChild(childBuilder.build());
586 } else if (child instanceof MapNode) {
587 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
588 Builders.mapBuilder((ListSchemaNode) childSchema);
589 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
590 ((ListSchemaNode) childSchema).getKeyDefinition());
591 builder.withChild(childBuilder.build());
592 } else if (child instanceof LeafNode) {
593 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
594 final Object nodeVal = child.getValue();
595 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
596 Builders.leafBuilder((LeafSchemaNode) childSchema);
597 if (keys.contains(child.getNodeType())) {
598 leafBuilder.withValue(child.getValue());
599 builder.withChild(leafBuilder.build());
602 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
603 leafBuilder.withValue(child.getValue());
604 builder.withChild(leafBuilder.build());
607 if (defaultVal != null && defaultVal.equals(nodeVal)) {
608 leafBuilder.withValue(child.getValue());
609 builder.withChild(leafBuilder.build());
617 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
618 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
619 final List<QName> keys) {
620 for (final MapEntryNode mapEntryNode : result.getValue()) {
621 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
622 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
623 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
624 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
625 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
626 builder.withChild(mapEntryBuilder.build());
630 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
631 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
632 final YangInstanceIdentifier actualPath, final boolean trim) {
633 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
634 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
635 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
636 if (child instanceof ContainerNode) {
637 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
638 Builders.containerBuilder((ContainerSchemaNode) childSchema);
639 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
640 builder.withChild(builderChild.build());
641 } else if (child instanceof MapNode) {
642 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
643 Builders.mapBuilder((ListSchemaNode) childSchema);
644 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
645 ((ListSchemaNode) childSchema).getKeyDefinition());
646 builder.withChild(childBuilder.build());
647 } else if (child instanceof LeafNode) {
648 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
649 final Object nodeVal = child.getValue();
650 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
651 Builders.leafBuilder((LeafSchemaNode) childSchema);
653 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
654 leafBuilder.withValue(child.getValue());
655 builder.withChild(leafBuilder.build());
658 if (defaultVal != null && defaultVal.equals(nodeVal)) {
659 leafBuilder.withValue(child.getValue());
660 builder.withChild(leafBuilder.build());
668 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
670 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
671 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
672 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
673 final String insert, final String point) {
674 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
675 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
676 return rwTransaction.commit();
680 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
682 private void postDataWithinTransaction(
683 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
684 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
685 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
686 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
689 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
690 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
691 final SchemaContext schemaContext, final String insert, final String point) {
692 if (insert == null) {
693 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
697 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
698 checkItemDoesNotExists(rwTransaction, datastore, path);
701 if (schemaNode instanceof ListSchemaNode) {
702 final OrderedMapNode readList =
703 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
704 if (readList == null || readList.getValue().isEmpty()) {
705 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
707 rwTransaction.delete(datastore, path.getParent().getParent());
708 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
709 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
713 final OrderedLeafSetNode<?> readLeafList =
714 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
715 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
716 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
718 rwTransaction.delete(datastore, path.getParent());
719 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
720 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
726 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
729 if (schemaNode instanceof ListSchemaNode) {
730 final OrderedMapNode readList =
731 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
732 if (readList == null || readList.getValue().isEmpty()) {
733 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
735 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
740 final OrderedLeafSetNode<?> readLeafList =
741 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
742 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
743 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
745 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
751 if (schemaNode instanceof ListSchemaNode) {
752 final OrderedMapNode readList =
753 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
754 if (readList == null || readList.getValue().isEmpty()) {
755 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
757 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
762 final OrderedLeafSetNode<?> readLeafList =
763 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
764 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
765 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
767 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
768 readLeafList, false);
773 throw new RestconfDocumentedException(
774 "Used bad value of insert parameter. Possible values are first, last, before or after, "
775 + "but was: " + insert);
779 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
780 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
781 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
782 final boolean before) {
783 rwTransaction.delete(datastore, path.getParent().getParent());
784 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
785 int lastItemPosition = 0;
786 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
787 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
795 int lastInsertedPosition = 0;
796 final NormalizedNode<?, ?> emptySubtree =
797 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
798 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
799 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
800 if (lastInsertedPosition == lastItemPosition) {
801 checkItemDoesNotExists(rwTransaction, datastore, path);
802 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
804 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
805 checkItemDoesNotExists(rwTransaction, datastore, childPath);
806 rwTransaction.put(datastore, childPath, nodeChild);
807 lastInsertedPosition++;
811 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
812 final LogicalDatastoreType datastore,
813 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
814 final String point, final MapNode readList, final boolean before) {
815 rwTransaction.delete(datastore, path.getParent().getParent());
816 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
817 int lastItemPosition = 0;
818 for (final MapEntryNode mapEntryNode : readList.getValue()) {
819 if (mapEntryNode.getIdentifier()
820 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
828 int lastInsertedPosition = 0;
829 final NormalizedNode<?, ?> emptySubtree =
830 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
831 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
832 for (final MapEntryNode mapEntryNode : readList.getValue()) {
833 if (lastInsertedPosition == lastItemPosition) {
834 checkItemDoesNotExists(rwTransaction, datastore, path);
835 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
837 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
838 checkItemDoesNotExists(rwTransaction, datastore, childPath);
839 rwTransaction.put(datastore, childPath, mapEntryNode);
840 lastInsertedPosition++;
844 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
845 final YangInstanceIdentifier parent = path.getParent();
846 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
847 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
849 if (dataSchemaNode instanceof ListSchemaNode) {
850 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
851 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
853 return dataSchemaNode;
855 if (dataSchemaNode instanceof LeafListSchemaNode) {
856 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
857 throw new RestconfDocumentedException(
858 "Insert parameter can be used only with ordered-by user leaf-list.");
860 return dataSchemaNode;
862 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
865 private static void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
866 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
867 final SchemaContext schemaContext) {
868 final Collection<? extends NormalizedNode<?, ?>> children;
869 if (payload instanceof MapNode) {
870 children = ((MapNode) payload).getValue();
871 } else if (payload instanceof LeafSetNode) {
872 children = ((LeafSetNode<?>) payload).getValue();
874 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
878 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
879 if (children.isEmpty()) {
880 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
881 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
885 // Kick off batch existence check first...
886 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
888 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
889 // result of the existence checks...
890 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
891 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
892 for (final NormalizedNode<?, ?> child : children) {
893 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
894 // as that would allow us to skip the existence checks
895 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
898 // ... finally collect existence checks and abort the transaction if any of them failed.
899 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
901 failure = check.getFailure();
902 } catch (InterruptedException e) {
903 rwTransaction.cancel();
904 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
907 if (failure != null) {
908 rwTransaction.cancel();
909 final ReadFailedException e = failure.getValue();
911 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
912 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
915 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
920 private static void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
921 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
922 final SchemaContext schemaContext) {
923 checkItemDoesNotExists(rwTransaction, datastore, path);
924 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
925 rwTransaction.put(datastore, path, payload);
928 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
929 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
931 return rwTransaction.exists(store, path).get();
932 } catch (InterruptedException e) {
933 rwTransaction.cancel();
934 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
935 } catch (ExecutionException e) {
936 rwTransaction.cancel();
937 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
938 Throwables.getCauseAs(e, ReadFailedException.class));
943 * Check if item already exists. Throws error if it does NOT already exist.
944 * @param rwTransaction Current transaction
945 * @param store Used datastore
946 * @param path Path to item to verify its existence
948 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
949 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
950 if (!doesItemExist(rwTransaction, store, path)) {
951 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
952 rwTransaction.cancel();
953 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
954 ErrorTag.DATA_MISSING);
959 * Check if item does NOT already exist. Throws error if it already exists.
960 * @param rwTransaction Current transaction
961 * @param store Used datastore
962 * @param path Path to item to verify its existence
964 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
965 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
966 if (doesItemExist(rwTransaction, store, path)) {
967 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
968 rwTransaction.cancel();
969 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
970 ErrorTag.DATA_EXISTS);
975 * PUT data and submit {@link DOMDataReadWriteTransaction}.
982 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
983 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
984 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
985 final String insert, final String point) {
986 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
987 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
988 return readWriteTransaction.commit();
992 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
994 private void putDataWithinTransaction(
995 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
996 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
997 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
998 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1001 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1002 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1003 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1004 final String insert, final String point) {
1005 if (insert == null) {
1006 makePut(rwTransaction, datastore, path, payload, schemaContext);
1010 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1011 checkItemDoesNotExists(rwTransaction, datastore, path);
1014 if (schemaNode instanceof ListSchemaNode) {
1015 final OrderedMapNode readList =
1016 (OrderedMapNode) this.readConfigurationData(path.getParent());
1017 if (readList == null || readList.getValue().isEmpty()) {
1018 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1020 rwTransaction.delete(datastore, path.getParent());
1021 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1022 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1025 final OrderedLeafSetNode<?> readLeafList =
1026 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1027 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1028 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1030 rwTransaction.delete(datastore, path.getParent());
1031 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1032 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1038 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1041 if (schemaNode instanceof ListSchemaNode) {
1042 final OrderedMapNode readList =
1043 (OrderedMapNode) this.readConfigurationData(path.getParent());
1044 if (readList == null || readList.getValue().isEmpty()) {
1045 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1047 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1051 final OrderedLeafSetNode<?> readLeafList =
1052 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1053 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1054 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1056 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1057 readLeafList, true);
1062 if (schemaNode instanceof ListSchemaNode) {
1063 final OrderedMapNode readList =
1064 (OrderedMapNode) this.readConfigurationData(path.getParent());
1065 if (readList == null || readList.getValue().isEmpty()) {
1066 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1068 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1072 final OrderedLeafSetNode<?> readLeafList =
1073 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1074 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1075 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1077 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1078 readLeafList, false);
1083 throw new RestconfDocumentedException(
1084 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1089 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1090 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1091 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1092 final boolean before) {
1093 tx.delete(datastore, path.getParent());
1094 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1096 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1097 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1106 final NormalizedNode<?, ?> emptySubtree =
1107 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1108 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1109 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1110 if (index2 == index1) {
1111 simplePut(datastore, path, tx, schemaContext, payload);
1113 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1114 tx.put(datastore, childPath, nodeChild);
1119 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1120 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1121 final String point, final OrderedMapNode readList, final boolean before) {
1122 tx.delete(datastore, path.getParent());
1123 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1125 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1126 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1135 final NormalizedNode<?, ?> emptySubtree =
1136 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1137 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1138 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1139 if (index2 == index1) {
1140 simplePut(datastore, path, tx, schemaContext, payload);
1142 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1143 tx.put(datastore, childPath, mapEntryNode);
1148 private static void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1149 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1150 if (payload instanceof MapNode) {
1151 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1152 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1153 ensureParentsByMerge(datastore, path, tx, schemaContext);
1154 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1155 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1156 tx.put(datastore, childPath, child);
1159 simplePut(datastore, path, tx, schemaContext, payload);
1163 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1164 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext,
1165 final NormalizedNode<?, ?> payload) {
1166 ensureParentsByMerge(datastore, path, tx, schemaContext);
1167 tx.put(datastore, path, payload);
1170 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1171 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1172 final YangInstanceIdentifier path) {
1173 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1174 checkItemExists(readWriteTransaction, datastore, path);
1175 readWriteTransaction.delete(datastore, path);
1176 return readWriteTransaction.commit();
1179 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1180 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1181 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1182 tx.delete(datastore, path);
1185 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1186 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1187 final SchemaContext schemaContext) {
1188 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1189 ensureParentsByMerge(datastore, path, tx, schemaContext);
1191 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1192 // OpenDaylight should not change it.
1193 tx.merge(datastore, path, payload);
1196 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1197 if (listener.isListening()) {
1201 final SchemaPath path = listener.getSchemaPath();
1202 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1203 .registerNotificationListener(listener, path);
1205 listener.setRegistration(registration);
1208 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1209 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1210 final SchemaContext schemaContext) {
1211 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1212 YangInstanceIdentifier rootNormalizedPath = null;
1214 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1216 while (it.hasNext()) {
1217 final PathArgument pathArgument = it.next();
1218 if (rootNormalizedPath == null) {
1219 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1223 normalizedPathWithoutChildArgs.add(pathArgument);
1227 if (normalizedPathWithoutChildArgs.isEmpty()) {
1231 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1233 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1234 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1235 tx.merge(store, rootNormalizedPath, parentStructure);
1238 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1239 LOG.warn("DOM data broker service is not available for mount point {}", path);
1240 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1243 private static final class PatchStatusContextHelper {
1244 PatchStatusContext status;
1246 public PatchStatusContext getStatus() {
1250 public void setStatus(final PatchStatusContext status) {
1251 this.status = status;