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