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