2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.sal.restconf.impl;
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;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.concurrent.CountDownLatch;
24 import javax.annotation.Nullable;
25 import javax.ws.rs.core.Response.Status;
26 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
35 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
36 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
37 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
38 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
39 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
40 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
41 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
42 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
43 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
44 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
45 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
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;
58 public class BrokerFacade {
59 private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
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;
67 private BrokerFacade() {}
69 public void setRpcService(final DOMRpcService router) {
70 this.rpcService = router;
73 public void setDomNotificationService(final DOMNotificationService domNotification) {
74 this.domNotification = domNotification;
77 public void setContext(final ConsumerSession context) {
78 this.context = context;
81 public static BrokerFacade getInstance() {
82 return BrokerFacade.INSTANCE;
85 private void checkPreconditions() {
86 if ((this.context == null) || (this.domDataBroker == null)) {
87 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
92 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
94 return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
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);
102 final String errMsg = "DOM data broker service isn't available for mount point " + path;
104 throw new RestconfDocumentedException(errMsg);
108 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
109 checkPreconditions();
110 return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
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);
118 final String errMsg = "DOM data broker service isn't available for mount point " + path;
120 throw new RestconfDocumentedException(errMsg);
124 * <b>PUT configuration data</b>
126 * Prepare result(status) for PUT operation and PUT data via transaction.
127 * Return wrapped status and future from PUT.
129 * @param globalSchema
130 * - used by merge parents (if contains list)
135 * @return wrapper of status and future of PUT
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);
143 checkPreconditions();
145 final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
146 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
148 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
149 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema);
150 return new PutResult(status, future);
154 * <b>PUT configuration data (Mount point)</b>
156 * Prepare result(status) for PUT operation and PUT data via transaction.
157 * Return wrapped status and future from PUT.
160 * - mount point for getting transaction for operation and schema
161 * context for merging parents(if contains list)
166 * @return wrapper of status and future of PUT
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);
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);
184 final String errMsg = "DOM data broker service isn't available for mount point " + path;
186 throw new RestconfDocumentedException(errMsg);
189 public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext patchContext) throws Exception {
190 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
192 // get new transaction and schema context on server or on mounted device
193 final SchemaContext schemaContext;
194 final DOMDataReadWriteTransaction patchTransaction;
195 if (mountPoint == null) {
196 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
197 patchTransaction = this.domDataBroker.newReadWriteTransaction();
199 schemaContext = mountPoint.getSchemaContext();
201 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
203 if (optional.isPresent()) {
204 patchTransaction = optional.get().newReadWriteTransaction();
206 // if mount point does not have broker it is not possible to continue and global error is reported
207 LOG.error("Http PATCH {} has failed - device {} does not support broker service",
208 patchContext.getPatchId(), mountPoint.getIdentifier());
209 return new PATCHStatusContext(
210 patchContext.getPatchId(),
213 ImmutableList.of(new RestconfError(
214 ErrorType.APPLICATION,
215 ErrorTag.OPERATION_FAILED,
216 "DOM data broker service isn't available for mount point "
217 + mountPoint.getIdentifier()))
222 final List<PATCHStatusEntity> editCollection = new ArrayList<>();
223 List<RestconfError> editErrors;
224 boolean withoutError = true;
226 for (final PATCHEntity patchEntity : patchContext.getData()) {
227 final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
233 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
234 patchEntity.getNode(), schemaContext);
235 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
236 } catch (final RestconfDocumentedException e) {
237 LOG.error("Error call http PATCH operation {} on target {}",
239 patchEntity.getTargetNode().toString());
241 editErrors = new ArrayList<>();
242 editErrors.addAll(e.getErrors());
243 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
244 withoutError = false;
251 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
252 .getTargetNode(), patchEntity.getNode(), schemaContext);
253 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
254 } catch (final RestconfDocumentedException e) {
255 LOG.error("Error call http PATCH operation {} on target {}",
257 patchEntity.getTargetNode().toString());
259 editErrors = new ArrayList<>();
260 editErrors.addAll(e.getErrors());
261 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
262 withoutError = false;
269 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
271 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
272 } catch (final RestconfDocumentedException e) {
273 LOG.error("Error call http PATCH operation {} on target {}",
275 patchEntity.getTargetNode().toString());
277 editErrors = new ArrayList<>();
278 editErrors.addAll(e.getErrors());
279 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
280 withoutError = false;
287 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
289 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
290 } catch (final RestconfDocumentedException e) {
291 LOG.error("Error call http PATCH operation {} on target {}",
293 patchEntity.getTargetNode().toString());
295 editErrors = new ArrayList<>();
296 editErrors.addAll(e.getErrors());
297 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
298 withoutError = false;
305 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
306 patchEntity.getNode(), schemaContext);
307 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
308 } catch (final RestconfDocumentedException e) {
309 LOG.error("Error call http PATCH operation {} on target {}",
311 patchEntity.getTargetNode().toString());
313 editErrors = new ArrayList<>();
314 editErrors.addAll(e.getErrors());
315 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
316 withoutError = false;
321 LOG.error("Unsupported http PATCH operation {} on target {}",
323 patchEntity.getTargetNode().toString());
328 // if errors then cancel transaction and return error status
330 patchTransaction.cancel();
331 return new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
334 // if no errors commit transaction
335 final CountDownLatch waiter = new CountDownLatch(1);
336 final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
337 final PATCHStatusContextHelper status = new PATCHStatusContextHelper();
339 Futures.addCallback(future, new FutureCallback<Void>() {
341 public void onSuccess(@Nullable final Void result) {
342 status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
348 public void onFailure(final Throwable t) {
349 // if commit failed it is global error
350 LOG.error("Http PATCH {} transaction commit has failed", patchContext.getPatchId());
351 status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
352 false, ImmutableList.of(
353 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, t.getMessage()))));
359 return status.getStatus();
362 // POST configuration
363 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
364 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
365 checkPreconditions();
366 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
369 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
370 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
371 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
372 if (domDataBrokerService.isPresent()) {
373 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
374 payload, mountPoint.getSchemaContext());
376 final String errMsg = "DOM data broker service isn't available for mount point " + path;
378 throw new RestconfDocumentedException(errMsg);
381 // DELETE configuration
382 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
383 final YangInstanceIdentifier path) {
384 checkPreconditions();
385 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
388 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
389 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
390 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
391 if (domDataBrokerService.isPresent()) {
392 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
394 final String errMsg = "DOM data broker service isn't available for mount point " + path;
396 throw new RestconfDocumentedException(errMsg);
400 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
401 checkPreconditions();
402 if (this.rpcService == null) {
403 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
405 LOG.trace("Invoke RPC {} with input: {}", type, input);
406 return this.rpcService.invokeRpc(type, input);
409 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
410 final ListenerAdapter listener) {
411 checkPreconditions();
413 if (listener.isListening()) {
417 final YangInstanceIdentifier path = listener.getPath();
418 final ListenerRegistration<DOMDataChangeListener> registration = this.domDataBroker.registerDataChangeListener(
419 datastore, path, listener, scope);
421 listener.setRegistration(registration);
424 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
425 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
426 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
427 final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
428 final ReadDataResult<NormalizedNode<?, ?>> readData = new ReadDataResult<>();
429 final CountDownLatch responseWaiter = new CountDownLatch(1);
431 Futures.addCallback(listenableFuture, new FutureCallback<Optional<NormalizedNode<?, ?>>>() {
434 public void onSuccess(final Optional<NormalizedNode<?, ?>> result) {
435 handlingCallback(null, datastore, path, result, readData);
436 responseWaiter.countDown();
440 public void onFailure(final Throwable t) {
441 responseWaiter.countDown();
442 handlingCallback(t, datastore, path, null, null);
447 responseWaiter.await();
448 } catch (final Exception e) {
449 final String msg = "Problem while waiting for response";
451 throw new RestconfDocumentedException(msg, e);
453 return readData.getResult();
457 * POST data and submit transaction {@link DOMDataReadWriteTransaction}
460 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
461 final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
462 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
463 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
464 postData(rWTransaction, datastore, path, payload, schemaContext);
465 return rWTransaction.submit();
469 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}
471 private void postDataWithinTransaction(
472 final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
473 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
474 LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
475 postData(rWTransaction, datastore, path, payload, schemaContext);
478 // FIXME: This is doing correct post for container and list children, not sure if this will work for choice case
479 private void postData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
480 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
481 final SchemaContext schemaContext) {
482 if (payload instanceof MapNode) {
483 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
484 rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
485 ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
486 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
487 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
488 checkItemDoesNotExists(rWTransaction, datastore, childPath);
489 rWTransaction.put(datastore, childPath, child);
492 checkItemDoesNotExists(rWTransaction, datastore, path);
493 ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
494 rWTransaction.put(datastore, path, payload);
499 * Check if item already exists. Throws error if it does NOT already exist.
500 * @param rWTransaction Current transaction
501 * @param store Used datastore
502 * @param path Path to item to verify its existence
504 private void checkItemExists(final DOMDataReadWriteTransaction rWTransaction,
505 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
506 final CountDownLatch responseWaiter = new CountDownLatch(1);
507 final ReadDataResult<Boolean> readData = new ReadDataResult<>();
508 final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
510 Futures.addCallback(future, new FutureCallback<Boolean>() {
512 public void onSuccess(@Nullable final Boolean result) {
513 handlingCallback(null, store, path, Optional.of(result), readData);
514 responseWaiter.countDown();
518 public void onFailure(final Throwable t) {
519 responseWaiter.countDown();
520 handlingCallback(t, store, path, null, null);
525 responseWaiter.await();
526 } catch (final Exception e) {
527 final String msg = "Problem while waiting for response";
529 throw new RestconfDocumentedException(msg, e);
532 if ((readData.getResult() == null) || !readData.getResult()) {
533 final String errMsg = "Operation via Restconf was not executed because data does not exist";
534 LOG.trace("{}:{}", errMsg, path);
535 rWTransaction.cancel();
536 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
537 ErrorTag.DATA_MISSING);
542 * Check if item does NOT already exist. Throws error if it already exists.
543 * @param rWTransaction Current transaction
544 * @param store Used datastore
545 * @param path Path to item to verify its existence
547 private void checkItemDoesNotExists(final DOMDataReadWriteTransaction rWTransaction,
548 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
549 final CountDownLatch responseWaiter = new CountDownLatch(1);
550 final ReadDataResult<Boolean> readData = new ReadDataResult<>();
551 final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
553 Futures.addCallback(future, new FutureCallback<Boolean>() {
555 public void onSuccess(@Nullable final Boolean result) {
556 handlingCallback(null, store, path, Optional.of(result), readData);
557 responseWaiter.countDown();
561 public void onFailure(final Throwable t) {
562 responseWaiter.countDown();
563 handlingCallback(t, store, path, null, null);
568 responseWaiter.await();
569 } catch (final Exception e) {
570 final String msg = "Problem while waiting for response";
572 throw new RestconfDocumentedException(msg, e);
575 if (readData.getResult()) {
576 final String errMsg = "Operation via Restconf was not executed because data already exists";
577 LOG.trace("{}:{}", errMsg, path);
578 rWTransaction.cancel();
579 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
580 ErrorTag.DATA_EXISTS);
585 * PUT data and submit {@link DOMDataReadWriteTransaction}
587 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
588 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
589 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
590 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
591 putData(readWriteTransaction, datastore, path, payload, schemaContext);
592 return readWriteTransaction.submit();
596 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}
598 private void putDataWithinTransaction(
599 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
600 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
601 LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
602 putData(writeTransaction, datastore, path, payload, schemaContext);
605 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
606 private void putData(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
607 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
608 if (payload instanceof MapNode) {
609 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
610 writeTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
611 ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
612 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
613 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
614 writeTransaction.put(datastore, childPath, child);
617 ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
618 writeTransaction.put(datastore, path, payload);
622 private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
623 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
624 final YangInstanceIdentifier path) {
625 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
626 checkItemExists(readWriteTransaction, datastore, path);
627 readWriteTransaction.delete(datastore, path);
628 return readWriteTransaction.submit();
631 private void deleteDataWithinTransaction(
632 final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
633 final YangInstanceIdentifier path) {
634 LOG.trace("Delete {} within Restconf PATCH: {}", datastore.name(), path);
635 writeTransaction.delete(datastore, path);
638 private void mergeDataWithinTransaction(
639 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
640 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
641 LOG.trace("Merge {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
642 ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
644 // merging is necessary only for lists otherwise we can call put method
645 if (payload instanceof MapNode) {
646 writeTransaction.merge(datastore, path, payload);
648 writeTransaction.put(datastore, path, payload);
652 public void setDomDataBroker(final DOMDataBroker domDataBroker) {
653 this.domDataBroker = domDataBroker;
657 * Helper class for result of transaction commit callback.
658 * @param <T> Type of result
660 private final class ReadDataResult<T> {
667 void setResult(final T result) {
668 this.result = result;
673 * Set result from transaction commit callback.
674 * @param t Throwable if transaction commit failed
675 * @param datastore Datastore from which data are read
676 * @param path Path from which data are read
677 * @param result Result of read from {@code datastore}
678 * @param readData Result value which will be set
679 * @param <X> Result type
681 protected final static <X> void handlingCallback(final Throwable t, final LogicalDatastoreType datastore,
682 final YangInstanceIdentifier path, final Optional<X> result,
683 final ReadDataResult<X> readData) {
685 LOG.warn("Exception by reading {} via Restconf: {}", datastore.name(), path, t);
686 throw new RestconfDocumentedException("Problem to get data from transaction.", t);
688 LOG.debug("Reading result data from transaction.");
689 if (result != null) {
690 if (result.isPresent()) {
691 readData.setResult(result.get());
697 public void registerToListenNotification(final NotificationListenerAdapter listener) {
698 checkPreconditions();
700 if (listener.isListening()) {
704 final SchemaPath path = listener.getSchemaPath();
705 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
706 .registerNotificationListener(listener, path);
708 listener.setRegistration(registration);
711 private final class PATCHStatusContextHelper {
712 PATCHStatusContext status;
714 public PATCHStatusContext getStatus() {
718 public void setStatus(final PATCHStatusContext status) {
719 this.status = status;
723 private void ensureParentsByMerge(final LogicalDatastoreType store, final YangInstanceIdentifier normalizedPath,
724 final DOMDataReadWriteTransaction rwTx, final SchemaContext schemaContext) {
725 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
726 YangInstanceIdentifier rootNormalizedPath = null;
728 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
730 while (it.hasNext()) {
731 final PathArgument pathArgument = it.next();
732 if (rootNormalizedPath == null) {
733 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
737 normalizedPathWithoutChildArgs.add(pathArgument);
741 if (normalizedPathWithoutChildArgs.isEmpty()) {
745 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
747 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
748 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
749 rwTx.merge(store, rootNormalizedPath, parentStructure);