Renamed controller.md.sal.dom.api to mdsal.dom.api
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / controller / md / sal / dom / broker / impl / 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.controller.md.sal.dom.broker.impl;
9
10 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
11 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
12 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
13 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
14 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
15 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingConflictException;
16 import org.opendaylight.mdsal.dom.api.DOMDataTreeShardingService;
17
18 import com.google.common.base.Preconditions;
19 import java.util.Collection;
20 import java.util.EnumMap;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.TreeMap;
25 import javax.annotation.concurrent.GuardedBy;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.yangtools.concepts.ListenerRegistration;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService {
32     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTree.class);
33     private final Map<LogicalDatastoreType, ShardingTableEntry> shardingTables = new EnumMap<>(LogicalDatastoreType.class);
34     @GuardedBy("this")
35     private final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> idToProducer = new TreeMap<>();
36
37     @GuardedBy("this")
38     private ShardingTableEntry lookupShard(final DOMDataTreeIdentifier prefix) {
39         final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
40         if (t == null) {
41             return null;
42         }
43
44         return t.lookup(prefix.getRootIdentifier());
45     }
46
47     @GuardedBy("this")
48     private void storeShard(final DOMDataTreeIdentifier prefix, final ShardRegistration<?> reg) {
49         ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
50         if (t == null) {
51             t = new ShardingTableEntry();
52             shardingTables.put(prefix.getDatastoreType(), t);
53         }
54
55         t.store(prefix.getRootIdentifier(), reg);
56     }
57
58     void removeShard(final ShardRegistration<?> reg) {
59         final DOMDataTreeIdentifier prefix = reg.getPrefix();
60         final ShardRegistration<?> parentReg;
61
62         synchronized (this) {
63             final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
64             if (t == null) {
65                 LOG.warn("Shard registration {} points to non-existent table", reg);
66                 return;
67             }
68
69             t.remove(prefix.getRootIdentifier());
70             parentReg = lookupShard(prefix).getRegistration();
71
72             /*
73              * FIXME: adjust all producers. This is tricky, as we need different locking strategy,
74              *        simply because we risk AB/BA deadlock with a producer being split off from
75              *        a producer.
76              *
77              */
78         }
79
80         if (parentReg != null) {
81             parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
82         }
83     }
84
85     @Override
86     public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException {
87         final ShardRegistration<T> reg;
88         final ShardRegistration<?> parentReg;
89
90         synchronized (this) {
91             /*
92              * Lookup the parent shard (e.g. the one which currently matches the prefix),
93              * and if it exists, check if its registration prefix does not collide with
94              * this registration.
95              */
96             final ShardingTableEntry parent = lookupShard(prefix);
97             parentReg = parent.getRegistration();
98             if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
99                 throw new DOMDataTreeShardingConflictException(String.format("Prefix %s is already occupied by shard %s", prefix, parentReg.getInstance()));
100             }
101
102             // FIXME: wrap the shard in a proper adaptor based on implemented interface
103
104             reg = new ShardRegistration<T>(this, prefix, shard);
105
106             storeShard(prefix, reg);
107
108             // FIXME: update any producers/registrations
109         }
110
111         // Notify the parent shard
112         if (parentReg != null) {
113             parentReg.getInstance().onChildAttached(prefix, shard);
114         }
115
116         return reg;
117     }
118
119     @GuardedBy("this")
120     private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
121         for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : idToProducer.entrySet()) {
122             if (e.getKey().contains(subtree)) {
123                 return e.getValue();
124             }
125         }
126
127         return null;
128     }
129
130     synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
131         for (final DOMDataTreeIdentifier s : producer.getSubtrees()) {
132             final DOMDataTreeProducer r = idToProducer.remove(s);
133             if (!producer.equals(r)) {
134                 LOG.error("Removed producer {} on subtree {} while removing {}", r, s, producer);
135             }
136         }
137     }
138
139     @GuardedBy("this")
140     private DOMDataTreeProducer createProducer(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
141         // Record the producer's attachment points
142         final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, shardMap);
143         for (DOMDataTreeIdentifier s : shardMap.keySet()) {
144             idToProducer.put(s, ret);
145         }
146
147         return ret;
148     }
149
150     @Override
151     public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
152         Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
153
154         final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
155         for (final DOMDataTreeIdentifier s : subtrees) {
156             // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
157             final DOMDataTreeProducer producer = findProducer(s);
158             Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", s, producer);
159
160             shardMap.put(s, lookupShard(s).getRegistration().getInstance());
161         }
162
163         return createProducer(shardMap);
164     }
165
166     synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent, final Collection<DOMDataTreeIdentifier> subtrees) {
167         Preconditions.checkNotNull(parent);
168
169         final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
170         for (final DOMDataTreeIdentifier s : subtrees) {
171             shardMap.put(s, lookupShard(s).getRegistration().getInstance());
172         }
173
174         return createProducer(shardMap);
175     }
176
177     @Override
178     public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener, final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges, final Collection<DOMDataTreeProducer> producers) {
179         // FIXME Implement this.
180         throw new UnsupportedOperationException("Not implemented yet.");
181     }
182 }