1 package org.opendaylight.mdsal.dom.broker;
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertTrue;
5 import static org.mockito.Matchers.any;
6 import static org.mockito.Matchers.anyCollection;
7 import static org.mockito.Matchers.anyMap;
8 import static org.mockito.Mockito.doNothing;
9 import static org.mockito.Mockito.doReturn;
10 import static org.mockito.Mockito.inOrder;
11 import static org.mockito.Mockito.timeout;
12 import static org.mockito.Mockito.verify;
13 import static org.mockito.Mockito.verifyNoMoreInteractions;
15 import com.google.common.util.concurrent.Futures;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.List;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.mockito.ArgumentCaptor;
25 import org.mockito.Captor;
26 import org.mockito.InOrder;
27 import org.mockito.Mockito;
28 import org.mockito.MockitoAnnotations;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
34 import org.opendaylight.mdsal.dom.broker.util.TestModel;
35 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
36 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardWriteTransaction;
37 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard;
38 import org.opendaylight.mdsal.dom.store.inmemory.WriteableDOMDataTreeShard;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
43 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
49 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
50 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
51 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
55 public class ShardedDOMDataTreeProducerMultiShardTest {
57 private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
59 private static SchemaContext schemaContext = null;
63 schemaContext = TestModel.createTestContext();
64 } catch (final ReactorException e) {
65 LOG.error("Unable to create schema context for TestModel", e);
69 private static final DOMDataTreeIdentifier ROOT_ID =
70 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
71 private static final DOMDataTreeIdentifier TEST_ID =
72 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
74 private static final DOMDataTreeIdentifier TEST2_ID =
75 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST2_PATH);
77 private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
78 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
79 private static final DOMDataTreeIdentifier ANOTHER_SHARD_ID =
80 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.ANOTHER_SHARD_PATH);
82 private InMemoryDOMDataTreeShard rootShard;
83 private InMemoryDOMDataTreeShard anotherInnerShard;
85 private ShardedDOMDataTree dataTreeService;
86 private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
87 private ListenerRegistration<InMemoryDOMDataTreeShard> innerShardReg;
89 private final ExecutorService executor = Executors.newSingleThreadExecutor();
92 private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
94 private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
96 private final ContainerNode crossShardContainer = createCrossShardContainer();
99 public void setUp() throws Exception {
100 MockitoAnnotations.initMocks(this);
102 rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1, 1);
103 rootShard.onGlobalContextUpdated(schemaContext);
105 final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
106 final DOMDataTreeProducer shardRegProducer = dataTree.createProducer(Collections.singletonList(ROOT_ID));
107 rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard, shardRegProducer);
108 shardRegProducer.close();
110 dataTreeService = dataTree;
113 @Test(expected=IllegalStateException.class)
114 public void testTxReadyMultiples() throws Exception {
115 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
116 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
117 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
119 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
120 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
122 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
128 @Test(expected=IllegalStateException.class)
129 public void testSubmitUnclosedCursor() throws Exception {
130 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
131 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
132 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
134 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
135 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
138 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
143 public void testMultipleCursorsFromOneTx() throws Exception {
144 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
145 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
147 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
149 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
150 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
151 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
153 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
154 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
157 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
160 final DOMDataTreeWriteCursor newCursor = transaction.createCursor(ROOT_ID);
161 newCursor.enter(TestModel.TEST_PATH.getLastPathArgument());
162 final ContainerNode innerContainer = ImmutableContainerNodeBuilder.create()
163 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
164 .withChild(ImmutableLeafNodeBuilder.<String>create()
165 .withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1))
166 .withValue("inner-value")
170 newCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), innerContainer);
173 transaction.submit();
175 verify(mockedDataTreeListener, timeout(1000)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
176 final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
177 assertTrue(capturedValue.size() == 1);
179 final ContainerNode dataAfter = (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
180 assertEquals(innerContainer, dataAfter);
181 verifyNoMoreInteractions(mockedDataTreeListener);
185 public void testSingleShardListener() throws Exception {
186 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
187 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
189 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
191 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
192 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
193 writeCrossShardContainer(transaction);
195 verify(mockedDataTreeListener, timeout(1000)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
196 final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
197 assertTrue(capturedValue.size() == 1);
199 final ContainerNode dataAfter = (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
200 assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), dataAfter);
202 final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> capturedSubtrees = captorForSubtrees.getValue();
203 assertTrue(capturedSubtrees.size() == 1);
204 assertTrue(capturedSubtrees.containsKey(INNER_CONTAINER_ID));
205 assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), capturedSubtrees.get(INNER_CONTAINER_ID));
207 verifyNoMoreInteractions(mockedDataTreeListener);
211 public void testMultipleShards() throws Exception {
212 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
213 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
215 final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1, 1);
216 innerShard.onGlobalContextUpdated(schemaContext);
217 final DOMDataTreeProducer shardRegProducer = dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
218 innerShardReg = dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, shardRegProducer);
219 shardRegProducer.close();
221 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID), true, Collections.emptyList());
223 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
224 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
225 writeCrossShardContainer(transaction);
227 final ContainerNode testContainerVerificationNode = ImmutableContainerNodeBuilder.create()
228 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
231 //verify listeners have been notified
232 verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
233 final List<Collection<DataTreeCandidate>> capturedChanges = captorForChanges.getAllValues();
234 final List<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> capturedSubtrees = captorForSubtrees.getAllValues();
235 final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(0).iterator().next();
237 assertTrue(capturedSubtrees.get(0).size() == 1);
238 assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
239 assertEquals(testContainerVerificationNode, capturedSubtrees.get(0).get(TEST_ID));
241 final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(1).iterator().next();
242 assertTrue(capturedSubtrees.get(1).size() == 1);
243 assertEquals(crossShardContainer, secondNotificationCandidate.getRootNode().getDataAfter().get());
244 assertEquals(crossShardContainer, capturedSubtrees.get(1).get(TEST_ID));
246 verifyNoMoreInteractions(mockedDataTreeListener);
250 public void testMultipleWritesIntoSingleShard() throws Exception {
251 final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
252 doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
254 dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
256 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
257 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
258 writeCrossShardContainer(transaction);
260 final DOMDataTreeShardWriteTransaction newTx = producer.createTransaction();
261 final DOMDataTreeWriteCursor newCursor = newTx.createCursor(ROOT_ID);
263 newCursor.delete(TestModel.TEST_PATH.getLastPathArgument());
267 public void testMockedSubshards() throws Exception {
268 final WriteableDOMDataTreeShard mockedInnerShard = Mockito.mock(WriteableDOMDataTreeShard.class);
269 final ShardedDOMDataTreeProducer shardRegProducer = Mockito.mock(ShardedDOMDataTreeProducer.class);
270 doReturn(Collections.singleton(INNER_CONTAINER_ID)).when(shardRegProducer).getSubtrees();
271 doNothing().when(shardRegProducer).subshardAdded(anyMap());
273 dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, mockedInnerShard, shardRegProducer);
274 final DOMDataTreeShardProducer mockedProducer = Mockito.mock(DOMDataTreeShardProducer.class);
275 doReturn(mockedProducer).when(mockedInnerShard).createProducer(any(Collection.class));
277 final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
279 final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
280 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
281 cursor.enter(TestModel.TEST_PATH.getLastPathArgument());
283 final LeafNode<String> shardedValue1 =
284 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
285 final LeafNode<String> shardedValue2 =
286 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
288 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerNodeBuilder = ImmutableContainerNodeBuilder.create();
289 final ContainerNode containerNode =
291 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
292 .withChild(shardedValue1)
293 .withChild(shardedValue2)
296 final DOMDataTreeShardWriteTransaction mockedTx = Mockito.mock(DOMDataTreeShardWriteTransaction.class);
297 doReturn(mockedTx).when(mockedProducer).createTransaction();
299 doNothing().when(mockedTx).ready();
300 doReturn(Futures.immediateFuture(true)).when(mockedTx).validate();
301 doReturn(Futures.immediateFuture(null)).when(mockedTx).prepare();
302 doReturn(Futures.immediateFuture(null)).when(mockedTx).commit();
304 final DOMDataTreeWriteCursor mockedCursor = Mockito.mock(DOMDataTreeWriteCursor.class);
305 doNothing().when(mockedCursor).write(any(PathArgument.class), any(NormalizedNode.class));
306 doNothing().when(mockedCursor).close();
307 doReturn(mockedCursor).when(mockedTx).createCursor(any(DOMDataTreeIdentifier.class));
309 cursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), containerNode);
310 cursor.enter(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
312 final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
313 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
314 .withChild(ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE)).build())
317 cursor.write(TestModel.ANOTHER_SHARD_PATH.getLastPathArgument(), lowerShardContainer);
320 transaction.submit().get();
322 final InOrder inOrder = inOrder(mockedTx);
323 inOrder.verify(mockedTx).ready();
324 inOrder.verify(mockedTx).validate();
325 inOrder.verify(mockedTx).prepare();
326 inOrder.verify(mockedTx).commit();
330 private ContainerNode createCrossShardContainer() {
331 final LeafNode<String> shardedValue1 =
332 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
333 final LeafNode<String> shardedValue2 =
334 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
337 final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
338 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
339 .withChild(ImmutableLeafNodeBuilder.create()
340 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
341 .withValue("testing-value")
345 final ContainerNode containerNode =
346 ImmutableContainerNodeBuilder.create()
347 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
348 .withChild(shardedValue1)
349 .withChild(shardedValue2)
350 .withChild(lowerShardContainer)
353 final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
354 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
355 .withChild(containerNode)
358 return testContainer;
361 private void writeCrossShardContainer(final DOMDataTreeShardWriteTransaction transaction) throws Exception{
362 final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
364 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), crossShardContainer);
368 transaction.submit().get();