2 * Copyright (c) 2016 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
9 package org.opendaylight.mdsal.dom.store.inmemory;
11 import com.google.common.base.Preconditions;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.List;
19 import java.util.stream.Collectors;
20 import javax.annotation.Nonnull;
21 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
23 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
24 import org.opendaylight.mdsal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
25 import org.opendaylight.mdsal.dom.spi.RegistrationTreeNode;
26 import org.opendaylight.mdsal.dom.spi.store.AbstractDOMStoreTreeChangePublisher;
27 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
28 import org.opendaylight.yangtools.concepts.ListenerRegistration;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
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.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
36 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 abstract class AbstractDOMShardTreeChangePublisher extends AbstractDOMStoreTreeChangePublisher implements DOMStoreTreeChangePublisher {
42 private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMShardTreeChangePublisher.class);
44 private YangInstanceIdentifier shardPath;
45 private final Map<DOMDataTreeIdentifier, ChildShardContext> childShards;
46 private final DataTree dataTree;
48 protected AbstractDOMShardTreeChangePublisher(final DataTree dataTree,
49 final YangInstanceIdentifier shardPath,
50 final Map<DOMDataTreeIdentifier, ChildShardContext> childShards) {
51 this.dataTree = Preconditions.checkNotNull(dataTree);
52 this.shardPath = Preconditions.checkNotNull(shardPath);
53 this.childShards = Preconditions.checkNotNull(childShards);
57 public <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier path, final L listener) {
60 return setupListenerContext(path, listener);
66 private <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> setupListenerContext(final YangInstanceIdentifier listenerPath, final L listener) {
67 // we need to register the listener registration path based on the shards root
68 // we have to strip the shard path from the listener path and then register
69 YangInstanceIdentifier strippedIdentifier = listenerPath;
70 if (!shardPath.isEmpty()) {
71 strippedIdentifier = YangInstanceIdentifier.create(stripShardPath(listenerPath));
74 final DOMDataTreeListenerWithSubshards subshardListener = new DOMDataTreeListenerWithSubshards(dataTree, strippedIdentifier, listener);
75 final AbstractDOMDataTreeChangeListenerRegistration<L> reg = setupContextWithoutSubshards(strippedIdentifier, subshardListener);
77 for (final ChildShardContext maybeAffected : childShards.values()) {
78 if (listenerPath.contains(maybeAffected.getPrefix().getRootIdentifier())) {
79 // consumer has a subshard somewhere on lower level
80 // register to the notification manager with snapshot and forward child notifications to parent
81 LOG.debug("Adding new subshard{{}} to listener at {}", maybeAffected.getPrefix(), listenerPath);
82 subshardListener.addSubshard(maybeAffected);
83 } else if (maybeAffected.getPrefix().getRootIdentifier().contains(listenerPath)) {
84 // bind path is inside subshard
85 // TODO can this happen? seems like in ShardedDOMDataTree we are already registering to the lowest shard possible
86 throw new UnsupportedOperationException("Listener should be registered directly into a subshard");
92 private <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> setupContextWithoutSubshards(final YangInstanceIdentifier listenerPath, final DOMDataTreeListenerWithSubshards listener) {
93 LOG.debug("Registering root listener at {}", listenerPath);
94 final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node = findNodeFor(listenerPath.getPathArguments());
95 final AbstractDOMDataTreeChangeListenerRegistration<L> registration = new AbstractDOMDataTreeChangeListenerRegistration<L>((L) listener) {
97 protected void removeRegistration() {
99 AbstractDOMShardTreeChangePublisher.this.removeRegistration(node, this);
100 registrationRemoved(this);
103 addRegistration(node, registration);
107 private Iterable<PathArgument> stripShardPath(final YangInstanceIdentifier listenerPath) {
108 if (shardPath.isEmpty()) {
109 return listenerPath.getPathArguments();
112 final List<PathArgument> listenerPathArgs = new ArrayList<>(listenerPath.getPathArguments());
113 final Iterator<PathArgument> shardIter = shardPath.getPathArguments().iterator();
114 final Iterator<PathArgument> listenerIter = listenerPathArgs.iterator();
116 while (shardIter.hasNext()) {
117 if (shardIter.next().equals(listenerIter.next())) {
118 listenerIter.remove();
124 return listenerPathArgs;
127 private static final class DOMDataTreeListenerWithSubshards implements DOMDataTreeChangeListener {
129 // TODO should we synchronize the access to the dataTree snapshots?
130 private final DataTree dataTree;
131 private final YangInstanceIdentifier listenerPath;
132 private final DOMDataTreeChangeListener delegate;
134 private final Map<YangInstanceIdentifier, ListenerRegistration<DOMDataTreeChangeListener>> registrations =
137 DOMDataTreeListenerWithSubshards(final DataTree dataTree,
138 final YangInstanceIdentifier listenerPath,
139 final DOMDataTreeChangeListener delegate) {
140 this.dataTree = Preconditions.checkNotNull(dataTree);
141 this.listenerPath = Preconditions.checkNotNull(listenerPath);
142 this.delegate = Preconditions.checkNotNull(delegate);
146 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
147 LOG.debug("Received data changed {}", changes.iterator().next());
148 final DataTreeCandidate newCandidate = applyChanges(changes);
149 delegate.onDataTreeChanged(Collections.singleton(newCandidate));
152 void onDataTreeChanged(final YangInstanceIdentifier rootPath, final Collection<DataTreeCandidate> changes) {
153 onDataTreeChanged(changes.stream()
154 .map(candidate -> DataTreeCandidates.newDataTreeCandidate(rootPath, candidate.getRootNode()))
155 .collect(Collectors.toList()));
158 void addSubshard(final ChildShardContext context) {
159 Preconditions.checkState(context.getShard() instanceof DOMStoreTreeChangePublisher,
160 "All subshards that are a part of ListenerContext need to be listenable");
162 final DOMStoreTreeChangePublisher listenableShard = (DOMStoreTreeChangePublisher) context.getShard();
163 // since this is going into subshard we want to listen for ALL changes in the subshard
164 registrations.put(context.getPrefix().getRootIdentifier(),
165 listenableShard.registerTreeChangeListener(context.getPrefix().getRootIdentifier(),
166 changes -> onDataTreeChanged(context.getPrefix().getRootIdentifier(), changes)));
170 for (ListenerRegistration<DOMDataTreeChangeListener> registration : registrations.values()) {
171 registration.close();
173 registrations.clear();
176 private DataTreeCandidate applyChanges(final Collection<DataTreeCandidate> changes) {
177 final DataTreeModification modification = dataTree.takeSnapshot().newModification();
178 for (final DataTreeCandidate change : changes) {
179 DataTreeCandidates.applyToModification(modification, change);
182 modification.ready();
184 dataTree.validate(modification);
185 } catch (DataValidationFailedException e) {
186 LOG.error("Validation failed for built modification", e);
187 throw new RuntimeException("Notification validation failed", e);
190 // strip nodes we dont need since this listener doesn't have to be registered at the root of the DataTree
191 DataTreeCandidateNode modifiedChild = dataTree.prepare(modification).getRootNode();
192 for (final PathArgument pathArgument : listenerPath.getPathArguments()) {
193 // there should be no null pointers since we wouldn't get a notification change
194 // if there was no node modified at the listener's location
195 modifiedChild = modifiedChild.getModifiedChild(pathArgument);
198 return DataTreeCandidates.newDataTreeCandidate(listenerPath, modifiedChild);