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