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