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