BUG-4202: Initial prototype of subshard aware Shard implementation
[mdsal.git] / dom / mdsal-dom-inmemory-datastore / src / main / java / org / opendaylight / mdsal / dom / store / inmemory / InMemoryDOMDataTreeShard.java
1 /*
2  * Copyright (c) 2016 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
9 package org.opendaylight.mdsal.dom.store.inmemory;
10
11 import com.google.common.annotations.Beta;
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.Preconditions;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
21 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
22 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
23 import org.opendaylight.mdsal.dom.spi.DOMDataTreePrefixTable;
24 import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeSnapshot;
25 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
26 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
27 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
28 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
29 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
30
31 @Beta
32 public class InMemoryDOMDataTreeShard implements WriteableDOMDataTreeShard, SchemaContextListener {
33
34     private static final class SubshardProducerSpecification {
35         private final Collection<DOMDataTreeIdentifier> prefixes = new ArrayList<>(1);
36         private final ChildShardContext shard;
37
38         SubshardProducerSpecification(final ChildShardContext subshard) {
39             this.shard = Preconditions.checkNotNull(subshard);
40         }
41
42         void addPrefix(final DOMDataTreeIdentifier prefix) {
43             prefixes.add(prefix);
44         }
45
46         DOMDataTreeShardProducer createProducer() {
47             return shard.getShard().createProducer(prefixes);
48         }
49
50         public DOMDataTreeIdentifier getPrefix() {
51             return shard.getPrefix();
52         }
53     }
54
55     private static final class ChildShardContext {
56         private final WriteableDOMDataTreeShard shard;
57         private final DOMDataTreeIdentifier prefix;
58
59         public ChildShardContext(final DOMDataTreeIdentifier prefix, final WriteableDOMDataTreeShard shard) {
60             this.prefix = Preconditions.checkNotNull(prefix);
61             this.shard = Preconditions.checkNotNull(shard);
62         }
63
64         public WriteableDOMDataTreeShard getShard() {
65             return shard;
66         }
67
68         public DOMDataTreeIdentifier getPrefix() {
69             return prefix;
70         }
71     }
72
73     private final DOMDataTreePrefixTable<ChildShardContext> childShardsTable = DOMDataTreePrefixTable.create();
74     private final Map<DOMDataTreeIdentifier, ChildShardContext> childShards = new HashMap<>();
75     private final DOMDataTreeIdentifier prefix;
76     private final DataTree dataTree;
77
78     private InMemoryDOMDataTreeShard(final DOMDataTreeIdentifier prefix) {
79         this.prefix = Preconditions.checkNotNull(prefix);
80
81         final TreeType treeType = treeTypeFor(prefix.getDatastoreType());
82         this.dataTree = prefix.getRootIdentifier().isEmpty() ? InMemoryDataTreeFactory.getInstance().create(treeType)
83                 : InMemoryDataTreeFactory.getInstance().create(treeType, prefix.getRootIdentifier());
84     }
85
86     public static InMemoryDOMDataTreeShard create(final DOMDataTreeIdentifier id) {
87         return new InMemoryDOMDataTreeShard(id);
88     }
89
90     @Override
91     public void onGlobalContextUpdated(final SchemaContext context) {
92         dataTree.setSchemaContext(context);
93     }
94
95     @Override
96     public void onChildAttached(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) {
97         Preconditions.checkArgument(child != this, "Attempted to attach child %s onto self", this);
98         reparentChildShards(prefix, child);
99         addChildShard(prefix, child);
100     }
101
102     @Override
103     public void onChildDetached(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) {
104         childShards.remove(prefix);
105     }
106
107     @Override
108     public InMemoryDOMDataTreeShardProducer createProducer(final Collection<DOMDataTreeIdentifier> prefixes) {
109         for (DOMDataTreeIdentifier prodPrefix : prefixes) {
110             Preconditions.checkArgument(prefix.contains(prodPrefix), "Prefix %s is not contained under shart root",
111                 prodPrefix, prefix);
112         }
113         return new InMemoryDOMDataTreeShardProducer(this, prefixes);
114     }
115
116     private void addChildShard(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) {
117         ChildShardContext context = createContextFor(prefix, child);
118         childShards.put(prefix, context);
119         childShardsTable.store(prefix, context);
120     }
121
122     private void reparentChildShards(final DOMDataTreeIdentifier newChildPrefix, final DOMDataTreeShard newChild) {
123         Iterator<Entry<DOMDataTreeIdentifier, ChildShardContext>> actualChildren = childShards.entrySet().iterator();
124         Map<DOMDataTreeIdentifier, ChildShardContext> reparented = new HashMap<>();
125         while (actualChildren.hasNext()) {
126             final Entry<DOMDataTreeIdentifier, ChildShardContext> actualChild = actualChildren.next();
127             final DOMDataTreeIdentifier actualPrefix = actualChild.getKey();
128             Preconditions.checkArgument(!newChildPrefix.equals(actualPrefix),
129                     "Child shard with prefix %s already attached", newChildPrefix);
130             if (newChildPrefix.contains(actualPrefix)) {
131                 ChildShardContext actualContext = actualChild.getValue();
132                 actualChildren.remove();
133                 newChild.onChildAttached(actualPrefix, actualContext.getShard());
134                 reparented.put(actualChild.getKey(), actualContext);
135                 childShardsTable.remove(actualPrefix);
136             }
137         }
138         updateProducersAndListeners(reparented);
139     }
140
141     private void updateProducersAndListeners(final Map<DOMDataTreeIdentifier, ChildShardContext> reparented) {
142         // FIXME: implement this
143         throw new UnsupportedOperationException();
144     }
145
146     private static ChildShardContext createContextFor(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) {
147         Preconditions.checkArgument(child instanceof WriteableDOMDataTreeShard,
148             "Child %s is not a writable shared", child);
149         return new ChildShardContext(prefix, (WriteableDOMDataTreeShard) child);
150     }
151
152     private static TreeType treeTypeFor(final LogicalDatastoreType dsType) {
153         switch (dsType) {
154             case CONFIGURATION:
155                 return TreeType.CONFIGURATION;
156             case OPERATIONAL:
157                 return TreeType.OPERATIONAL;
158             default:
159                 throw new IllegalArgumentException("Unsupported Data Store type:" + dsType);
160         }
161     }
162
163     @VisibleForTesting
164     Map<DOMDataTreeIdentifier, DOMDataTreeShard> getChildShards() {
165         Map<DOMDataTreeIdentifier, DOMDataTreeShard> ret = new HashMap<>();
166         for (Entry<DOMDataTreeIdentifier, ChildShardContext> entry : childShards.entrySet()) {
167             ret.put(entry.getKey(), entry.getValue().getShard());
168         }
169         return ret;
170     }
171
172     InmemoryDOMDataTreeShardWriteTransaction createTransaction(final InmemoryDOMDataTreeShardWriteTransaction previousTx) {
173         // FIXME: implement this
174         throw new UnsupportedOperationException();
175     }
176
177     InmemoryDOMDataTreeShardWriteTransaction createTransaction(final Collection<DOMDataTreeIdentifier> prefixes) {
178
179         Map<DOMDataTreeIdentifier, SubshardProducerSpecification> affectedSubshards = new HashMap<>();
180         for (DOMDataTreeIdentifier producerPrefix : prefixes) {
181             for (ChildShardContext maybeAffected : childShards.values()) {
182                 final DOMDataTreeIdentifier bindPath;
183                 if (producerPrefix.contains(maybeAffected.getPrefix())) {
184                     bindPath = maybeAffected.getPrefix();
185                 } else if (maybeAffected.getPrefix().contains(producerPrefix)) {
186                     // Bound path is inside subshard
187                     bindPath = producerPrefix;
188                 } else {
189                     continue;
190                 }
191
192                 SubshardProducerSpecification spec = affectedSubshards.get(maybeAffected.getPrefix());
193                 if (spec == null) {
194                     spec = new SubshardProducerSpecification(maybeAffected);
195                     affectedSubshards.put(maybeAffected.getPrefix(), spec);
196                 }
197                 spec.addPrefix(bindPath);
198             }
199         }
200
201         ShardRootModificationContext rootContext = new ShardRootModificationContext(prefix,
202                 (CursorAwareDataTreeSnapshot) dataTree.takeSnapshot());
203         ShardDataModificationBuilder builder = new ShardDataModificationBuilder(rootContext);
204         for (SubshardProducerSpecification spec : affectedSubshards.values()) {
205             ForeignShardModificationContext foreignContext =
206                     new ForeignShardModificationContext(spec.getPrefix(), spec.createProducer());
207             builder.addSubshard(foreignContext);
208         }
209
210         return new InmemoryDOMDataTreeShardWriteTransaction(builder.build());
211     }
212 }