Bump versions to 13.0.4-SNAPSHOT
[mdsal.git] / dom / mdsal-dom-inmemory-datastore / src / main / java / org / opendaylight / mdsal / dom / store / inmemory / InMemoryDOMStoreTreeChangePublisher.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.mdsal.dom.store.inmemory;
9
10 import java.util.List;
11 import java.util.concurrent.Executor;
12 import org.eclipse.jdt.annotation.NonNull;
13 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
14 import org.opendaylight.mdsal.dom.spi.store.AbstractDOMStoreTreeChangePublisher;
15 import org.opendaylight.yangtools.concepts.Registration;
16 import org.opendaylight.yangtools.util.concurrent.EqualityQueuedNotificationManager;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
19 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
20 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
21 import org.opendaylight.yangtools.yang.data.tree.spi.DataTreeCandidates;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 final class InMemoryDOMStoreTreeChangePublisher extends AbstractDOMStoreTreeChangePublisher {
26     private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMStoreTreeChangePublisher.class);
27
28     // Registrations use identity for equality, hence we can skip wrapping them
29     private final EqualityQueuedNotificationManager<Reg, DataTreeCandidate> notificationManager;
30
31     InMemoryDOMStoreTreeChangePublisher(final String dsName, final Executor listenerExecutor, final int maxQueueSize) {
32         notificationManager = new EqualityQueuedNotificationManager<>("DataTreeChangeListenerQueueMgr + dsName",
33             listenerExecutor, maxQueueSize,
34             (listener, notifications) -> {
35                 if (listener.notClosed()) {
36                     listener.listener().onDataTreeChanged(notifications);
37                 }
38             });
39     }
40
41     private InMemoryDOMStoreTreeChangePublisher(
42             final EqualityQueuedNotificationManager<Reg, DataTreeCandidate> notificationManager) {
43         this.notificationManager = notificationManager;
44     }
45
46     EqualityQueuedNotificationManager<?, ?> getNotificationManager() {
47         return notificationManager;
48     }
49
50     @Override
51     protected void notifyListener(final Reg registration, final List<DataTreeCandidate> changes) {
52         LOG.debug("Enqueueing candidates {} for registration {}", changes, registration);
53         notificationManager.submitNotifications(registration, changes);
54     }
55
56     @Override
57     protected synchronized void registrationRemoved(final Reg registration) {
58         LOG.debug("Closing registration {}", registration);
59
60         // FIXME: remove the queue for this registration and make sure we clear it
61     }
62
63     Registration registerTreeChangeListener(final YangInstanceIdentifier treeId,
64             final DOMDataTreeChangeListener listener, final DataTreeSnapshot snapshot) {
65         final var reg = registerTreeChangeListener(treeId, listener);
66         final var preExistingData = snapshot.readNode(YangInstanceIdentifier.of());
67         if (preExistingData.isEmpty()) {
68             listener.onInitialData();
69             return reg;
70         }
71
72         final var data = preExistingData.orElseThrow();
73         if (treeId.isEmpty()) {
74             if (data instanceof DataContainerNode container) {
75                 if (container.isEmpty()) {
76                     // If we are listening on root of data tree we still get empty normalized node, root is always
77                     // present, we should filter this out separately and notify it by 'onInitialData()' once.
78                     // Otherwise, it is just a valid data node with empty value which also should be notified by
79                     // "onDataTreeChanged(List<DataTreeCandidate>)".
80                     listener.onInitialData();
81                     return reg;
82                 }
83             } else {
84                 throw new IllegalStateException("Unexpected root node type " + data.contract());
85             }
86         }
87
88         final var candidate = DataTreeCandidates.fromNormalizedNode(YangInstanceIdentifier.of(), data);
89         final var publisher = new InMemoryDOMStoreTreeChangePublisher(notificationManager);
90         publisher.registerTreeChangeListener(treeId, listener);
91         if (!publisher.publishChange(candidate)) {
92             // There is no data in the conceptual data tree then notify with 'onInitialData()'.
93             listener.onInitialData();
94         }
95
96         return reg;
97     }
98
99     synchronized boolean publishChange(final @NonNull DataTreeCandidate candidate) {
100         // Runs synchronized with registrationRemoved()
101         return processCandidateTree(candidate);
102     }
103 }