2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.dom.broker;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Matchers.anyCollection;
14 import static org.mockito.Matchers.anyMap;
15 import static org.mockito.Mockito.doNothing;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.inOrder;
18 import static org.mockito.Mockito.timeout;
19 import static org.mockito.Mockito.verify;
20 import static org.mockito.Mockito.verifyNoMoreInteractions;
22 import com.google.common.util.concurrent.Futures;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.mockito.ArgumentCaptor;
32 import org.mockito.Captor;
33 import org.mockito.InOrder;
34 import org.mockito.Mockito;
35 import org.mockito.MockitoAnnotations;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
41 import org.opendaylight.mdsal.dom.broker.util.TestModel;
42 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
43 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardWriteTransaction;
44 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard;
45 import org.opendaylight.mdsal.dom.store.inmemory.WriteableDOMDataTreeShard;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
50 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
54 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
55 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
56 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
62 public class ShardedDOMDataTreeProducerMultiShardTest {
64 private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
66 private static SchemaContext schemaContext = null;
70 schemaContext = TestModel.createTestContext();
71 } catch (final ReactorException e) {
72 LOG.error("Unable to create schema context for TestModel", e);
76 private static final DOMDataTreeIdentifier ROOT_ID =
77 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
78 private static final DOMDataTreeIdentifier TEST_ID =
79 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
81 private static final DOMDataTreeIdentifier TEST2_ID =
82 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST2_PATH);
84 private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
85 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
86 private static final DOMDataTreeIdentifier ANOTHER_SHARD_ID =
87 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.ANOTHER_SHARD_PATH);
89 private InMemoryDOMDataTreeShard rootShard;
90 private InMemoryDOMDataTreeShard anotherInnerShard;
92 private ShardedDOMDataTree dataTreeService;
93 private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
94 private ListenerRegistration<InMemoryDOMDataTreeShard> innerShardReg;
96 private final ExecutorService executor = Executors.newSingleThreadExecutor();
99 private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
101 private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
103 private final ContainerNode crossShardContainer = createCrossShardContainer();
106 public void setUp() throws Exception {
107 MockitoAnnotations.initMocks(this);
109 rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1);
110 rootShard.onGlobalContextUpdated(schemaContext);
112 final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
113 final DOMDataTreeProducer shardRegProducer = dataTree.createProducer(Collections.singletonList(ROOT_ID));
114 rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard, shardRegProducer);
115 shardRegProducer.close();
117 dataTreeService = dataTree;
120 @Test(expected = IllegalStateException.class)
121 public void testTxReadyMultiples() throws Exception {
122 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
123 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
124 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
126 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
127 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
129 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
135 @Test(expected = IllegalStateException.class)
136 public void testSubmitUnclosedCursor() throws Exception {
137 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
138 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
139 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
141 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
142 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
145 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
150 public void testMultipleCursorsFromOneTx() throws Exception {
151 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
152 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
154 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID),
155 true, Collections.emptyList());
157 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
158 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
159 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
161 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
162 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
165 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
168 final DOMDataTreeWriteCursor newCursor = transaction.createCursor(ROOT_ID);
169 newCursor.enter(TestModel.TEST_PATH.getLastPathArgument());
170 final ContainerNode innerContainer = ImmutableContainerNodeBuilder.create()
171 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
172 .withChild(ImmutableLeafNodeBuilder.<String>create()
173 .withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1))
174 .withValue("inner-value")
178 newCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), innerContainer);
181 transaction.submit();
183 verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(
184 captorForChanges.capture(), captorForSubtrees.capture());
185 final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
186 assertTrue(capturedValue.size() == 1);
188 final ContainerNode dataAfter =
189 (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
190 assertEquals(innerContainer, dataAfter);
191 verifyNoMoreInteractions(mockedDataTreeListener);
195 public void testSingleShardListener() throws Exception {
196 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
197 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
199 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true,
200 Collections.emptyList());
202 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
203 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
204 writeCrossShardContainer(transaction);
206 verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(
207 captorForChanges.capture(), captorForSubtrees.capture());
208 final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
209 assertTrue(capturedValue.size() == 1);
211 final ContainerNode dataAfter =
212 (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
213 assertEquals(crossShardContainer.getChild(
214 TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), dataAfter);
216 final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> capturedSubtrees = captorForSubtrees.getValue();
217 assertTrue(capturedSubtrees.size() == 1);
218 assertTrue(capturedSubtrees.containsKey(INNER_CONTAINER_ID));
219 assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(),
220 capturedSubtrees.get(INNER_CONTAINER_ID));
222 verifyNoMoreInteractions(mockedDataTreeListener);
226 public void testMultipleShards() throws Exception {
227 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
228 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
230 final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1);
231 innerShard.onGlobalContextUpdated(schemaContext);
232 final DOMDataTreeProducer shardRegProducer =
233 dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
234 innerShardReg = dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, shardRegProducer);
235 shardRegProducer.close();
237 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID),
238 true, Collections.emptyList());
240 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
241 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
242 writeCrossShardContainer(transaction);
244 final ContainerNode testContainerVerificationNode = ImmutableContainerNodeBuilder.create()
245 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
248 //verify listeners have been notified
249 verify(mockedDataTreeListener, timeout(1000).times(4)).onDataTreeChanged(
250 captorForChanges.capture(), captorForSubtrees.capture());
251 final List<Collection<DataTreeCandidate>> capturedChanges = captorForChanges.getAllValues();
252 final List<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> capturedSubtrees =
253 captorForSubtrees.getAllValues();
254 final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(2).iterator().next();
256 assertTrue(capturedSubtrees.get(2).size() == 1);
257 assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
258 assertEquals(testContainerVerificationNode, capturedSubtrees.get(2).get(TEST_ID));
260 final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(3).iterator().next();
261 assertTrue(capturedSubtrees.get(3).size() == 1);
262 assertEquals(crossShardContainer, secondNotificationCandidate.getRootNode().getDataAfter().get());
263 assertEquals(crossShardContainer, capturedSubtrees.get(3).get(TEST_ID));
265 verifyNoMoreInteractions(mockedDataTreeListener);
269 public void testMultipleWritesIntoSingleShard() throws Exception {
270 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
271 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
273 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID),
274 true, Collections.emptyList());
276 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
277 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
278 writeCrossShardContainer(transaction);
280 final DOMDataTreeShardWriteTransaction newTx = producer.createTransaction();
281 final DOMDataTreeWriteCursor newCursor = newTx.createCursor(ROOT_ID);
283 newCursor.delete(TestModel.TEST_PATH.getLastPathArgument());
287 public void testMockedSubshards() throws Exception {
288 final WriteableDOMDataTreeShard mockedInnerShard = Mockito.mock(WriteableDOMDataTreeShard.class);
289 final ShardedDOMDataTreeProducer shardRegProducer = Mockito.mock(ShardedDOMDataTreeProducer.class);
290 doReturn(Collections.singleton(INNER_CONTAINER_ID)).when(shardRegProducer).getSubtrees();
291 doNothing().when(shardRegProducer).subshardAdded(anyMap());
293 dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, mockedInnerShard, shardRegProducer);
294 final DOMDataTreeShardProducer mockedProducer = Mockito.mock(DOMDataTreeShardProducer.class);
295 doReturn(mockedProducer).when(mockedInnerShard).createProducer(any(Collection.class));
297 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
299 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
300 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
301 cursor.enter(TestModel.TEST_PATH.getLastPathArgument());
303 final LeafNode<String> shardedValue1 =
304 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(
305 new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
306 final LeafNode<String> shardedValue2 =
307 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(
308 new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
310 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerNodeBuilder =
311 ImmutableContainerNodeBuilder.create();
312 final ContainerNode containerNode =
314 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
315 .withChild(shardedValue1)
316 .withChild(shardedValue2)
319 final DOMDataTreeShardWriteTransaction mockedTx = Mockito.mock(DOMDataTreeShardWriteTransaction.class);
320 doReturn(mockedTx).when(mockedProducer).createTransaction();
322 doNothing().when(mockedTx).ready();
323 doReturn(Futures.immediateFuture(true)).when(mockedTx).validate();
324 doReturn(Futures.immediateFuture(null)).when(mockedTx).prepare();
325 doReturn(Futures.immediateFuture(null)).when(mockedTx).commit();
327 final DOMDataTreeWriteCursor mockedCursor = Mockito.mock(DOMDataTreeWriteCursor.class);
328 doNothing().when(mockedCursor).write(any(PathArgument.class), any(NormalizedNode.class));
329 doNothing().when(mockedCursor).close();
330 doReturn(mockedCursor).when(mockedTx).createCursor(any(DOMDataTreeIdentifier.class));
332 cursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), containerNode);
333 cursor.enter(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
335 final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
336 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
337 .withChild(ImmutableLeafNodeBuilder.create().withNodeIdentifier(
338 new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE)).build())
341 cursor.write(TestModel.ANOTHER_SHARD_PATH.getLastPathArgument(), lowerShardContainer);
344 transaction.submit().get();
346 final InOrder inOrder = inOrder(mockedTx);
347 inOrder.verify(mockedTx).ready();
348 inOrder.verify(mockedTx).validate();
349 inOrder.verify(mockedTx).prepare();
350 inOrder.verify(mockedTx).commit();
354 private ContainerNode createCrossShardContainer() {
355 final LeafNode<String> shardedValue1 =
356 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(
357 new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
358 final LeafNode<String> shardedValue2 =
359 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(
360 TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
363 final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
364 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
365 .withChild(ImmutableLeafNodeBuilder.create()
366 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
367 .withValue("testing-value")
371 final ContainerNode containerNode =
372 ImmutableContainerNodeBuilder.create()
373 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
374 .withChild(shardedValue1)
375 .withChild(shardedValue2)
376 .withChild(lowerShardContainer)
379 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
380 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
381 .withChild(containerNode)
384 return testContainer;
387 private void writeCrossShardContainer(final DOMDataTreeShardWriteTransaction transaction) throws Exception {
388 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
390 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), crossShardContainer);
394 transaction.submit().get();