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