Bump MRI upstreams
[netconf.git] / restconf / restconf-nb-bierman02 / src / main / java / org / opendaylight / netconf / sal / restconf / impl / BrokerFacade.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netconf.sal.restconf.impl;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
13 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
14
15 import com.google.common.base.Throwables;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.util.concurrent.FluentFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import java.io.Closeable;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map.Entry;
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.eclipse.jdt.annotation.NonNull;
36 import org.opendaylight.mdsal.common.api.CommitInfo;
37 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
38 import org.opendaylight.mdsal.common.api.ReadFailedException;
39 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
44 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
45 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
46 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
47 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
48 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
49 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
50 import org.opendaylight.mdsal.dom.api.DOMRpcService;
51 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
52 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
53 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
54 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
55 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
56 import org.opendaylight.restconf.common.errors.RestconfError;
57 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
58 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
59 import org.opendaylight.restconf.common.patch.PatchContext;
60 import org.opendaylight.restconf.common.patch.PatchEditOperation;
61 import org.opendaylight.restconf.common.patch.PatchEntity;
62 import org.opendaylight.restconf.common.patch.PatchStatusContext;
63 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
64 import org.opendaylight.restconf.common.util.DataChangeScope;
65 import org.opendaylight.yangtools.concepts.ListenerRegistration;
66 import org.opendaylight.yangtools.yang.common.QName;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
71 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
73 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
82 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
83 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
84 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
85 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
86 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders;
87 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
88 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
89 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
91 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
92 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
93 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
94 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
95 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
96 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
97 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
98 import org.slf4j.Logger;
99 import org.slf4j.LoggerFactory;
100
101 @Singleton
102 public class BrokerFacade implements Closeable {
103     private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
104
105     private final ThreadLocal<Boolean> isMounted = new ThreadLocal<>();
106     private final DOMNotificationService domNotification;
107     private final ControllerContext controllerContext;
108     private final DOMDataBroker domDataBroker;
109
110     private volatile DOMRpcService rpcService;
111
112     @Inject
113     public BrokerFacade(@Reference final DOMRpcService rpcService, @Reference final DOMDataBroker domDataBroker,
114             @Reference final DOMNotificationService domNotification, final ControllerContext controllerContext) {
115         this.rpcService = requireNonNull(rpcService);
116         this.domDataBroker = requireNonNull(domDataBroker);
117         this.domNotification = requireNonNull(domNotification);
118         this.controllerContext = requireNonNull(controllerContext);
119     }
120
121     /**
122      * Factory method.
123      *
124      * @deprecated Just use
125      *             {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
126      *             constructor instead.
127      */
128     @Deprecated
129     public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
130             final DOMNotificationService domNotification, final ControllerContext controllerContext) {
131         return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
132     }
133
134     @Override
135     @PreDestroy
136     public void close() {
137     }
138
139     /**
140      * Read config data by path.
141      *
142      * @param path
143      *            path of data
144      * @return read date
145      */
146     public NormalizedNode readConfigurationData(final YangInstanceIdentifier path) {
147         return readConfigurationData(path, null);
148     }
149
150     /**
151      * Read config data by path.
152      *
153      * @param path
154      *            path of data
155      * @param withDefa
156      *            value of with-defaults parameter
157      * @return read date
158      */
159     public NormalizedNode readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
160         try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
161             return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
162         }
163     }
164
165     /**
166      * Read config data from mount point by path.
167      *
168      * @param mountPoint
169      *            mount point for reading data
170      * @param path
171      *            path of data
172      * @return read data
173      */
174     public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
175         return readConfigurationData(mountPoint, path, null);
176     }
177
178     /**
179      * Read config data from mount point by path.
180      *
181      * @param mountPoint
182      *            mount point for reading data
183      * @param path
184      *            path of data
185      * @param withDefa
186      *            value of with-defaults parameter
187      * @return read data
188      */
189     public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
190             final String withDefa) {
191         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
192         if (domDataBrokerService.isPresent()) {
193             try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
194                 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
195             }
196         }
197         throw dataBrokerUnavailable(path);
198     }
199
200     /**
201      * Read operational data by path.
202      *
203      * @param path
204      *            path of data
205      * @return read data
206      */
207     public NormalizedNode readOperationalData(final YangInstanceIdentifier path) {
208         try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
209             return readDataViaTransaction(tx, OPERATIONAL, path);
210         }
211     }
212
213     /**
214      * Read operational data from mount point by path.
215      *
216      * @param mountPoint
217      *            mount point for reading data
218      * @param path
219      *            path of data
220      * @return read data
221      */
222     public NormalizedNode readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
223         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
224         if (domDataBrokerService.isPresent()) {
225             try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
226                 return readDataViaTransaction(tx, OPERATIONAL, path);
227             }
228         }
229         throw dataBrokerUnavailable(path);
230     }
231
232     /**
233      * <b>PUT configuration data</b>
234      *
235      * <p>
236      * Prepare result(status) for PUT operation and PUT data via transaction.
237      * Return wrapped status and future from PUT.
238      *
239      * @param globalSchema
240      *            used by merge parents (if contains list)
241      * @param path
242      *            path of node
243      * @param payload
244      *            input data
245      * @param point
246      *            point
247      * @param insert
248      *            insert
249      * @return wrapper of status and future of PUT
250      */
251     public PutResult commitConfigurationDataPut(final EffectiveModelContext globalSchema,
252             final YangInstanceIdentifier path, final NormalizedNode payload, final String insert, final String point) {
253         requireNonNull(globalSchema);
254         requireNonNull(path);
255         requireNonNull(payload);
256
257         isMounted.set(false);
258         final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
259         final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
260                 : Status.CREATED;
261         final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
262                 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
263         isMounted.remove();
264         return new PutResult(status, future);
265     }
266
267     /**
268      * <b>PUT configuration data (Mount point)</b>
269      *
270      * <p>
271      * Prepare result(status) for PUT operation and PUT data via transaction.
272      * Return wrapped status and future from PUT.
273      *
274      * @param mountPoint
275      *            mount point for getting transaction for operation and schema
276      *            context for merging parents(if contains list)
277      * @param path
278      *            path of node
279      * @param payload
280      *            input data
281      * @param point
282      *            point
283      * @param insert
284      *            insert
285      * @return wrapper of status and future of PUT
286      */
287     public PutResult commitMountPointDataPut(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
288             final NormalizedNode payload, final String insert, final String point) {
289         requireNonNull(mountPoint);
290         requireNonNull(path);
291         requireNonNull(payload);
292
293         isMounted.set(true);
294         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
295         if (domDataBrokerService.isPresent()) {
296             final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
297                     domDataBrokerService.get().newReadWriteTransaction();
298             final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
299                     ? Status.OK : Status.CREATED;
300             final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
301                     newReadWriteTransaction, CONFIGURATION, path, payload, modelContext(mountPoint), insert, point);
302             isMounted.remove();
303             return new PutResult(status, future);
304         }
305         isMounted.remove();
306         throw dataBrokerUnavailable(path);
307     }
308
309     public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
310             throws Exception {
311         final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
312
313         // get new transaction and schema context on server or on mounted device
314         final EffectiveModelContext schemaContext;
315         final DOMDataTreeReadWriteTransaction patchTransaction;
316         if (mountPoint == null) {
317             schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
318             patchTransaction = this.domDataBroker.newReadWriteTransaction();
319         } else {
320             schemaContext = modelContext(mountPoint);
321
322             final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
323
324             if (optional.isPresent()) {
325                 patchTransaction = optional.get().newReadWriteTransaction();
326             } else {
327                 // if mount point does not have broker it is not possible to continue and global error is reported
328                 LOG.error("Http Patch {} has failed - device {} does not support broker service",
329                         patchContext.getPatchId(), mountPoint.getIdentifier());
330                 return new PatchStatusContext(
331                         patchContext.getPatchId(),
332                         null,
333                         false,
334                         ImmutableList.of(new RestconfError(
335                                 ErrorType.APPLICATION,
336                                 ErrorTag.OPERATION_FAILED,
337                                 "DOM data broker service isn't available for mount point "
338                                         + mountPoint.getIdentifier()))
339                 );
340             }
341         }
342
343         final List<PatchStatusEntity> editCollection = new ArrayList<>();
344         List<RestconfError> editErrors;
345         boolean withoutError = true;
346
347         for (final PatchEntity patchEntity : patchContext.getData()) {
348             final PatchEditOperation operation = patchEntity.getOperation();
349             switch (operation) {
350                 case CREATE:
351                     if (withoutError) {
352                         try {
353                             postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
354                                     patchEntity.getNode(), schemaContext);
355                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
356                         } catch (final RestconfDocumentedException e) {
357                             LOG.error("Error call http Patch operation {} on target {}",
358                                     operation,
359                                     patchEntity.getTargetNode().toString());
360
361                             editErrors = new ArrayList<>();
362                             editErrors.addAll(e.getErrors());
363                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
364                             withoutError = false;
365                         }
366                     }
367                     break;
368                 case REPLACE:
369                     if (withoutError) {
370                         try {
371                             putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
372                                     .getTargetNode(), patchEntity.getNode(), schemaContext);
373                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
374                         } catch (final RestconfDocumentedException e) {
375                             LOG.error("Error call http Patch operation {} on target {}",
376                                     operation,
377                                     patchEntity.getTargetNode().toString());
378
379                             editErrors = new ArrayList<>();
380                             editErrors.addAll(e.getErrors());
381                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
382                             withoutError = false;
383                         }
384                     }
385                     break;
386                 case DELETE:
387                 case REMOVE:
388                     if (withoutError) {
389                         try {
390                             deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
391                                     .getTargetNode());
392                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
393                         } catch (final RestconfDocumentedException e) {
394                             LOG.error("Error call http Patch operation {} on target {}",
395                                     operation,
396                                     patchEntity.getTargetNode().toString());
397
398                             editErrors = new ArrayList<>();
399                             editErrors.addAll(e.getErrors());
400                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
401                             withoutError = false;
402                         }
403                     }
404                     break;
405                 case MERGE:
406                     if (withoutError) {
407                         try {
408                             mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
409                                     patchEntity.getNode(), schemaContext);
410                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
411                         } catch (final RestconfDocumentedException e) {
412                             LOG.error("Error call http Patch operation {} on target {}",
413                                     operation,
414                                     patchEntity.getTargetNode().toString());
415
416                             editErrors = new ArrayList<>();
417                             editErrors.addAll(e.getErrors());
418                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
419                             withoutError = false;
420                         }
421                     }
422                     break;
423                 default:
424                     LOG.error("Unsupported http Patch operation {} on target {}",
425                             operation,
426                             patchEntity.getTargetNode().toString());
427                     break;
428             }
429         }
430
431         // if errors then cancel transaction and return error status
432         if (!withoutError) {
433             patchTransaction.cancel();
434             return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
435         }
436
437         // if no errors commit transaction
438         final CountDownLatch waiter = new CountDownLatch(1);
439         final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
440         final PatchStatusContextHelper status = new PatchStatusContextHelper();
441
442         future.addCallback(new FutureCallback<CommitInfo>() {
443             @Override
444             public void onSuccess(final CommitInfo result) {
445                 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
446                         true, null));
447                 waiter.countDown();
448             }
449
450             @Override
451             public void onFailure(final Throwable throwable) {
452                 // if commit failed it is global error
453                 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
454                 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
455                     false, ImmutableList.of(
456                         new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
457                 waiter.countDown();
458             }
459         }, MoreExecutors.directExecutor());
460
461         waiter.await();
462         return status.getStatus();
463     }
464
465     // POST configuration
466     public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
467             final EffectiveModelContext globalSchema, final YangInstanceIdentifier path,
468             final NormalizedNode payload, final String insert, final String point) {
469         isMounted.set(false);
470         FluentFuture<? extends CommitInfo> future =
471                 postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
472                                        globalSchema, insert, point);
473         isMounted.remove();
474         return future;
475     }
476
477     public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
478             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload,
479             final String insert, final String point) {
480         isMounted.set(true);
481         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
482         if (domDataBrokerService.isPresent()) {
483             FluentFuture<? extends CommitInfo> future =
484                     postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
485                                            payload, modelContext(mountPoint), insert, point);
486             isMounted.remove();
487             return future;
488         }
489         isMounted.remove();
490         throw dataBrokerUnavailable(path);
491     }
492
493     // DELETE configuration
494     public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
495         return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
496     }
497
498     public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
499             final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
500         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
501         if (domDataBrokerService.isPresent()) {
502             return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
503         }
504         throw dataBrokerUnavailable(path);
505     }
506
507     // RPC
508     public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull QName type,
509             final @NonNull NormalizedNode input) {
510         if (this.rpcService == null) {
511             throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
512         }
513         LOG.trace("Invoke RPC {} with input: {}", type, input);
514         return this.rpcService.invokeRpc(type, input);
515     }
516
517     public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
518             final ListenerAdapter listener) {
519         if (listener.isListening()) {
520             return;
521         }
522
523         final YangInstanceIdentifier path = listener.getPath();
524         DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
525                 .getInstance(DOMDataTreeChangeService.class);
526         if (changeService == null) {
527             throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
528                                                         + this.domDataBroker);
529         }
530         DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
531         ListenerRegistration<ListenerAdapter> registration =
532                                     changeService.registerDataTreeChangeListener(root, listener);
533         listener.setRegistration(registration);
534     }
535
536     private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
537             final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
538         return readDataViaTransaction(transaction, datastore, path, null);
539     }
540
541     private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
542             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
543         LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
544
545         try {
546             final Optional<NormalizedNode> optional = transaction.read(datastore, path).get();
547             return optional.map(normalizedNode -> withDefa == null ? normalizedNode :
548                 prepareDataByParamWithDef(normalizedNode, path, withDefa)).orElse(null);
549         } catch (InterruptedException e) {
550             LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
551             throw new RestconfDocumentedException("Error reading data.", e);
552         } catch (ExecutionException e) {
553             LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
554             throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
555                 ReadFailedException.class));
556         }
557     }
558
559     private NormalizedNode prepareDataByParamWithDef(final NormalizedNode result,
560             final YangInstanceIdentifier path, final String withDefa) {
561         boolean trim;
562         switch (withDefa) {
563             case "trim":
564                 trim = true;
565                 break;
566             case "explicit":
567                 trim = false;
568                 break;
569             default:
570                 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
571         }
572
573         final EffectiveModelContext ctx = controllerContext.getGlobalSchema();
574         final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
575         final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
576         if (result instanceof ContainerNode) {
577             final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
578                 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
579             buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
580             return builder.build();
581         }
582
583         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
584             SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
585         buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
586             ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
587         return builder.build();
588     }
589
590     private void buildMapEntryBuilder(
591             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
592             final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
593             final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
594         for (final DataContainerChild child : result.body()) {
595             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
596             final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
597             if (child instanceof ContainerNode) {
598                 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
599                         SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
600                 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
601                 builder.withChild(childBuilder.build());
602             } else if (child instanceof MapNode) {
603                 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
604                         SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
605                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
606                         ((ListSchemaNode) childSchema).getKeyDefinition());
607                 builder.withChild(childBuilder.build());
608             } else if (child instanceof LeafNode) {
609                 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
610                 final Object nodeVal = child.body();
611                 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
612                         SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
613                 if (keys.contains(child.getIdentifier().getNodeType())) {
614                     leafBuilder.withValue(child.body());
615                     builder.withChild(leafBuilder.build());
616                 } else {
617                     if (trim) {
618                         if (defaultVal == null || !defaultVal.equals(nodeVal)) {
619                             leafBuilder.withValue(child.body());
620                             builder.withChild(leafBuilder.build());
621                         }
622                     } else {
623                         if (defaultVal != null && defaultVal.equals(nodeVal)) {
624                             leafBuilder.withValue(child.body());
625                             builder.withChild(leafBuilder.build());
626                         }
627                     }
628                 }
629             }
630         }
631     }
632
633     private void buildList(final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder, final MapNode result,
634             final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
635             final List<QName> keys) {
636         for (final MapEntryNode mapEntryNode : result.body()) {
637             final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
638             final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow()
639                     .getDataSchemaNode();
640             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
641                     SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) childSchema);
642             buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
643             builder.withChild(mapEntryBuilder.build());
644         }
645     }
646
647     private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
648             final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
649             final YangInstanceIdentifier actualPath, final boolean trim) {
650         for (final DataContainerChild child : result.body()) {
651             final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
652             final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
653             if (child instanceof ContainerNode) {
654                 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
655                         SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
656                 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
657                 builder.withChild(builderChild.build());
658             } else if (child instanceof MapNode) {
659                 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
660                         SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
661                 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
662                         ((ListSchemaNode) childSchema).getKeyDefinition());
663                 builder.withChild(childBuilder.build());
664             } else if (child instanceof LeafNode) {
665                 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
666                 final Object nodeVal = child.body();
667                 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
668                         SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
669                 if (trim) {
670                     if (defaultVal == null || !defaultVal.equals(nodeVal)) {
671                         leafBuilder.withValue(child.body());
672                         builder.withChild(leafBuilder.build());
673                     }
674                 } else {
675                     if (defaultVal != null && defaultVal.equals(nodeVal)) {
676                         leafBuilder.withValue(child.body());
677                         builder.withChild(leafBuilder.build());
678                     }
679                 }
680             }
681         }
682     }
683
684     /**
685      * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
686      */
687     private FluentFuture<? extends CommitInfo> postDataViaTransaction(
688             final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
689             final YangInstanceIdentifier path, final NormalizedNode payload,
690             final EffectiveModelContext schemaContext, final String insert, final String point) {
691         LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
692         postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
693         return rwTransaction.commit();
694     }
695
696     /**
697      * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
698      */
699     private void postDataWithinTransaction(
700             final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
701             final YangInstanceIdentifier path, final NormalizedNode payload,
702             final EffectiveModelContext schemaContext) {
703         LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
704         postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
705     }
706
707     private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
708                           final YangInstanceIdentifier path, final NormalizedNode payload,
709                           final EffectiveModelContext schemaContext, final String insert, final String point) {
710         if (insert == null) {
711             makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
712             return;
713         }
714
715         final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
716         checkItemDoesNotExists(rwTransaction, datastore, path);
717         switch (insert) {
718             case "first":
719                 if (schemaNode instanceof ListSchemaNode) {
720                     final UserMapNode readList =
721                             (UserMapNode) this.readConfigurationData(path.getParent().getParent());
722                     if (readList == null || readList.isEmpty()) {
723                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
724                     } else {
725                         rwTransaction.delete(datastore, path.getParent().getParent());
726                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
727                         makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
728                             schemaContext);
729                     }
730                 } else {
731                     final UserLeafSetNode<?> readLeafList =
732                             (UserLeafSetNode<?>) readConfigurationData(path.getParent());
733                     if (readLeafList == null || readLeafList.isEmpty()) {
734                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
735                     } else {
736                         rwTransaction.delete(datastore, path.getParent());
737                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
738                         makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
739                             schemaContext);
740                     }
741                 }
742                 break;
743             case "last":
744                 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
745                 break;
746             case "before":
747                 if (schemaNode instanceof ListSchemaNode) {
748                     final UserMapNode readList =
749                             (UserMapNode) this.readConfigurationData(path.getParent().getParent());
750                     if (readList == null || readList.isEmpty()) {
751                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
752                     } else {
753                         insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
754                             readList,
755                             true);
756                     }
757                 } else {
758                     final UserLeafSetNode<?> readLeafList =
759                             (UserLeafSetNode<?>) readConfigurationData(path.getParent());
760                     if (readLeafList == null || readLeafList.isEmpty()) {
761                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
762                     } else {
763                         insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
764                             readLeafList, true);
765                     }
766                 }
767                 break;
768             case "after":
769                 if (schemaNode instanceof ListSchemaNode) {
770                     final UserMapNode readList =
771                             (UserMapNode) this.readConfigurationData(path.getParent().getParent());
772                     if (readList == null || readList.isEmpty()) {
773                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
774                     } else {
775                         insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
776                             readList,
777                             false);
778                     }
779                 } else {
780                     final UserLeafSetNode<?> readLeafList =
781                             (UserLeafSetNode<?>) readConfigurationData(path.getParent());
782                     if (readLeafList == null || readLeafList.isEmpty()) {
783                         simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
784                     } else {
785                         insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
786                             readLeafList, false);
787                     }
788                 }
789                 break;
790             default:
791                 throw new RestconfDocumentedException(
792                     "Used bad value of insert parameter. Possible values are first, last, before or after, "
793                             + "but was: " + insert);
794         }
795     }
796
797     private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
798             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
799             final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
800             final boolean before) {
801         rwTransaction.delete(datastore, path.getParent().getParent());
802         final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
803         int lastItemPosition = 0;
804         for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
805             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
806                 break;
807             }
808             lastItemPosition++;
809         }
810         if (!before) {
811             lastItemPosition++;
812         }
813         int lastInsertedPosition = 0;
814         final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
815         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
816         for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
817             if (lastInsertedPosition == lastItemPosition) {
818                 checkItemDoesNotExists(rwTransaction, datastore, path);
819                 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
820             }
821             final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
822             checkItemDoesNotExists(rwTransaction, datastore, childPath);
823             rwTransaction.put(datastore, childPath, nodeChild);
824             lastInsertedPosition++;
825         }
826     }
827
828     private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
829             final LogicalDatastoreType datastore,
830             final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
831             final String point, final MapNode readList, final boolean before) {
832         rwTransaction.delete(datastore, path.getParent().getParent());
833         final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
834         int lastItemPosition = 0;
835         for (final MapEntryNode mapEntryNode : readList.body()) {
836             if (mapEntryNode.getIdentifier()
837                     .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
838                 break;
839             }
840             lastItemPosition++;
841         }
842         if (!before) {
843             lastItemPosition++;
844         }
845         int lastInsertedPosition = 0;
846         final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
847         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
848         for (final MapEntryNode mapEntryNode : readList.body()) {
849             if (lastInsertedPosition == lastItemPosition) {
850                 checkItemDoesNotExists(rwTransaction, datastore, path);
851                 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
852             }
853             final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
854             checkItemDoesNotExists(rwTransaction, datastore, childPath);
855             rwTransaction.put(datastore, childPath, mapEntryNode);
856             lastInsertedPosition++;
857         }
858     }
859
860     private static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
861             final YangInstanceIdentifier path) {
862         final YangInstanceIdentifier parent = path.getParent();
863         final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow();
864         final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
865
866         if (dataSchemaNode instanceof ListSchemaNode) {
867             if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
868                 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
869             }
870             return dataSchemaNode;
871         }
872         if (dataSchemaNode instanceof LeafListSchemaNode) {
873             if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
874                 throw new RestconfDocumentedException(
875                         "Insert parameter can be used only with ordered-by user leaf-list.");
876             }
877             return dataSchemaNode;
878         }
879         throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
880     }
881
882     private void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
883             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
884             final SchemaContext schemaContext) {
885         final Collection<? extends NormalizedNode> children;
886         if (payload instanceof MapNode) {
887             children = ((MapNode) payload).body();
888         } else if (payload instanceof LeafSetNode) {
889             children = ((LeafSetNode<?>) payload).body();
890         } else {
891             simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
892             return;
893         }
894
895         final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
896         if (children.isEmpty()) {
897             if (isMounted != null && !isMounted.get()) {
898
899                 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
900                                     emptySubtree);
901                 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
902             }
903             return;
904         }
905
906         // Kick off batch existence check first...
907         final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
908
909         // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
910         // result of the existence checks...
911         if (isMounted != null && !isMounted.get()) {
912
913             rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
914             ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
915         }
916         for (final NormalizedNode child : children) {
917             // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
918             //        as that would allow us to skip the existence checks
919             rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
920         }
921
922         // ... finally collect existence checks and abort the transaction if any of them failed.
923         final Entry<YangInstanceIdentifier, ReadFailedException> failure;
924         try {
925             failure = check.getFailure();
926         } catch (InterruptedException e) {
927             rwTransaction.cancel();
928             throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
929         }
930
931         if (failure != null) {
932             rwTransaction.cancel();
933             final ReadFailedException e = failure.getValue();
934             if (e == null) {
935                 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
936                     ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
937             }
938
939             throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
940                 e.getErrorList());
941         }
942     }
943
944     private void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
945             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
946             final SchemaContext schemaContext) {
947         checkItemDoesNotExists(rwTransaction, datastore, path);
948         if (isMounted != null && !isMounted.get()) {
949             ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
950         }
951         rwTransaction.put(datastore, path, payload);
952     }
953
954     private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
955             final LogicalDatastoreType store, final YangInstanceIdentifier path) {
956         try {
957             return rwTransaction.exists(store, path).get();
958         } catch (InterruptedException e) {
959             rwTransaction.cancel();
960             throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
961         } catch (ExecutionException e) {
962             rwTransaction.cancel();
963             throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
964                 Throwables.getCauseAs(e, ReadFailedException.class));
965         }
966     }
967
968     /**
969      * Check if item already exists. Throws error if it does NOT already exist.
970      * @param rwTransaction Current transaction
971      * @param store Used datastore
972      * @param path Path to item to verify its existence
973      */
974     private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
975             final LogicalDatastoreType store, final YangInstanceIdentifier path) {
976         if (!doesItemExist(rwTransaction, store, path)) {
977             LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
978             rwTransaction.cancel();
979             throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
980                     ErrorTag.DATA_MISSING);
981         }
982     }
983
984     /**
985      * Check if item does NOT already exist. Throws error if it already exists.
986      * @param rwTransaction Current transaction
987      * @param store Used datastore
988      * @param path Path to item to verify its existence
989      */
990     private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
991             final LogicalDatastoreType store, final YangInstanceIdentifier path) {
992         if (doesItemExist(rwTransaction, store, path)) {
993             LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
994             rwTransaction.cancel();
995             throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
996                     ErrorTag.DATA_EXISTS);
997         }
998     }
999
1000     /**
1001      * PUT data and submit {@link DOMDataReadWriteTransaction}.
1002      *
1003      * @param point
1004      *            point
1005      * @param insert
1006      *            insert
1007      */
1008     private FluentFuture<? extends CommitInfo> putDataViaTransaction(
1009             final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1010             final YangInstanceIdentifier path, final NormalizedNode payload,
1011             final EffectiveModelContext schemaContext, final String insert, final String point) {
1012         LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1013         putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1014         return readWriteTransaction.commit();
1015     }
1016
1017     /**
1018      * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1019      */
1020     private void putDataWithinTransaction(
1021             final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1022             final YangInstanceIdentifier path, final NormalizedNode payload,
1023             final EffectiveModelContext schemaContext) {
1024         LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1025         putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1026     }
1027
1028     // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1029     private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1030             final YangInstanceIdentifier path, final NormalizedNode payload,
1031             final EffectiveModelContext schemaContext, final String insert, final String point) {
1032         if (insert == null) {
1033             makePut(rwTransaction, datastore, path, payload, schemaContext);
1034             return;
1035         }
1036
1037         final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1038         checkItemDoesNotExists(rwTransaction, datastore, path);
1039         switch (insert) {
1040             case "first":
1041                 if (schemaNode instanceof ListSchemaNode) {
1042                     final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1043                     if (readList == null || readList.isEmpty()) {
1044                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1045                     } else {
1046                         rwTransaction.delete(datastore, path.getParent());
1047                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1048                         makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1049                     }
1050                 } else {
1051                     final UserLeafSetNode<?> readLeafList =
1052                             (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1053                     if (readLeafList == null || readLeafList.isEmpty()) {
1054                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1055                     } else {
1056                         rwTransaction.delete(datastore, path.getParent());
1057                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1058                         makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1059                             schemaContext);
1060                     }
1061                 }
1062                 break;
1063             case "last":
1064                 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1065                 break;
1066             case "before":
1067                 if (schemaNode instanceof ListSchemaNode) {
1068                     final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1069                     if (readList == null || readList.isEmpty()) {
1070                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1071                     } else {
1072                         insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1073                             readList, true);
1074                     }
1075                 } else {
1076                     final UserLeafSetNode<?> readLeafList =
1077                             (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1078                     if (readLeafList == null || readLeafList.isEmpty()) {
1079                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1080                     } else {
1081                         insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1082                             readLeafList, true);
1083                     }
1084                 }
1085                 break;
1086             case "after":
1087                 if (schemaNode instanceof ListSchemaNode) {
1088                     final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1089                     if (readList == null || readList.isEmpty()) {
1090                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1091                     } else {
1092                         insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1093                             readList, false);
1094                     }
1095                 } else {
1096                     final UserLeafSetNode<?> readLeafList =
1097                             (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1098                     if (readLeafList == null || readLeafList.isEmpty()) {
1099                         simplePut(datastore, path, rwTransaction, schemaContext, payload);
1100                     } else {
1101                         insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1102                             readLeafList, false);
1103                     }
1104                 }
1105                 break;
1106             default:
1107                 throw new RestconfDocumentedException(
1108                     "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1109                             + insert);
1110         }
1111     }
1112
1113     private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1114             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1115             final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
1116             final boolean before) {
1117         tx.delete(datastore, path.getParent());
1118         final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1119         int index1 = 0;
1120         for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1121             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1122                 break;
1123             }
1124             index1++;
1125         }
1126         if (!before) {
1127             index1++;
1128         }
1129         int index2 = 0;
1130         final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1131         tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1132         for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1133             if (index2 == index1) {
1134                 simplePut(datastore, path, tx, schemaContext, payload);
1135             }
1136             final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1137             tx.put(datastore, childPath, nodeChild);
1138             index2++;
1139         }
1140     }
1141
1142     private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1143             final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
1144             final String point, final UserMapNode readList, final boolean before) {
1145         tx.delete(datastore, path.getParent());
1146         final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1147         int index1 = 0;
1148         for (final MapEntryNode mapEntryNode : readList.body()) {
1149             if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1150                 break;
1151             }
1152             index1++;
1153         }
1154         if (!before) {
1155             index1++;
1156         }
1157         int index2 = 0;
1158         final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1159         tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1160         for (final MapEntryNode mapEntryNode : readList.body()) {
1161             if (index2 == index1) {
1162                 simplePut(datastore, path, tx, schemaContext, payload);
1163             }
1164             final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1165             tx.put(datastore, childPath, mapEntryNode);
1166             index2++;
1167         }
1168     }
1169
1170     private void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1171             final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext) {
1172         if (payload instanceof MapNode) {
1173             final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1174             if (isMounted != null && !isMounted.get()) {
1175                 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1176                 ensureParentsByMerge(datastore, path, tx, schemaContext);
1177             }
1178             for (final MapEntryNode child : ((MapNode) payload).body()) {
1179                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1180                 tx.put(datastore, childPath, child);
1181             }
1182         } else {
1183             simplePut(datastore, path, tx, schemaContext, payload);
1184         }
1185     }
1186
1187     private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1188             final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode payload) {
1189         if (isMounted != null && !isMounted.get()) {
1190             ensureParentsByMerge(datastore, path, tx, schemaContext);
1191         }
1192         tx.put(datastore, path, payload);
1193     }
1194
1195     private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1196             final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1197             final YangInstanceIdentifier path) {
1198         LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1199         checkItemExists(readWriteTransaction, datastore, path);
1200         readWriteTransaction.delete(datastore, path);
1201         return readWriteTransaction.commit();
1202     }
1203
1204     private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1205             final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1206         LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1207         tx.delete(datastore, path);
1208     }
1209
1210     private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1211             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1212             final SchemaContext schemaContext) {
1213         LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1214         ensureParentsByMerge(datastore, path, tx, schemaContext);
1215
1216         // Since YANG Patch provides the option to specify what kind of operation for each edit,
1217         // OpenDaylight should not change it.
1218         tx.merge(datastore, path, payload);
1219     }
1220
1221     public void registerToListenNotification(final NotificationListenerAdapter listener) {
1222         if (listener.isListening()) {
1223             return;
1224         }
1225
1226         final SchemaPath path = listener.getSchemaPath();
1227         final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1228                 .registerNotificationListener(listener, Absolute.of(ImmutableList.copyOf(path.getPathFromRoot())));
1229
1230         listener.setRegistration(registration);
1231     }
1232
1233     private static void ensureParentsByMerge(final LogicalDatastoreType store,
1234             final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1235             final SchemaContext schemaContext) {
1236         final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1237         YangInstanceIdentifier rootNormalizedPath = null;
1238
1239         final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1240
1241         while (it.hasNext()) {
1242             final PathArgument pathArgument = it.next();
1243             if (rootNormalizedPath == null) {
1244                 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1245             }
1246
1247             if (it.hasNext()) {
1248                 normalizedPathWithoutChildArgs.add(pathArgument);
1249             }
1250         }
1251
1252         if (normalizedPathWithoutChildArgs.isEmpty()) {
1253             return;
1254         }
1255
1256         checkArgument(rootNormalizedPath != null, "Empty path received");
1257         tx.merge(store, rootNormalizedPath, ImmutableNodes.fromInstanceId(schemaContext,
1258             YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)));
1259     }
1260
1261     private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1262         LOG.warn("DOM data broker service is not available for mount point {}", path);
1263         return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1264     }
1265
1266     private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
1267         return mountPoint.getService(DOMSchemaService.class)
1268             .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
1269             .orElse(null);
1270     }
1271
1272     private static final class PatchStatusContextHelper {
1273         PatchStatusContext status;
1274
1275         public PatchStatusContext getStatus() {
1276             return this.status;
1277         }
1278
1279         public void setStatus(final PatchStatusContext status) {
1280             this.status = status;
1281         }
1282     }
1283 }