2 * Copyright (c) 2015 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
8 package org.opendaylight.mdsal.dom.broker;
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.Iterables;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashMap;
17 import javax.annotation.concurrent.GuardedBy;
18 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
20 import org.opendaylight.mdsal.dom.api.DOMDataTreeLoopException;
21 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
23 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingConflictException;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingService;
26 import org.opendaylight.mdsal.dom.spi.DOMDataTreePrefixTable;
27 import org.opendaylight.mdsal.dom.spi.DOMDataTreePrefixTableEntry;
28 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
29 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
30 import org.opendaylight.yangtools.concepts.ListenerRegistration;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService {
35 private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTree.class);
38 private final DOMDataTreePrefixTable<ShardRegistration<?>> shards = DOMDataTreePrefixTable.create();
40 private final DOMDataTreePrefixTable<DOMDataTreeProducer> producers = DOMDataTreePrefixTable.create();
42 void removeShard(final ShardRegistration<?> reg) {
43 final DOMDataTreeIdentifier prefix = reg.getPrefix();
44 final ShardRegistration<?> parentReg;
47 shards.remove(prefix);
48 parentReg = shards.lookup(prefix).getValue();
51 * FIXME: adjust all producers and listeners. This is tricky, as we need different
52 * locking strategy, simply because we risk AB/BA deadlock with a producer being split
53 * off from a producer.
57 if (parentReg != null) {
58 parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
63 public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(
64 final DOMDataTreeIdentifier prefix, final T shard, final DOMDataTreeProducer producer)
65 throws DOMDataTreeShardingConflictException {
67 final DOMDataTreeIdentifier firstSubtree = Iterables.getOnlyElement(((
68 ShardedDOMDataTreeProducer) producer).getSubtrees());
69 Preconditions.checkArgument(firstSubtree != null, "Producer that is used to verify namespace claim can"
70 + " only claim a single namespace");
71 Preconditions.checkArgument(prefix.equals(firstSubtree), "Trying to register shard to a different namespace"
72 + " than the producer has claimed");
74 final ShardRegistration<T> reg;
75 final ShardRegistration<?> parentReg;
79 * Lookup the parent shard (e.g. the one which currently matches the prefix),
80 * and if it exists, check if its registration prefix does not collide with
83 final DOMDataTreePrefixTableEntry<ShardRegistration<?>> parent = shards.lookup(prefix);
85 parentReg = parent.getValue();
86 if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
87 throw new DOMDataTreeShardingConflictException(String.format(
88 "Prefix %s is already occupied by shard %s", prefix, parentReg.getInstance()));
94 // FIXME: wrap the shard in a proper adaptor based on implemented interface
96 reg = new ShardRegistration<T>(this, prefix, shard);
98 shards.store(prefix, reg);
100 ((ShardedDOMDataTreeProducer) producer).subshardAdded(Collections.singletonMap(prefix, shard));
103 // Notify the parent shard
104 if (parentReg != null) {
105 parentReg.getInstance().onChildAttached(prefix, shard);
112 private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
114 final DOMDataTreePrefixTableEntry<DOMDataTreeProducer> producerEntry = producers.lookup(subtree);
115 if (producerEntry != null) {
116 return producerEntry.getValue();
121 synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
122 for (final DOMDataTreeIdentifier s : producer.getSubtrees()) {
128 private DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees,
129 final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
130 // Record the producer's attachment points
131 final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, subtrees, shardMap);
132 for (final DOMDataTreeIdentifier subtree : subtrees) {
133 producers.store(subtree, ret);
140 public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
141 Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
143 final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
144 for (final DOMDataTreeIdentifier subtree : subtrees) {
145 // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
146 final DOMDataTreeProducer producer = findProducer(subtree);
147 Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", subtree, producer);
149 final DOMDataTreePrefixTableEntry<ShardRegistration<?>> possibleShardReg = shards.lookup(subtree);
150 if (possibleShardReg != null && possibleShardReg.getValue() != null) {
151 shardMap.put(subtree, possibleShardReg.getValue().getInstance());
155 return createProducer(subtrees, shardMap);
158 synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent,
159 final Collection<DOMDataTreeIdentifier> subtrees) {
160 Preconditions.checkNotNull(parent);
162 final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
163 for (final DOMDataTreeIdentifier s : subtrees) {
164 shardMap.put(s, shards.lookup(s).getValue().getInstance());
167 return createProducer(subtrees, shardMap);
170 @SuppressWarnings("checkstyle:IllegalCatch")
172 public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener,
173 final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges,
174 final Collection<DOMDataTreeProducer> producers) throws DOMDataTreeLoopException {
175 Preconditions.checkNotNull(listener, "listener");
176 Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees must not be empty.");
177 final ShardedDOMDataTreeListenerContext<T> listenerContext =
178 ShardedDOMDataTreeListenerContext.create(listener, subtrees, allowRxMerges);
180 // FIXME: Add attachment of producers
181 for (final DOMDataTreeProducer producer : producers) {
182 Preconditions.checkArgument(producer instanceof ShardedDOMDataTreeProducer);
183 final ShardedDOMDataTreeProducer castedProducer = ((ShardedDOMDataTreeProducer) producer);
184 simpleLoopCheck(subtrees, castedProducer.getSubtrees());
185 // FIXME: We should also unbound listeners
186 castedProducer.boundToListener(listenerContext);
189 for (final DOMDataTreeIdentifier subtree : subtrees) {
190 final DOMDataTreeShard shard = shards.lookup(subtree).getValue().getInstance();
191 // FIXME: What should we do if listener is wildcard? And shards are on per
193 Preconditions.checkArgument(shard instanceof DOMStoreTreeChangePublisher,
194 "Subtree %s does not point to listenable subtree.", subtree);
196 listenerContext.register(subtree, (DOMStoreTreeChangePublisher) shard);
198 } catch (final Exception e) {
199 listenerContext.close();
202 return new AbstractListenerRegistration<T>(listener) {
204 protected void removeRegistration() {
205 ShardedDOMDataTree.this.removeListener(listenerContext);
210 private static void simpleLoopCheck(final Collection<DOMDataTreeIdentifier> listen,
211 final Set<DOMDataTreeIdentifier> writes) throws DOMDataTreeLoopException {
212 for (final DOMDataTreeIdentifier listenPath : listen) {
213 for (final DOMDataTreeIdentifier writePath : writes) {
214 if (listenPath.contains(writePath)) {
215 throw new DOMDataTreeLoopException(String.format(
216 "Listener must not listen on parent (%s), and also writes child (%s)", listenPath,
218 } else if (writePath.contains(listenPath)) {
219 throw new DOMDataTreeLoopException(
220 String.format("Listener must not write parent (%s), and also listen on child (%s)",
221 writePath, listenPath));
227 void removeListener(final ShardedDOMDataTreeListenerContext<?> listener) {
228 // FIXME: detach producers