2 * Copyright (c) 2017 Pantheon Technologies, s.ro. 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.mdsal.dom.spi.shard;
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;
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;
38 * A compatibility class for bridging DOMDataTreeChangeListener, which can listen on only single subtree with
39 * {@link DOMDataTreeListener} interface.
41 * @author Robert Varga
42 * @deprecated This class is scheduled for removal when we remove compatibility with dom.spi.store APIs.
45 final class DOMDataTreeChangeListenerAggregator
46 extends AbstractStateAggregator<DOMDataTreeChangeListenerAggregator.State> {
48 static final class State extends AbstractStateAggregator.State implements Identifiable<DOMDataTreeIdentifier> {
49 private final DOMDataTreeIdentifier identifier;
50 final List<DataTreeCandidate> changes;
52 State(final DOMDataTreeIdentifier identifier, final List<DataTreeCandidate> changes) {
53 this.identifier = Preconditions.checkNotNull(identifier);
54 this.changes = Preconditions.checkNotNull(changes);
58 public DOMDataTreeIdentifier getIdentifier() {
63 private static final class StateBuilder extends AbstractStateAggregator.StateBuilder<State> {
65 private final List<DataTreeCandidate> changes = new ArrayList<>();
66 private final DOMDataTreeIdentifier identifier;
68 StateBuilder(final DOMDataTreeIdentifier identifier) {
69 this.identifier = Preconditions.checkNotNull(identifier);
73 protected synchronized void append(final State state) {
74 changes.addAll(state.changes);
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);
82 final Optional<NormalizedNode<?, ?>> lastData = last.getRootNode().getDataAfter();
83 if (lastData.isPresent()) {
84 changes.add(DataTreeCandidates.fromNormalizedNode(last.getRootPath(), lastData.get()));
89 public synchronized State build() {
90 final State ret = new State(identifier, ImmutableList.copyOf(changes));
96 private static final class Operational extends AbstractStateAggregator.Operational<State> {
97 private final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> subtrees = new HashMap<>();
98 private final DOMDataTreeListener listener;
100 Operational(final Collection<AbstractStateAggregator.StateBuilder<State>> builders,
101 final DOMDataTreeListener listener) {
103 this.listener = Preconditions.checkNotNull(listener);
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());
120 subtrees.remove(state.getIdentifier());
124 changes.addAll(candidates);
128 final int size = changes.size();
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);
137 private static final Logger LOG = LoggerFactory.getLogger(DOMDataTreeChangeListenerAggregator.class);
139 private final boolean allowRxMerges;
141 DOMDataTreeChangeListenerAggregator(final int sizeHint, final boolean allowRxMerges) {
143 this.allowRxMerges = allowRxMerges;
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);
151 return changes -> receiveState(builder, new State(treeId, ImmutableList.copyOf(changes)));
154 <L extends DOMDataTreeListener> ListenerRegistration<L> start(final L listener,
155 final Collection<ListenerRegistration<?>> regs) {
157 final Operational ret = new Operational(builders, listener);
158 ret.notifyListener(Iterators.transform(builders.iterator(), AbstractStateAggregator.StateBuilder::build));
162 return new AbstractListenerRegistration<L>(listener) {
164 protected void removeRegistration() {
165 regs.forEach(ListenerRegistration::close);