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