Removed sonar warnings.
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / ShardedDOMDataTree.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.dom.broker;
9
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;
15 import java.util.Map;
16 import java.util.Set;
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
32 public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService {
33
34     @GuardedBy("this")
35     private final DOMDataTreePrefixTable<DOMDataTreeShardRegistration<?>> shards = DOMDataTreePrefixTable.create();
36     @GuardedBy("this")
37     private final DOMDataTreePrefixTable<DOMDataTreeProducer> producers = DOMDataTreePrefixTable.create();
38
39     void removeShard(final DOMDataTreeShardRegistration<?> reg) {
40         final DOMDataTreeIdentifier prefix = reg.getPrefix();
41         final DOMDataTreeShardRegistration<?> parentReg;
42
43         synchronized (this) {
44             shards.remove(prefix);
45             parentReg = shards.lookup(prefix).getValue();
46
47             /*
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.
51              */
52         }
53
54         if (parentReg != null) {
55             parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
56         }
57     }
58
59     @Override
60     public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(
61             final DOMDataTreeIdentifier prefix, final T shard, final DOMDataTreeProducer producer)
62                     throws DOMDataTreeShardingConflictException {
63
64         final DOMDataTreeIdentifier firstSubtree = Iterables.getOnlyElement(((
65                 ShardedDOMDataTreeProducer) producer).getSubtrees());
66         Preconditions.checkArgument(firstSubtree != null, "Producer that is used to verify namespace claim can"
67                 + " only claim a single namespace");
68         Preconditions.checkArgument(prefix.equals(firstSubtree), "Trying to register shard to a different namespace"
69                 + " than the producer has claimed");
70
71         final DOMDataTreeShardRegistration<T> reg;
72         final DOMDataTreeShardRegistration<?> parentReg;
73
74         synchronized (this) {
75             /*
76              * Lookup the parent shard (e.g. the one which currently matches the prefix),
77              * and if it exists, check if its registration prefix does not collide with
78              * this registration.
79              */
80             final DOMDataTreePrefixTableEntry<DOMDataTreeShardRegistration<?>> parent = shards.lookup(prefix);
81             if (parent != null) {
82                 parentReg = parent.getValue();
83                 if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
84                     throw new DOMDataTreeShardingConflictException(String.format(
85                             "Prefix %s is already occupied by shard %s", prefix, parentReg.getInstance()));
86                 }
87             } else {
88                 parentReg = null;
89             }
90
91             // FIXME: wrap the shard in a proper adaptor based on implemented interface
92
93             reg = new DOMDataTreeShardRegistration<>(this, prefix, shard);
94
95             shards.store(prefix, reg);
96
97             ((ShardedDOMDataTreeProducer) producer).subshardAdded(Collections.singletonMap(prefix, shard));
98         }
99
100         // Notify the parent shard
101         if (parentReg != null) {
102             parentReg.getInstance().onChildAttached(prefix, shard);
103         }
104
105         return reg;
106     }
107
108     @GuardedBy("this")
109     private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
110
111         final DOMDataTreePrefixTableEntry<DOMDataTreeProducer> producerEntry = producers.lookup(subtree);
112         if (producerEntry != null) {
113             return producerEntry.getValue();
114         }
115         return null;
116     }
117
118     synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
119         for (final DOMDataTreeIdentifier s : producer.getSubtrees()) {
120             producers.remove(s);
121         }
122     }
123
124     @GuardedBy("this")
125     private DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees,
126             final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
127         // Record the producer's attachment points
128         final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, subtrees, shardMap);
129         for (final DOMDataTreeIdentifier subtree : subtrees) {
130             producers.store(subtree, ret);
131         }
132
133         return ret;
134     }
135
136     @Override
137     public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
138         Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
139
140         final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
141         for (final DOMDataTreeIdentifier subtree : subtrees) {
142             // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
143             final DOMDataTreeProducer producer = findProducer(subtree);
144             Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", subtree, producer);
145
146             final DOMDataTreePrefixTableEntry<DOMDataTreeShardRegistration<?>> possibleShardReg =
147                     shards.lookup(subtree);
148             if (possibleShardReg != null && possibleShardReg.getValue() != null) {
149                 shardMap.put(subtree, possibleShardReg.getValue().getInstance());
150             }
151         }
152
153         return createProducer(subtrees, shardMap);
154     }
155
156     synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent,
157             final Collection<DOMDataTreeIdentifier> subtrees) {
158         Preconditions.checkNotNull(parent);
159
160         final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
161         for (final DOMDataTreeIdentifier s : subtrees) {
162             shardMap.put(s, shards.lookup(s).getValue().getInstance());
163         }
164
165         return createProducer(subtrees, shardMap);
166     }
167
168     @SuppressWarnings("checkstyle:IllegalCatch")
169     @Override
170     public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener,
171             final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges,
172             final Collection<DOMDataTreeProducer> producers) throws DOMDataTreeLoopException {
173         Preconditions.checkNotNull(listener, "listener");
174         Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees must not be empty.");
175         final ShardedDOMDataTreeListenerContext<T> listenerContext =
176                 ShardedDOMDataTreeListenerContext.create(listener);
177         try {
178             // FIXME: Add attachment of producers
179             for (final DOMDataTreeProducer producer : producers) {
180                 Preconditions.checkArgument(producer instanceof ShardedDOMDataTreeProducer);
181                 final ShardedDOMDataTreeProducer castedProducer = (ShardedDOMDataTreeProducer) producer;
182                 simpleLoopCheck(subtrees, castedProducer.getSubtrees());
183                 // FIXME: We should also unbound listeners
184                 castedProducer.bindToListener(listenerContext);
185             }
186
187             for (final DOMDataTreeIdentifier subtree : subtrees) {
188                 final DOMDataTreeShard shard = shards.lookup(subtree).getValue().getInstance();
189                 // FIXME: What should we do if listener is wildcard? And shards are on per
190                 // node basis?
191                 Preconditions.checkArgument(shard instanceof DOMStoreTreeChangePublisher,
192                         "Subtree %s does not point to listenable subtree.", subtree);
193
194                 listenerContext.register(subtree, (DOMStoreTreeChangePublisher) shard);
195             }
196         } catch (final Exception e) {
197             listenerContext.close();
198             throw e;
199         }
200         return new AbstractListenerRegistration<T>(listener) {
201             @Override
202             protected void removeRegistration() {
203                 ShardedDOMDataTree.this.removeListener(listenerContext);
204             }
205         };
206     }
207
208     private static void simpleLoopCheck(final Collection<DOMDataTreeIdentifier> listen,
209             final Set<DOMDataTreeIdentifier> writes) throws DOMDataTreeLoopException {
210         for (final DOMDataTreeIdentifier listenPath : listen) {
211             for (final DOMDataTreeIdentifier writePath : writes) {
212                 if (listenPath.contains(writePath)) {
213                     throw new DOMDataTreeLoopException(String.format(
214                             "Listener must not listen on parent (%s), and also writes child (%s)", listenPath,
215                             writePath));
216                 } else if (writePath.contains(listenPath)) {
217                     throw new DOMDataTreeLoopException(
218                             String.format("Listener must not write parent (%s), and also listen on child (%s)",
219                                     writePath, listenPath));
220                 }
221             }
222         }
223     }
224
225     void removeListener(final ShardedDOMDataTreeListenerContext<?> listener) {
226         // FIXME: detach producers
227         listener.close();
228     }
229 }