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