45ce55169111d4b0e8b6704282deb3e849f3e7a8
[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.collect.Lists;
17 import com.google.common.util.concurrent.CheckedFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.ExecutionException;
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.TransactionCommitFailedException;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
35 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
36 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
37 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
38 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
39 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
40 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
41 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
42 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
43 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
44 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
45 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
46 import org.opendaylight.restconf.restful.utils.TransactionUtil;
47 import org.opendaylight.yangtools.concepts.ListenerRegistration;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
52 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
53 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
54 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 public class BrokerFacade {
59     private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
60
61     private final static BrokerFacade INSTANCE = new BrokerFacade();
62     private volatile DOMRpcService rpcService;
63     private volatile ConsumerSession context;
64     private DOMDataBroker domDataBroker;
65     private DOMNotificationService domNotification;
66
67     private BrokerFacade() {}
68
69     public void setRpcService(final DOMRpcService router) {
70         this.rpcService = router;
71     }
72
73     public void setDomNotificationService(final DOMNotificationService domNotification) {
74         this.domNotification = domNotification;
75     }
76
77     public void setContext(final ConsumerSession context) {
78         this.context = context;
79     }
80
81     public static BrokerFacade getInstance() {
82         return BrokerFacade.INSTANCE;
83     }
84
85     private void checkPreconditions() {
86         if ((this.context == null) || (this.domDataBroker == null)) {
87             throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
88         }
89     }
90
91     // READ configuration
92     public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
93         checkPreconditions();
94         return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
95     }
96
97     public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
98         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
99         if (domDataBrokerService.isPresent()) {
100             return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
101         }
102         final String errMsg = "DOM data broker service isn't available for mount point " + path;
103         LOG.warn(errMsg);
104         throw new RestconfDocumentedException(errMsg);
105     }
106
107     // READ operational
108     public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
109         checkPreconditions();
110         return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
111     }
112
113     public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
114         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
115         if (domDataBrokerService.isPresent()) {
116             return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
117         }
118         final String errMsg = "DOM data broker service isn't available for mount point " + path;
119         LOG.warn(errMsg);
120         throw new RestconfDocumentedException(errMsg);
121     }
122
123     /**
124      * <b>PUT configuration data</b>
125      *
126      * Prepare result(status) for PUT operation and PUT data via transaction.
127      * Return wrapped status and future from PUT.
128      *
129      * @param globalSchema
130      *            - used by merge parents (if contains list)
131      * @param path
132      *            - path of node
133      * @param payload
134      *            - input data
135      * @return wrapper of status and future of PUT
136      */
137     public PutResult commitConfigurationDataPut(
138             final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
139         Preconditions.checkNotNull(globalSchema);
140         Preconditions.checkNotNull(path);
141         Preconditions.checkNotNull(payload);
142
143         checkPreconditions();
144
145         final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
146         final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
147                 : Status.CREATED;
148         final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
149                 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema);
150         return new PutResult(status, future);
151     }
152
153     /**
154      * <b>PUT configuration data (Mount point)</b>
155      *
156      * Prepare result(status) for PUT operation and PUT data via transaction.
157      * Return wrapped status and future from PUT.
158      *
159      * @param mountPoint
160      *            - mount point for getting transaction for operation and schema
161      *            context for merging parents(if contains list)
162      * @param path
163      *            - path of node
164      * @param payload
165      *            - input data
166      * @return wrapper of status and future of PUT
167      */
168     public PutResult commitMountPointDataPut(
169             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
170         Preconditions.checkNotNull(mountPoint);
171         Preconditions.checkNotNull(path);
172         Preconditions.checkNotNull(payload);
173
174         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
175         if (domDataBrokerService.isPresent()) {
176             final DOMDataReadWriteTransaction newReadWriteTransaction = domDataBrokerService.get().newReadWriteTransaction();
177             final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
178                     ? Status.OK : Status.CREATED;
179             final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
180                     newReadWriteTransaction, CONFIGURATION, path,
181                     payload, mountPoint.getSchemaContext());
182             return new PutResult(status, future);
183         }
184         final String errMsg = "DOM data broker service isn't available for mount point " + path;
185         LOG.warn(errMsg);
186         throw new RestconfDocumentedException(errMsg);
187     }
188
189     public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext context,
190                                                                       final SchemaContext globalSchema)
191             throws InterruptedException {
192         final DOMDataReadWriteTransaction patchTransaction = this.domDataBroker.newReadWriteTransaction();
193         final List<PATCHStatusEntity> editCollection = new ArrayList<>();
194
195         List<RestconfError> editErrors;
196         int errorCounter = 0;
197
198         for (final PATCHEntity patchEntity : context.getData()) {
199             final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
200
201             switch (operation) {
202                 case CREATE:
203                     if (errorCounter == 0) {
204                         try {
205                             postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
206                                     patchEntity.getNode(), globalSchema);
207                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
208                         } catch (final RestconfDocumentedException e) {
209                             editErrors = new ArrayList<>();
210                             editErrors.addAll(e.getErrors());
211                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
212                             errorCounter++;
213                         }
214                     }
215                     break;
216                 case REPLACE:
217                     if (errorCounter == 0) {
218                         try {
219                             putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
220                                     .getTargetNode(), patchEntity.getNode(), globalSchema);
221                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
222                         } catch (final RestconfDocumentedException e) {
223                             editErrors = new ArrayList<>();
224                             editErrors.addAll(e.getErrors());
225                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
226                             errorCounter++;
227                         }
228                     }
229                     break;
230                 case DELETE:
231                     if (errorCounter == 0) {
232                         try {
233                             deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
234                                     .getTargetNode());
235                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
236                         } catch (final RestconfDocumentedException e) {
237                             editErrors = new ArrayList<>();
238                             editErrors.addAll(e.getErrors());
239                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
240                             errorCounter++;
241                         }
242                     }
243                     break;
244                 case REMOVE:
245                     if (errorCounter == 0) {
246                         try {
247                             deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
248                                     .getTargetNode());
249                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
250                         } catch (final RestconfDocumentedException e) {
251                             LOG.error("Error removing {} by {} operation", patchEntity.getTargetNode().toString(),
252                                     patchEntity.getEditId(), e);
253                         }
254                     }
255                     break;
256                 case MERGE:
257                     if (errorCounter == 0) {
258                         try {
259                             mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
260                                     patchEntity.getNode(), globalSchema);
261                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
262                         } catch (final RestconfDocumentedException e) {
263                             editErrors = new ArrayList<>();
264                             editErrors.addAll(e.getErrors());
265                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
266                             errorCounter++;
267                         }
268                     }
269                     break;
270             }
271         }
272
273         // if errors then cancel transaction and return error status
274         if (errorCounter != 0) {
275             patchTransaction.cancel();
276             return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
277         }
278
279         // if no errors commit transaction
280         final CountDownLatch waiter = new CountDownLatch(1);
281         final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
282         final PATCHStatusContextHelper status = new PATCHStatusContextHelper();
283
284         Futures.addCallback(future, new FutureCallback<Void>() {
285             @Override
286             public void onSuccess(@Nullable final Void result) {
287                 status.setStatus(new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
288                         true, null));
289                 waiter.countDown();
290             }
291
292             @Override
293             public void onFailure(final Throwable t) {
294                 // if commit failed it is global error
295                 status.setStatus(new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
296                         false, Lists.newArrayList(
297                         new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, t.getMessage()))));
298                 waiter.countDown();
299             }
300         });
301
302         waiter.await();
303         return status.getStatus();
304     }
305
306     // POST configuration
307     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
308             final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
309         checkPreconditions();
310         return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
311     }
312
313     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
314             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
315         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
316         if (domDataBrokerService.isPresent()) {
317             return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
318                     payload, mountPoint.getSchemaContext());
319         }
320         final String errMsg = "DOM data broker service isn't available for mount point " + path;
321         LOG.warn(errMsg);
322         throw new RestconfDocumentedException(errMsg);
323     }
324
325     // DELETE configuration
326     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
327             final YangInstanceIdentifier path) {
328         checkPreconditions();
329         return deleteDataViaTransaction(this.domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
330     }
331
332     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
333             final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
334         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
335         if (domDataBrokerService.isPresent()) {
336             return deleteDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path);
337         }
338         final String errMsg = "DOM data broker service isn't available for mount point " + path;
339         LOG.warn(errMsg);
340         throw new RestconfDocumentedException(errMsg);
341     }
342
343     // RPC
344     public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
345         checkPreconditions();
346         if (this.rpcService == null) {
347             throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
348         }
349         LOG.trace("Invoke RPC {} with input: {}", type, input);
350         return this.rpcService.invokeRpc(type, input);
351     }
352
353     public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
354             final ListenerAdapter listener) {
355         checkPreconditions();
356
357         if (listener.isListening()) {
358             return;
359         }
360
361         final YangInstanceIdentifier path = listener.getPath();
362         final ListenerRegistration<DOMDataChangeListener> registration = this.domDataBroker.registerDataChangeListener(
363                 datastore, path, listener, scope);
364
365         listener.setRegistration(registration);
366     }
367
368     private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
369             final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
370         LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
371         final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
372         final ReadDataResult readData = new ReadDataResult();
373         final CountDownLatch responseWaiter = new CountDownLatch(1);
374
375         Futures.addCallback(listenableFuture, new FutureCallback<Optional<NormalizedNode<?, ?>>>() {
376
377             @Override
378             public void onSuccess(final Optional<NormalizedNode<?, ?>> result) {
379                 handlingCallback(null, datastore, path, result, readData);
380                 responseWaiter.countDown();
381             }
382
383             @Override
384             public void onFailure(final Throwable t) {
385                 handlingCallback(t, datastore, path, null, null);
386                 responseWaiter.countDown();
387             }
388         });
389
390         try {
391             responseWaiter.await();
392         } catch (final InterruptedException e) {
393             final String msg = "Problem while waiting for response";
394             LOG.warn(msg);
395             throw new RestconfDocumentedException(msg, e);
396         }
397         return readData.getResult();
398     }
399
400     protected static void handlingCallback(final Throwable t, final LogicalDatastoreType datastore,
401             final YangInstanceIdentifier path, final Optional<NormalizedNode<?, ?>> result,
402             final ReadDataResult readData) {
403         if (t != null) {
404             LOG.warn("Exception by reading {} via Restconf: {}", datastore.name(), path, t);
405             throw new RestconfDocumentedException("Problem to get data from transaction.", t);
406         } else {
407             LOG.debug("Reading result data from transaction.");
408             if (result != null) {
409                 if (result.isPresent()) {
410                     readData.setResult(result.get());
411                 }
412             }
413         }
414     }
415
416     private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
417             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
418             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
419         // FIXME: This is doing correct post for container and list children
420         //        not sure if this will work for choice case
421         if(payload instanceof MapNode) {
422             LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
423             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
424             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
425             TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
426             for(final MapEntryNode child : ((MapNode) payload).getValue()) {
427                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
428                 checkItemDoesNotExists(rWTransaction, datastore, childPath);
429                 rWTransaction.put(datastore, childPath, child);
430             }
431         } else {
432             checkItemDoesNotExists(rWTransaction,datastore, path);
433             TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
434             rWTransaction.put(datastore, path, payload);
435         }
436         return rWTransaction.submit();
437     }
438
439     private void postDataWithinTransaction(
440             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
441             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
442         // FIXME: This is doing correct post for container and list children
443         //        not sure if this will work for choice case
444         if(payload instanceof MapNode) {
445             LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
446             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
447             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
448             TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
449             for(final MapEntryNode child : ((MapNode) payload).getValue()) {
450                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
451                 checkItemDoesNotExists(rWTransaction, datastore, childPath);
452                 rWTransaction.put(datastore, childPath, child);
453             }
454         } else {
455             checkItemDoesNotExists(rWTransaction,datastore, path);
456             TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
457             rWTransaction.put(datastore, path, payload);
458         }
459     }
460
461     private void checkItemDoesNotExists(final DOMDataReadWriteTransaction rWTransaction,final LogicalDatastoreType store, final YangInstanceIdentifier path) {
462         final ListenableFuture<Boolean> futureDatastoreData = rWTransaction.exists(store, path);
463         try {
464             if (futureDatastoreData.get()) {
465                 final String errMsg = "Post Configuration via Restconf was not executed because data already exists";
466                 LOG.trace("{}:{}", errMsg, path);
467                 rWTransaction.cancel();
468                 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
469                         ErrorTag.DATA_EXISTS);
470             }
471         } catch (InterruptedException | ExecutionException e) {
472             LOG.warn("It wasn't possible to get data loaded from datastore at path {}", path, e);
473         }
474
475     }
476
477     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
478             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
479             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
480         LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
481         TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
482         writeTransaction.put(datastore, path, payload);
483         return writeTransaction.submit();
484     }
485
486     private void putDataWithinTransaction(
487             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
488             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
489         LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
490         TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
491         writeTransaction.put(datastore, path, payload);
492     }
493
494     private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
495             final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
496             final YangInstanceIdentifier path) {
497         LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
498         writeTransaction.delete(datastore, path);
499         return writeTransaction.submit();
500     }
501
502     private void deleteDataWithinTransaction(
503             final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
504             final YangInstanceIdentifier path) {
505         LOG.trace("Delete {} within Restconf PATCH: {}", datastore.name(), path);
506         writeTransaction.delete(datastore, path);
507     }
508
509     private void mergeDataWithinTransaction(
510             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
511             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
512         LOG.trace("Merge {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
513         TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
514
515         // merging is necessary only for lists otherwise we can call put method
516         if (payload instanceof MapNode) {
517             writeTransaction.merge(datastore, path, payload);
518         } else {
519             writeTransaction.put(datastore, path, payload);
520         }
521     }
522
523     public void setDomDataBroker(final DOMDataBroker domDataBroker) {
524         this.domDataBroker = domDataBroker;
525     }
526
527     private class ReadDataResult {
528         NormalizedNode<?, ?> result = null;
529
530         NormalizedNode<?, ?> getResult() {
531             return this.result;
532         }
533
534         void setResult(final NormalizedNode<?, ?> result) {
535             this.result = result;
536         }
537     }
538
539     public void registerToListenNotification(final NotificationListenerAdapter listener) {
540         checkPreconditions();
541
542         if (listener.isListening()) {
543             return;
544         }
545
546         final SchemaPath path = listener.getSchemaPath();
547         final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
548                 .registerNotificationListener(listener, path);
549
550         listener.setRegistration(registration);
551     }
552
553     private final class PATCHStatusContextHelper {
554         PATCHStatusContext status;
555
556         public PATCHStatusContext getStatus() {
557             return this.status;
558         }
559
560         public void setStatus(PATCHStatusContext status) {
561             this.status = status;
562         }
563     }
564 }