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