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