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