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