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.ArgumentMatchers.any;
13 import static org.mockito.ArgumentMatchers.anyCollection;
14 import static org.mockito.ArgumentMatchers.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.mock;
19 import static org.mockito.Mockito.timeout;
20 import static org.mockito.Mockito.verify;
21 import static org.mockito.Mockito.verifyNoMoreInteractions;
23 import com.google.common.util.concurrent.Futures;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.mockito.ArgumentCaptor;
33 import org.mockito.Captor;
34 import org.mockito.InOrder;
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.spi.shard.DOMDataTreeShardProducer;
43 import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardWriteTransaction;
44 import org.opendaylight.mdsal.dom.spi.shard.WriteableDOMDataTreeShard;
45 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard;
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;
59 public class ShardedDOMDataTreeProducerMultiShardTest {
61 private static final SchemaContext SCHEMA_CONTEXT = TestModel.createTestContext();
63 private static final DOMDataTreeIdentifier ROOT_ID =
64 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
65 private static final DOMDataTreeIdentifier TEST_ID =
66 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
68 private static final DOMDataTreeIdentifier TEST2_ID =
69 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST2_PATH);
71 private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
72 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
73 private static final DOMDataTreeIdentifier ANOTHER_SHARD_ID =
74 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.ANOTHER_SHARD_PATH);
76 private InMemoryDOMDataTreeShard rootShard;
77 private InMemoryDOMDataTreeShard anotherInnerShard;
79 private ShardedDOMDataTree dataTreeService;
80 private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
81 private ListenerRegistration<InMemoryDOMDataTreeShard> innerShardReg;
83 private final ExecutorService executor = Executors.newSingleThreadExecutor();
86 private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
88 private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
90 private final ContainerNode crossShardContainer = createCrossShardContainer();
93 public void setUp() throws Exception {
94 MockitoAnnotations.initMocks(this);
96 rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1);
97 rootShard.onGlobalContextUpdated(SCHEMA_CONTEXT);
99 final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
100 final DOMDataTreeProducer shardRegProducer = dataTree.createProducer(Collections.singletonList(ROOT_ID));
101 rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard, shardRegProducer);
102 shardRegProducer.close();
104 dataTreeService = dataTree;
107 @Test(expected = IllegalStateException.class)
108 public void testTxReadyMultiples() throws Exception {
109 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
110 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
111 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
113 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
114 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
116 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
122 @Test(expected = IllegalStateException.class)
123 public void testSubmitUnclosedCursor() throws Exception {
124 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
125 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
126 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
128 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
129 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
132 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
137 public void testMultipleCursorsFromOneTx() throws Exception {
138 final DOMDataTreeListener mockedDataTreeListener = mock(DOMDataTreeListener.class);
139 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
141 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID),
142 true, Collections.emptyList());
144 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
145 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
146 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
148 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
149 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
152 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
155 final DOMDataTreeWriteCursor newCursor = transaction.createCursor(ROOT_ID);
156 newCursor.enter(TestModel.TEST_PATH.getLastPathArgument());
157 final ContainerNode innerContainer = ImmutableContainerNodeBuilder.create()
158 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
159 .withChild(ImmutableLeafNodeBuilder.<String>create()
160 .withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1))
161 .withValue("inner-value")
165 newCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), innerContainer);
168 transaction.submit();
170 verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(
171 captorForChanges.capture(), captorForSubtrees.capture());
172 final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
173 assertTrue(capturedValue.size() == 1);
175 final ContainerNode dataAfter =
176 (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
177 assertEquals(innerContainer, dataAfter);
178 verifyNoMoreInteractions(mockedDataTreeListener);
182 public void testSingleShardListener() throws Exception {
183 final DOMDataTreeListener mockedDataTreeListener = mock(DOMDataTreeListener.class);
184 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
186 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true,
187 Collections.emptyList());
189 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
190 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
191 writeCrossShardContainer(transaction);
193 verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(
194 captorForChanges.capture(), captorForSubtrees.capture());
195 final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
196 assertTrue(capturedValue.size() == 1);
198 final ContainerNode dataAfter =
199 (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
200 assertEquals(crossShardContainer.getChild(
201 TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), dataAfter);
203 final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> capturedSubtrees = captorForSubtrees.getValue();
204 assertTrue(capturedSubtrees.size() == 1);
205 assertTrue(capturedSubtrees.containsKey(INNER_CONTAINER_ID));
206 assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(),
207 capturedSubtrees.get(INNER_CONTAINER_ID));
209 verifyNoMoreInteractions(mockedDataTreeListener);
213 public void testMultipleShards() throws Exception {
214 final DOMDataTreeListener mockedDataTreeListener = mock(DOMDataTreeListener.class);
215 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
217 final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1);
218 innerShard.onGlobalContextUpdated(SCHEMA_CONTEXT);
219 final DOMDataTreeProducer shardRegProducer =
220 dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
221 innerShardReg = dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, shardRegProducer);
222 shardRegProducer.close();
224 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID),
225 true, Collections.emptyList());
227 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
228 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
229 writeCrossShardContainer(transaction);
231 final ContainerNode testContainerVerificationNode = ImmutableContainerNodeBuilder.create()
232 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
235 //verify listeners have been notified
236 verify(mockedDataTreeListener, timeout(1000).times(4)).onDataTreeChanged(
237 captorForChanges.capture(), captorForSubtrees.capture());
238 final List<Collection<DataTreeCandidate>> capturedChanges = captorForChanges.getAllValues();
239 final List<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> capturedSubtrees =
240 captorForSubtrees.getAllValues();
241 final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(2).iterator().next();
243 assertTrue(capturedSubtrees.get(2).size() == 1);
244 assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
245 assertEquals(testContainerVerificationNode, capturedSubtrees.get(2).get(TEST_ID));
247 final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(3).iterator().next();
248 assertTrue(capturedSubtrees.get(3).size() == 1);
249 assertEquals(crossShardContainer, secondNotificationCandidate.getRootNode().getDataAfter().get());
250 assertEquals(crossShardContainer, capturedSubtrees.get(3).get(TEST_ID));
252 verifyNoMoreInteractions(mockedDataTreeListener);
256 public void testMultipleWritesIntoSingleShard() throws Exception {
257 final DOMDataTreeListener mockedDataTreeListener = mock(DOMDataTreeListener.class);
258 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
260 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID),
261 true, Collections.emptyList());
263 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
264 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
265 writeCrossShardContainer(transaction);
267 final DOMDataTreeShardWriteTransaction newTx = producer.createTransaction();
268 final DOMDataTreeWriteCursor newCursor = newTx.createCursor(ROOT_ID);
270 newCursor.delete(TestModel.TEST_PATH.getLastPathArgument());
274 public void testMockedSubshards() throws Exception {
275 final WriteableDOMDataTreeShard mockedInnerShard = mock(WriteableDOMDataTreeShard.class);
276 final DOMDataTreeShardProducer mockedProducer = mock(DOMDataTreeShardProducer.class);
277 doReturn(mockedProducer).when(mockedInnerShard).createProducer(anyCollection());
278 final ShardedDOMDataTreeProducer shardRegProducer = mock(ShardedDOMDataTreeProducer.class);
279 doReturn(Collections.singleton(INNER_CONTAINER_ID)).when(shardRegProducer).getSubtrees();
280 doNothing().when(shardRegProducer).subshardAdded(anyMap());
282 dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, mockedInnerShard, shardRegProducer);
284 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
286 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
287 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
288 cursor.enter(TestModel.TEST_PATH.getLastPathArgument());
290 final LeafNode<String> shardedValue1 =
291 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(
292 new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
293 final LeafNode<String> shardedValue2 =
294 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(
295 new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
297 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerNodeBuilder =
298 ImmutableContainerNodeBuilder.create();
299 final ContainerNode containerNode =
301 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
302 .withChild(shardedValue1)
303 .withChild(shardedValue2)
306 final DOMDataTreeShardWriteTransaction mockedTx = mock(DOMDataTreeShardWriteTransaction.class);
307 doReturn(mockedTx).when(mockedProducer).createTransaction();
309 doNothing().when(mockedTx).ready();
310 doReturn(Futures.immediateFuture(true)).when(mockedTx).validate();
311 doReturn(Futures.immediateFuture(null)).when(mockedTx).prepare();
312 doReturn(Futures.immediateFuture(null)).when(mockedTx).commit();
314 final DOMDataTreeWriteCursor mockedCursor = mock(DOMDataTreeWriteCursor.class);
315 doNothing().when(mockedCursor).write(any(PathArgument.class), any(NormalizedNode.class));
316 doNothing().when(mockedCursor).close();
317 doReturn(mockedCursor).when(mockedTx).createCursor(any(DOMDataTreeIdentifier.class));
319 cursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), containerNode);
320 cursor.enter(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
322 final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
323 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
324 .withChild(ImmutableLeafNodeBuilder.create().withNodeIdentifier(
325 new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE)).withValue("").build())
328 cursor.write(TestModel.ANOTHER_SHARD_PATH.getLastPathArgument(), lowerShardContainer);
331 transaction.submit().get();
333 final InOrder inOrder = inOrder(mockedTx);
334 inOrder.verify(mockedTx).ready();
335 inOrder.verify(mockedTx).validate();
336 inOrder.verify(mockedTx).prepare();
337 inOrder.verify(mockedTx).commit();
341 private static ContainerNode createCrossShardContainer() {
342 final LeafNode<String> shardedValue1 =
343 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(
344 new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
345 final LeafNode<String> shardedValue2 =
346 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(
347 TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
350 final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
351 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
352 .withChild(ImmutableLeafNodeBuilder.create()
353 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
354 .withValue("testing-value")
358 final ContainerNode containerNode =
359 ImmutableContainerNodeBuilder.create()
360 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
361 .withChild(shardedValue1)
362 .withChild(shardedValue2)
363 .withChild(lowerShardContainer)
366 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
367 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
368 .withChild(containerNode)
371 return testContainer;
374 private void writeCrossShardContainer(final DOMDataTreeShardWriteTransaction transaction) throws Exception {
375 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
377 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), crossShardContainer);
381 transaction.submit().get();