Bug 4202: submit shard transactions
[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: remove reparenting of producers, shards have to be registered from top to bottom
143         if (reparented.isEmpty()) {
144             //nothing was reparented no need to update anything
145             return;
146         }
147         throw new UnsupportedOperationException();
148     }
149
150     private static ChildShardContext createContextFor(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) {
151         Preconditions.checkArgument(child instanceof WriteableDOMDataTreeShard,
152             "Child %s is not a writable shared", child);
153         return new ChildShardContext(prefix, (WriteableDOMDataTreeShard) child);
154     }
155
156     private static TreeType treeTypeFor(final LogicalDatastoreType dsType) {
157         switch (dsType) {
158             case CONFIGURATION:
159                 return TreeType.CONFIGURATION;
160             case OPERATIONAL:
161                 return TreeType.OPERATIONAL;
162             default:
163                 throw new IllegalArgumentException("Unsupported Data Store type:" + dsType);
164         }
165     }
166
167     @VisibleForTesting
168     Map<DOMDataTreeIdentifier, DOMDataTreeShard> getChildShards() {
169         Map<DOMDataTreeIdentifier, DOMDataTreeShard> ret = new HashMap<>();
170         for (Entry<DOMDataTreeIdentifier, ChildShardContext> entry : childShards.entrySet()) {
171             ret.put(entry.getKey(), entry.getValue().getShard());
172         }
173         return ret;
174     }
175
176     InmemoryDOMDataTreeShardWriteTransaction createTransaction(final InmemoryDOMDataTreeShardWriteTransaction previousTx) {
177         // FIXME: implement this
178         throw new UnsupportedOperationException();
179     }
180
181     InmemoryDOMDataTreeShardWriteTransaction createTransaction(final Collection<DOMDataTreeIdentifier> prefixes) {
182
183         Map<DOMDataTreeIdentifier, SubshardProducerSpecification> affectedSubshards = new HashMap<>();
184         for (DOMDataTreeIdentifier producerPrefix : prefixes) {
185             for (ChildShardContext maybeAffected : childShards.values()) {
186                 final DOMDataTreeIdentifier bindPath;
187                 if (producerPrefix.contains(maybeAffected.getPrefix())) {
188                     bindPath = maybeAffected.getPrefix();
189                 } else if (maybeAffected.getPrefix().contains(producerPrefix)) {
190                     // Bound path is inside subshard
191                     bindPath = producerPrefix;
192                 } else {
193                     continue;
194                 }
195
196                 SubshardProducerSpecification spec = affectedSubshards.get(maybeAffected.getPrefix());
197                 if (spec == null) {
198                     spec = new SubshardProducerSpecification(maybeAffected);
199                     affectedSubshards.put(maybeAffected.getPrefix(), spec);
200                 }
201                 spec.addPrefix(bindPath);
202             }
203         }
204
205         ShardRootModificationContext rootContext = new ShardRootModificationContext(prefix,
206                 (CursorAwareDataTreeSnapshot) dataTree.takeSnapshot());
207         ShardDataModificationBuilder builder = new ShardDataModificationBuilder(rootContext);
208         for (SubshardProducerSpecification spec : affectedSubshards.values()) {
209             ForeignShardModificationContext foreignContext =
210                     new ForeignShardModificationContext(spec.getPrefix(), spec.createProducer());
211             builder.addSubshard(foreignContext);
212             builder.addSubshard(spec.getPrefix(), foreignContext);
213         }
214
215         return new InmemoryDOMDataTreeShardWriteTransaction(builder.build(), dataTree);
216     }
217 }