393e1cd2ef83ed1fe4323cb7a58b8bb351fdde4e
[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 package org.opendaylight.mdsal.dom.store.inmemory;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.annotations.VisibleForTesting;
15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.collect.Maps;
17 import com.google.common.util.concurrent.ListeningExecutorService;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.concurrent.Executor;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
30 import org.opendaylight.mdsal.dom.spi.DOMDataTreePrefixTable;
31 import org.opendaylight.mdsal.dom.spi.shard.ChildShardContext;
32 import org.opendaylight.mdsal.dom.spi.shard.ForeignShardModificationContext;
33 import org.opendaylight.mdsal.dom.spi.shard.ReadableWriteableDOMDataTreeShard;
34 import org.opendaylight.mdsal.dom.spi.shard.SubshardProducerSpecification;
35 import org.opendaylight.mdsal.dom.spi.shard.WriteableDOMDataTreeShard;
36 import org.opendaylight.yangtools.concepts.ListenerRegistration;
37 import org.opendaylight.yangtools.util.concurrent.CountingRejectedExecutionHandler;
38 import org.opendaylight.yangtools.util.concurrent.FastThreadPoolExecutor;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeSnapshot;
41 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
42 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
43 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
44 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 @Beta
51 public class InMemoryDOMDataTreeShard implements ReadableWriteableDOMDataTreeShard, SchemaContextListener {
52     private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataTreeShard.class);
53     private static final int DEFAULT_SUBMIT_QUEUE_SIZE = 1000;
54
55     private final DOMDataTreePrefixTable<ChildShardContext> childShardsTable = DOMDataTreePrefixTable.create();
56     private final Map<DOMDataTreeIdentifier, ChildShardContext> childShards = new HashMap<>();
57     private final Collection<InMemoryDOMDataTreeShardProducer> producers = new HashSet<>();
58     private final InMemoryDOMDataTreeShardChangePublisher shardChangePublisher;
59     private final ListeningExecutorService executor;
60     private final DOMDataTreeIdentifier prefix;
61     private final DataTree dataTree;
62
63     InMemoryDOMDataTreeShard(final DOMDataTreeIdentifier prefix, final Executor dataTreeChangeExecutor,
64             final int maxDataChangeListenerQueueSize, final int submitQueueSize) {
65         this.prefix = requireNonNull(prefix);
66
67         final DataTreeConfiguration treeBaseConfig = treeTypeFor(prefix.getDatastoreType());
68         final DataTreeConfiguration treeConfig = new DataTreeConfiguration.Builder(treeBaseConfig.getTreeType())
69                 .setMandatoryNodesValidation(treeBaseConfig.isMandatoryNodesValidationEnabled())
70                 .setUniqueIndexes(treeBaseConfig.isUniqueIndexEnabled())
71                 .setRootPath(prefix.getRootIdentifier())
72                 .build();
73
74         this.dataTree = new InMemoryDataTreeFactory().create(treeConfig);
75
76         this.shardChangePublisher = new InMemoryDOMDataTreeShardChangePublisher(dataTreeChangeExecutor,
77                 maxDataChangeListenerQueueSize, dataTree, prefix.getRootIdentifier(), childShards);
78
79         final FastThreadPoolExecutor fte = new FastThreadPoolExecutor(1, submitQueueSize, "Shard[" + prefix + "]",
80             InMemoryDOMDataTreeShard.class);
81         fte.setRejectedExecutionHandler(CountingRejectedExecutionHandler.newCallerWaitsPolicy());
82         this.executor = MoreExecutors.listeningDecorator(fte);
83     }
84
85     public static InMemoryDOMDataTreeShard create(final DOMDataTreeIdentifier id,
86                                                   final Executor dataTreeChangeExecutor,
87                                                   final int maxDataChangeListenerQueueSize) {
88         return new InMemoryDOMDataTreeShard(id.toOptimized(), dataTreeChangeExecutor,
89                 maxDataChangeListenerQueueSize, DEFAULT_SUBMIT_QUEUE_SIZE);
90     }
91
92     public static InMemoryDOMDataTreeShard create(final DOMDataTreeIdentifier id,
93                                                   final Executor dataTreeChangeExecutor,
94                                                   final int maxDataChangeListenerQueueSize,
95                                                   final int submitQueueSize) {
96         return new InMemoryDOMDataTreeShard(id.toOptimized(), dataTreeChangeExecutor,
97                 maxDataChangeListenerQueueSize, submitQueueSize);
98     }
99
100     @Override
101     public void onGlobalContextUpdated(final SchemaContext context) {
102         dataTree.setSchemaContext(context);
103     }
104
105     @Override
106     public void onChildAttached(final DOMDataTreeIdentifier childPrefix, final DOMDataTreeShard child) {
107         checkArgument(child != this, "Attempted to attach child %s onto self", this);
108         reparentChildShards(childPrefix, child);
109
110         final ChildShardContext context = createContextFor(childPrefix, child);
111         childShards.put(childPrefix, context);
112         childShardsTable.store(childPrefix, context);
113         updateProducers();
114     }
115
116     @Override
117     public void onChildDetached(final DOMDataTreeIdentifier childPrefix, final DOMDataTreeShard child) {
118         childShards.remove(childPrefix);
119         childShardsTable.remove(childPrefix);
120         updateProducers();
121     }
122
123     private void updateProducers() {
124         for (InMemoryDOMDataTreeShardProducer p : producers) {
125             p.setModificationFactory(createModificationFactory(p.getPrefixes()));
126         }
127     }
128
129     @VisibleForTesting
130     InMemoryShardDataModificationFactory createModificationFactory(final Collection<DOMDataTreeIdentifier> prefixes) {
131         final Map<DOMDataTreeIdentifier, SubshardProducerSpecification> affected = new HashMap<>();
132         for (final DOMDataTreeIdentifier producerPrefix : prefixes) {
133             for (final ChildShardContext child : childShards.values()) {
134                 final DOMDataTreeIdentifier bindPath;
135                 if (producerPrefix.contains(child.getPrefix())) {
136                     bindPath = child.getPrefix();
137                 } else if (child.getPrefix().contains(producerPrefix)) {
138                     // Bound path is inside subshard
139                     bindPath = producerPrefix;
140                 } else {
141                     continue;
142                 }
143
144                 SubshardProducerSpecification spec = affected.get(child.getPrefix());
145                 if (spec == null) {
146                     spec = new SubshardProducerSpecification(child);
147                     affected.put(child.getPrefix(), spec);
148                 }
149                 spec.addPrefix(bindPath);
150             }
151         }
152
153         final InmemoryShardDataModificationFactoryBuilder builder =
154                 new InmemoryShardDataModificationFactoryBuilder(prefix);
155         for (final SubshardProducerSpecification spec : affected.values()) {
156             final ForeignShardModificationContext foreignContext =
157                     new ForeignShardModificationContext(spec.getPrefix(), spec.createProducer());
158             builder.addSubshard(foreignContext);
159             builder.addSubshard(spec.getPrefix(), foreignContext);
160         }
161
162         return builder.build();
163     }
164
165     @Override
166     public InMemoryDOMDataTreeShardProducer createProducer(final Collection<DOMDataTreeIdentifier> prefixes) {
167         for (final DOMDataTreeIdentifier prodPrefix : prefixes) {
168             checkArgument(prefix.contains(prodPrefix), "Prefix %s is not contained under shart root", prodPrefix,
169                 prefix);
170         }
171
172         final InMemoryDOMDataTreeShardProducer ret = new InMemoryDOMDataTreeShardProducer(this, prefixes,
173                 createModificationFactory(prefixes));
174         producers.add(ret);
175         return ret;
176     }
177
178     void closeProducer(final InMemoryDOMDataTreeShardProducer producer) {
179         if (!producers.remove(producer)) {
180             LOG.warn("Producer {} not found in shard {}", producer, this);
181         }
182     }
183
184     @Override
185     public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(
186             final YangInstanceIdentifier treeId, final L listener) {
187         return shardChangePublisher.registerTreeChangeListener(treeId, listener);
188     }
189
190     private void reparentChildShards(final DOMDataTreeIdentifier newChildPrefix, final DOMDataTreeShard newChild) {
191         final Iterator<Entry<DOMDataTreeIdentifier, ChildShardContext>> actualChildren =
192                 childShards.entrySet().iterator();
193         final Map<DOMDataTreeIdentifier, ChildShardContext> reparented = new HashMap<>();
194         while (actualChildren.hasNext()) {
195             final Entry<DOMDataTreeIdentifier, ChildShardContext> actualChild = actualChildren.next();
196             final DOMDataTreeIdentifier actualPrefix = actualChild.getKey();
197             checkArgument(!newChildPrefix.equals(actualPrefix), "Child shard with prefix %s already attached",
198                 newChildPrefix);
199             if (newChildPrefix.contains(actualPrefix)) {
200                 final ChildShardContext actualContext = actualChild.getValue();
201                 actualChildren.remove();
202                 newChild.onChildAttached(actualPrefix, actualContext.getShard());
203                 reparented.put(actualChild.getKey(), actualContext);
204                 childShardsTable.remove(actualPrefix);
205             }
206         }
207         updateProducersAndListeners(reparented);
208     }
209
210     private void updateProducersAndListeners(final Map<DOMDataTreeIdentifier, ChildShardContext> reparented) {
211         // FIXME: remove reparenting of producers, shards have to be registered from top to bottom
212         if (reparented.isEmpty()) {
213             //nothing was reparented no need to update anything
214             return;
215         }
216         throw new UnsupportedOperationException();
217     }
218
219     private static ChildShardContext createContextFor(final DOMDataTreeIdentifier prefix,
220             final DOMDataTreeShard child) {
221         checkArgument(child instanceof WriteableDOMDataTreeShard, "Child %s is not a writable shared", child);
222         return new ChildShardContext(prefix, (WriteableDOMDataTreeShard) child);
223     }
224
225     private static DataTreeConfiguration treeTypeFor(final LogicalDatastoreType dsType) {
226         switch (dsType) {
227             case CONFIGURATION:
228                 return DataTreeConfiguration.DEFAULT_CONFIGURATION;
229             case OPERATIONAL:
230                 return DataTreeConfiguration.DEFAULT_OPERATIONAL;
231             default:
232                 throw new IllegalArgumentException("Unsupported Data Store type:" + dsType);
233         }
234     }
235
236     @VisibleForTesting
237     Map<DOMDataTreeIdentifier, DOMDataTreeShard> getChildShards() {
238         return ImmutableMap.copyOf(Maps.transformValues(childShards, ChildShardContext::getShard));
239     }
240
241     DataTreeSnapshot takeSnapshot() {
242         return dataTree.takeSnapshot();
243     }
244
245     InmemoryDOMDataTreeShardWriteTransaction createTransaction(final String transactionId,
246             final InMemoryDOMDataTreeShardProducer producer, final DataTreeSnapshot snapshot) {
247         checkArgument(snapshot instanceof CursorAwareDataTreeSnapshot);
248         return new InmemoryDOMDataTreeShardWriteTransaction(producer,
249                 producer.getModificationFactory().createModification((CursorAwareDataTreeSnapshot) snapshot), dataTree,
250                 shardChangePublisher, executor);
251     }
252 }