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