Adjust to yangtools-2.0.0 changes
[mdsal.git] / dom / mdsal-dom-spi / src / main / java / org / opendaylight / mdsal / dom / spi / shard / DOMDataTreeChangeListenerAggregator.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies, s.ro. 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.spi.shard;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Stopwatch;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.Iterables;
15 import com.google.common.collect.Iterators;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Optional;
23 import javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
26 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
27 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
28 import org.opendaylight.yangtools.concepts.Identifiable;
29 import org.opendaylight.yangtools.concepts.ListenerRegistration;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
32 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * A compatibility class for bridging DOMDataTreeChangeListener, which can listen on only single subtree with
39  * {@link DOMDataTreeListener} interface.
40  *
41  * @author Robert Varga
42  * @deprecated This class is scheduled for removal when we remove compatibility with dom.spi.store APIs.
43  */
44 @Deprecated
45 final class DOMDataTreeChangeListenerAggregator
46         extends AbstractStateAggregator<DOMDataTreeChangeListenerAggregator.State> {
47
48     static final class State extends AbstractStateAggregator.State implements Identifiable<DOMDataTreeIdentifier> {
49         private final DOMDataTreeIdentifier identifier;
50         final List<DataTreeCandidate> changes;
51
52         State(final DOMDataTreeIdentifier identifier, final List<DataTreeCandidate> changes) {
53             this.identifier = Preconditions.checkNotNull(identifier);
54             this.changes = Preconditions.checkNotNull(changes);
55         }
56
57         @Override
58         public DOMDataTreeIdentifier getIdentifier() {
59             return identifier;
60         }
61     }
62
63     private static final class StateBuilder extends AbstractStateAggregator.StateBuilder<State> {
64         @GuardedBy("this")
65         private final List<DataTreeCandidate> changes = new ArrayList<>();
66         private final DOMDataTreeIdentifier identifier;
67
68         StateBuilder(final DOMDataTreeIdentifier identifier) {
69             this.identifier = Preconditions.checkNotNull(identifier);
70         }
71
72         @Override
73         protected synchronized void append(final State state) {
74             changes.addAll(state.changes);
75         }
76
77         @Override
78         protected synchronized void appendInitial(final State state) {
79             // We are still starting up, so all we need to do is squash reported changes to an initial write event
80             final DataTreeCandidate last = Iterables.getLast(state.changes);
81             changes.clear();
82             final Optional<NormalizedNode<?, ?>> lastData = last.getRootNode().getDataAfter();
83             if (lastData.isPresent()) {
84                 changes.add(DataTreeCandidates.fromNormalizedNode(last.getRootPath(), lastData.get()));
85             }
86         }
87
88         @Override
89         public synchronized State build() {
90             final State ret = new State(identifier, ImmutableList.copyOf(changes));
91             changes.clear();
92             return ret;
93         }
94     }
95
96     private static final class Operational extends AbstractStateAggregator.Operational<State> {
97         private final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> subtrees = new HashMap<>();
98         private final DOMDataTreeListener listener;
99
100         Operational(final Collection<AbstractStateAggregator.StateBuilder<State>> builders,
101                 final DOMDataTreeListener listener) {
102             super(builders);
103             this.listener = Preconditions.checkNotNull(listener);
104         }
105
106         @Override
107         protected void notifyListener(final Iterator<State> iterator) {
108             final Stopwatch clock = Stopwatch.createStarted();
109             final List<DataTreeCandidate> changes = new ArrayList<>();
110             while (iterator.hasNext()) {
111                 final State state = iterator.next();
112                 final List<DataTreeCandidate> candidates = state.changes;
113                 if (!candidates.isEmpty()) {
114                     // Update current subtree snapshot based on last candidate node
115                     final DataTreeCandidateNode lastRoot = candidates.get(candidates.size() - 1).getRootNode();
116                     final Optional<NormalizedNode<?, ?>> optData = lastRoot.getDataAfter();
117                     if (optData.isPresent()) {
118                         subtrees.put(state.getIdentifier(), optData.get());
119                     } else {
120                         subtrees.remove(state.getIdentifier());
121                     }
122
123                     // Append changes
124                     changes.addAll(candidates);
125                 }
126             }
127
128             final int size = changes.size();
129             if (size != 0) {
130                 // Note: it is okay to leak changes, we must never leak mutable subtrees.
131                 listener.onDataTreeChanged(changes, ImmutableMap.copyOf(subtrees));
132                 LOG.trace("Listener {} processed {} changes in {}", listener, clock);
133             }
134         }
135     }
136
137     private static final Logger LOG = LoggerFactory.getLogger(DOMDataTreeChangeListenerAggregator.class);
138
139     private final boolean allowRxMerges;
140
141     DOMDataTreeChangeListenerAggregator(final int sizeHint, final boolean allowRxMerges) {
142         super(sizeHint);
143         this.allowRxMerges = allowRxMerges;
144     }
145
146     DOMDataTreeChangeListener createListener(final DOMDataTreeIdentifier treeId) {
147         // TODO: do not ignore allowRxMerges, but rather create a dedicated subclass or something
148         final StateBuilder builder = new StateBuilder(treeId);
149         addBuilder(builder);
150
151         return changes -> receiveState(builder, new State(treeId, ImmutableList.copyOf(changes)));
152     }
153
154     <L extends DOMDataTreeListener> ListenerRegistration<L> start(final L listener,
155             final Collection<ListenerRegistration<?>> regs) {
156         start(builders -> {
157             final Operational ret = new Operational(builders, listener);
158             ret.notifyListener(Iterators.transform(builders.iterator(), AbstractStateAggregator.StateBuilder::build));
159             return ret;
160         });
161
162         return new AbstractListenerRegistration<L>(listener) {
163             @Override
164             protected void removeRegistration() {
165                 regs.forEach(ListenerRegistration::close);
166             }
167         };
168     }
169 }