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