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.HashMap;
15 import javax.annotation.concurrent.GuardedBy;
16 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
17 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
18 import org.opendaylight.mdsal.dom.api.DOMDataTreeLoopException;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
20 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
21 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingConflictException;
23 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingService;
24 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
25 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
26 import org.opendaylight.yangtools.concepts.ListenerRegistration;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
30 public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService {
31 private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTree.class);
34 private final ShardingTable<ShardRegistration<?>> shards = ShardingTable.create();
36 private final ShardingTable<DOMDataTreeProducer> producers = ShardingTable.create();
39 void removeShard(final ShardRegistration<?> reg) {
40 final DOMDataTreeIdentifier prefix = reg.getPrefix();
41 final ShardRegistration<?> parentReg;
44 shards.remove(prefix);
45 parentReg = shards.lookup(prefix).getValue();
48 * FIXME: adjust all producers and listeners. This is tricky, as we need different
49 * locking strategy, simply because we risk AB/BA deadlock with a producer being split
50 * off from a producer.
54 if (parentReg != null) {
55 parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
60 public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException {
61 final ShardRegistration<T> reg;
62 final ShardRegistration<?> parentReg;
66 * Lookup the parent shard (e.g. the one which currently matches the prefix),
67 * and if it exists, check if its registration prefix does not collide with
70 final ShardingTableEntry<ShardRegistration<?>> parent = shards.lookup(prefix);
72 parentReg = parent.getValue();
73 if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
74 throw new DOMDataTreeShardingConflictException(String.format(
75 "Prefix %s is already occupied by shard %s", prefix, parentReg.getInstance()));
81 // FIXME: wrap the shard in a proper adaptor based on implemented interface
83 reg = new ShardRegistration<T>(this, prefix, shard);
85 shards.store(prefix, reg);
87 // FIXME: update any producers/registrations
90 // Notify the parent shard
91 if (parentReg != null) {
92 parentReg.getInstance().onChildAttached(prefix, shard);
99 private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
101 ShardingTableEntry<DOMDataTreeProducer> producerEntry = producers.lookup(subtree);
102 if (producerEntry != null) {
103 return producerEntry.getValue();
108 synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
109 for (final DOMDataTreeIdentifier s : producer.getSubtrees()) {
115 private DOMDataTreeProducer createProducer(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
116 // Record the producer's attachment points
117 final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, shardMap);
118 for (final DOMDataTreeIdentifier s : shardMap.keySet()) {
119 producers.store(s, ret);
126 public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
127 Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
129 final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
130 for (final DOMDataTreeIdentifier s : subtrees) {
131 // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
132 final DOMDataTreeProducer producer = findProducer(s);
133 Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", s, producer);
135 shardMap.put(s, shards.lookup(s).getValue().getInstance());
138 return createProducer(shardMap);
141 synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent, final Collection<DOMDataTreeIdentifier> subtrees) {
142 Preconditions.checkNotNull(parent);
144 final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
145 for (final DOMDataTreeIdentifier s : subtrees) {
146 shardMap.put(s, shards.lookup(s).getValue().getInstance());
149 return createProducer(shardMap);
153 public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener,
154 final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges,
155 final Collection<DOMDataTreeProducer> producers) throws DOMDataTreeLoopException {
156 Preconditions.checkNotNull(listener, "listener");
157 Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees must not be empty.");
158 final ShardedDOMDataTreeListenerContext<T> listenerContext =
159 ShardedDOMDataTreeListenerContext.create(listener, subtrees, allowRxMerges);
161 // FIXME: Add attachment of producers
162 for (DOMDataTreeProducer producer : producers) {
163 Preconditions.checkArgument(producer instanceof ShardedDOMDataTreeProducer);
164 ShardedDOMDataTreeProducer castedProducer = ((ShardedDOMDataTreeProducer) producer);
165 simpleLoopCheck(subtrees, castedProducer.getSubtrees());
166 // FIXME: We should also unbound listeners
167 castedProducer.boundToListener(listenerContext);
170 for (DOMDataTreeIdentifier subtree : subtrees) {
171 DOMDataTreeShard shard = shards.lookup(subtree).getValue().getInstance();
172 // FIXME: What should we do if listener is wildcard? And shards are on per
174 Preconditions.checkArgument(shard instanceof DOMStoreTreeChangePublisher,
175 "Subtree %s does not point to listenable subtree.", subtree);
177 listenerContext.register(subtree, (DOMStoreTreeChangePublisher) shard);
179 } catch (Exception e) {
180 listenerContext.close();
183 return new AbstractListenerRegistration<T>(listener) {
185 protected void removeRegistration() {
186 ShardedDOMDataTree.this.removeListener(listenerContext);
191 private static void simpleLoopCheck(Collection<DOMDataTreeIdentifier> listen, Set<DOMDataTreeIdentifier> writes)
192 throws DOMDataTreeLoopException {
193 for(DOMDataTreeIdentifier listenPath : listen) {
194 for (DOMDataTreeIdentifier writePath : writes) {
195 if (listenPath.contains(writePath)) {
196 throw new DOMDataTreeLoopException(String.format(
197 "Listener must not listen on parent (%s), and also writes child (%s)", listenPath,
199 } else if (writePath.contains(listenPath)) {
200 throw new DOMDataTreeLoopException(
201 String.format("Listener must not write parent (%s), and also listen on child (%s)",
202 writePath, listenPath));
208 void removeListener(ShardedDOMDataTreeListenerContext<?> listener) {
209 // FIXME: detach producers