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 java.util.Collection;
12 import java.util.EnumMap;
13 import java.util.HashMap;
15 import java.util.Map.Entry;
17 import java.util.TreeMap;
18 import javax.annotation.concurrent.GuardedBy;
19 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
20 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
21 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeLoopException;
23 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
26 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingConflictException;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingService;
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);
36 private final Map<LogicalDatastoreType, ShardingTableEntry> shardingTables = new EnumMap<>(LogicalDatastoreType.class);
38 private final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> idToProducer = new TreeMap<>();
41 private ShardingTableEntry lookupShard(final DOMDataTreeIdentifier prefix) {
42 final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
47 return t.lookup(prefix.getRootIdentifier());
51 private void storeShard(final DOMDataTreeIdentifier prefix, final ShardRegistration<?> reg) {
52 ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
54 t = new ShardingTableEntry();
55 shardingTables.put(prefix.getDatastoreType(), t);
58 t.store(prefix.getRootIdentifier(), reg);
61 void removeShard(final ShardRegistration<?> reg) {
62 final DOMDataTreeIdentifier prefix = reg.getPrefix();
63 final ShardRegistration<?> parentReg;
66 final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
68 LOG.warn("Shard registration {} points to non-existent table", reg);
72 t.remove(prefix.getRootIdentifier());
73 parentReg = lookupShard(prefix).getRegistration();
76 * FIXME: adjust all producers and listeners. This is tricky, as we need different
77 * locking strategy, simply because we risk AB/BA deadlock with a producer being split
78 * off from a producer.
82 if (parentReg != null) {
83 parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
88 public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException {
89 final ShardRegistration<T> reg;
90 final ShardRegistration<?> parentReg;
94 * Lookup the parent shard (e.g. the one which currently matches the prefix),
95 * and if it exists, check if its registration prefix does not collide with
98 final ShardingTableEntry parent = lookupShard(prefix);
100 parentReg = parent.getRegistration();
101 if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
102 throw new DOMDataTreeShardingConflictException(String.format(
103 "Prefix %s is already occupied by shard %s", prefix, parentReg.getInstance()));
109 // FIXME: wrap the shard in a proper adaptor based on implemented interface
111 reg = new ShardRegistration<T>(this, prefix, shard);
113 storeShard(prefix, reg);
115 // FIXME: update any producers/registrations
118 // Notify the parent shard
119 if (parentReg != null) {
120 parentReg.getInstance().onChildAttached(prefix, shard);
127 private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
128 for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : idToProducer.entrySet()) {
129 if (e.getKey().contains(subtree)) {
137 synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
138 for (final DOMDataTreeIdentifier s : producer.getSubtrees()) {
139 final DOMDataTreeProducer r = idToProducer.remove(s);
140 if (!producer.equals(r)) {
141 LOG.error("Removed producer {} on subtree {} while removing {}", r, s, producer);
147 private DOMDataTreeProducer createProducer(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
148 // Record the producer's attachment points
149 final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, shardMap);
150 for (final DOMDataTreeIdentifier s : shardMap.keySet()) {
151 idToProducer.put(s, ret);
158 public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
159 Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
161 final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
162 for (final DOMDataTreeIdentifier s : subtrees) {
163 // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
164 final DOMDataTreeProducer producer = findProducer(s);
165 Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", s, producer);
167 shardMap.put(s, lookupShard(s).getRegistration().getInstance());
170 return createProducer(shardMap);
173 synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent, final Collection<DOMDataTreeIdentifier> subtrees) {
174 Preconditions.checkNotNull(parent);
176 final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
177 for (final DOMDataTreeIdentifier s : subtrees) {
178 shardMap.put(s, lookupShard(s).getRegistration().getInstance());
181 return createProducer(shardMap);
185 public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener,
186 final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges,
187 final Collection<DOMDataTreeProducer> producers) throws DOMDataTreeLoopException {
188 Preconditions.checkNotNull(listener, "listener");
189 Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees must not be empty.");
190 final ShardedDOMDataTreeListenerContext<T> listenerContext =
191 ShardedDOMDataTreeListenerContext.create(listener, subtrees, allowRxMerges);
193 // FIXME: Add attachment of producers
194 for (DOMDataTreeProducer producer : producers) {
195 Preconditions.checkArgument(producer instanceof ShardedDOMDataTreeProducer);
196 ShardedDOMDataTreeProducer castedProducer = ((ShardedDOMDataTreeProducer) producer);
197 simpleLoopCheck(subtrees, castedProducer.getSubtrees());
198 // FIXME: We should also unbound listeners
199 castedProducer.boundToListener(listenerContext);
202 for (DOMDataTreeIdentifier subtree : subtrees) {
203 DOMDataTreeShard shard = lookupShard(subtree).getRegistration().getInstance();
204 // FIXME: What should we do if listener is wildcard? And shards are on per
206 Preconditions.checkArgument(shard instanceof DOMStoreTreeChangePublisher,
207 "Subtree %s does not point to listenable subtree.", subtree);
209 listenerContext.register(subtree, (DOMStoreTreeChangePublisher) shard);
211 } catch (Exception e) {
212 listenerContext.close();
215 return new AbstractListenerRegistration<T>(listener) {
217 protected void removeRegistration() {
218 ShardedDOMDataTree.this.removeListener(listenerContext);
223 private static void simpleLoopCheck(Collection<DOMDataTreeIdentifier> listen, Set<DOMDataTreeIdentifier> writes)
224 throws DOMDataTreeLoopException {
225 for(DOMDataTreeIdentifier listenPath : listen) {
226 for (DOMDataTreeIdentifier writePath : writes) {
227 if (listenPath.contains(writePath)) {
228 throw new DOMDataTreeLoopException(String.format(
229 "Listener must not listen on parent (%s), and also writes child (%s)", listenPath,
231 } else if (writePath.contains(listenPath)) {
232 throw new DOMDataTreeLoopException(
233 String.format("Listener must not write parent (%s), and also listen on child (%s)",
234 writePath, listenPath));
240 void removeListener(ShardedDOMDataTreeListenerContext<?> listener) {
241 // FIXME: detach producers