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