checkStyleViolationSeverity=error implemented for mdsal-dom-broker
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / ShardedDOMDataTreeProducer.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.ArrayListMultimap;
12 import com.google.common.collect.BiMap;
13 import com.google.common.collect.ImmutableBiMap;
14 import com.google.common.collect.ImmutableBiMap.Builder;
15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.ImmutableSet;
17 import com.google.common.collect.Multimap;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Set;
23 import javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
26 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducerBusyException;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducerException;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
30 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
31 import org.opendaylight.mdsal.dom.store.inmemory.WriteableDOMDataTreeShard;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
36     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducer.class);
37     private final Set<DOMDataTreeIdentifier> subtrees;
38     private final ShardedDOMDataTree dataTree;
39
40     private BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer = ImmutableBiMap.of();
41     private Map<DOMDataTreeIdentifier, DOMDataTreeShard> idToShard;
42
43     @GuardedBy("this")
44     private DOMDataTreeCursorAwareTransaction openTx;
45     @GuardedBy("this")
46     private Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children = Collections.emptyMap();
47     @GuardedBy("this")
48     private boolean closed;
49
50     @GuardedBy("this")
51     private ShardedDOMDataTreeListenerContext<?> attachedListener;
52
53     ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree,
54                                final Collection<DOMDataTreeIdentifier> subtrees,
55                                final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap,
56                                final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToId) {
57         this.dataTree = Preconditions.checkNotNull(dataTree);
58         if (!shardToId.isEmpty()) {
59             this.idToProducer = mapIdsToProducer(shardToId);
60         }
61         idToShard = ImmutableMap.copyOf(shardMap);
62         this.subtrees = ImmutableSet.copyOf(subtrees);
63     }
64
65     static DOMDataTreeProducer create(final ShardedDOMDataTree dataTree,
66                                       final Collection<DOMDataTreeIdentifier> subtrees,
67                                       final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
68         final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToIdentifiers = ArrayListMultimap.create();
69         // map which identifier belongs to which shard
70         for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> entry : shardMap.entrySet()) {
71             shardToIdentifiers.put(entry.getValue(), entry.getKey());
72         }
73
74         return new ShardedDOMDataTreeProducer(dataTree, subtrees, shardMap, shardToIdentifiers);
75     }
76
77     void subshardAdded(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
78         Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
79         final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToIdentifiers = ArrayListMultimap.create();
80         // map which identifier belongs to which shard
81         for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> entry : shardMap.entrySet()) {
82             shardToIdentifiers.put(entry.getValue(), entry.getKey());
83         }
84         this.idToProducer = mapIdsToProducer(shardToIdentifiers);
85         idToShard = ImmutableMap.copyOf(shardMap);
86     }
87
88     private BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> mapIdsToProducer(final Multimap<DOMDataTreeShard,
89             DOMDataTreeIdentifier> shardToId) {
90         final Builder<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducerBuilder = ImmutableBiMap.builder();
91         for (final Entry<DOMDataTreeShard, Collection<DOMDataTreeIdentifier>> entry : shardToId.asMap().entrySet()) {
92             if (entry.getKey() instanceof WriteableDOMDataTreeShard) {
93                 //create a single producer for all prefixes in a single shard
94                 final DOMDataTreeShardProducer producer = ((WriteableDOMDataTreeShard) entry.getKey())
95                         .createProducer(entry.getValue());
96                 // id mapped to producers
97                 for (final DOMDataTreeIdentifier id : entry.getValue()) {
98                     idToProducerBuilder.put(id, producer);
99                 }
100             } else {
101                 LOG.error("Unable to create a producer for shard that's not a WriteableDOMDataTreeShard");
102             }
103         }
104
105         return idToProducerBuilder.build();
106     }
107
108     @Override
109     public synchronized DOMDataTreeCursorAwareTransaction createTransaction(final boolean isolated) {
110         Preconditions.checkState(!closed, "Producer is already closed");
111         Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
112
113         this.openTx = new ShardedDOMDataTreeWriteTransaction(this, idToProducer, children);
114
115         return openTx;
116     }
117
118     @GuardedBy("this")
119     private boolean haveSubtree(final DOMDataTreeIdentifier subtree) {
120         for (final DOMDataTreeIdentifier i : idToShard.keySet()) {
121             if (i.contains(subtree)) {
122                 return true;
123             }
124         }
125
126         return false;
127     }
128
129     @GuardedBy("this")
130     private DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier domDataTreeIdentifier) {
131         for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : children.entrySet()) {
132             if (e.getKey().contains(domDataTreeIdentifier)) {
133                 return e.getValue();
134             }
135         }
136
137         return null;
138     }
139
140     @Override
141     public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
142         Preconditions.checkState(!closed, "Producer is already closed");
143         Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
144
145         for (final DOMDataTreeIdentifier s : subtrees) {
146             // Check if the subtree was visible at any time
147             Preconditions.checkArgument(haveSubtree(s), "Subtree %s was never available in producer %s", s, this);
148             // Check if the subtree has not been delegated to a child
149             final DOMDataTreeProducer child = lookupChild(s);
150             Preconditions.checkArgument(child == null, "Subtree %s is delegated to child producer %s", s, child);
151
152             // Check if part of the requested subtree is not delegated to a child.
153             for (final DOMDataTreeIdentifier c : children.keySet()) {
154                 if (s.contains(c)) {
155                     throw new IllegalArgumentException(String.format("Subtree %s cannot be delegated as it is"
156                             + " superset of already-delegated %s", s, c));
157                 }
158             }
159         }
160
161         final DOMDataTreeProducer ret = dataTree.createProducer(this, subtrees);
162         final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMDataTreeProducer> cb = ImmutableMap.builder();
163         cb.putAll(children);
164         for (final DOMDataTreeIdentifier s : subtrees) {
165             cb.put(s, ret);
166         }
167
168         children = cb.build();
169         return ret;
170     }
171
172     boolean isDelegatedToChild(final DOMDataTreeIdentifier path) {
173         for (final DOMDataTreeIdentifier c : children.keySet()) {
174             if (c.contains(path)) {
175                 return true;
176             }
177         }
178         return false;
179     }
180
181
182     @Override
183     public synchronized void close() throws DOMDataTreeProducerException {
184         if (!closed) {
185             if (openTx != null) {
186                 throw new DOMDataTreeProducerBusyException(String.format("Transaction %s is still open", openTx));
187             }
188
189             closed = true;
190             dataTree.destroyProducer(this);
191         }
192     }
193
194     protected Set<DOMDataTreeIdentifier> getSubtrees() {
195         return subtrees;
196     }
197
198     synchronized void cancelTransaction(final ShardedDOMDataTreeWriteTransaction transaction) {
199         if (!openTx.equals(transaction)) {
200             LOG.warn("Transaction {} is not open in producer {}", transaction, this);
201             return;
202         }
203
204         LOG.debug("Transaction {} cancelled", transaction);
205         openTx = null;
206     }
207
208     synchronized void transactionSubmitted(final ShardedDOMDataTreeWriteTransaction transaction) {
209         Preconditions.checkState(openTx.equals(transaction));
210         openTx = null;
211     }
212
213     synchronized void boundToListener(final ShardedDOMDataTreeListenerContext<?> listener) {
214         // FIXME: Add option to dettach
215         Preconditions.checkState(this.attachedListener == null,
216                 "Producer %s is already attached to other listener.",
217                 listener.getListener());
218         this.attachedListener = listener;
219     }
220 }