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