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