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