checkStyleViolationSeverity=error implemented for mdsal-dom-broker
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / ShardedDOMDataTreeProducerMultiShardTest.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.broker;
9
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;
21
22 import com.google.common.util.concurrent.Futures;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
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;
61
62 public class ShardedDOMDataTreeProducerMultiShardTest {
63
64     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
65
66     private static SchemaContext schemaContext = null;
67
68     static {
69         try {
70             schemaContext = TestModel.createTestContext();
71         } catch (final ReactorException e) {
72             LOG.error("Unable to create schema context for TestModel", e);
73         }
74     }
75
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);
80
81     private static final DOMDataTreeIdentifier TEST2_ID =
82             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST2_PATH);
83
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);
88
89     private InMemoryDOMDataTreeShard rootShard;
90     private InMemoryDOMDataTreeShard anotherInnerShard;
91
92     private ShardedDOMDataTree dataTreeService;
93     private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
94     private ListenerRegistration<InMemoryDOMDataTreeShard> innerShardReg;
95
96     private final ExecutorService executor = Executors.newSingleThreadExecutor();
97
98     @Captor
99     private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
100     @Captor
101     private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
102
103     private final ContainerNode crossShardContainer = createCrossShardContainer();
104
105     @Before
106     public void setUp() throws Exception {
107         MockitoAnnotations.initMocks(this);
108
109         rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1);
110         rootShard.onGlobalContextUpdated(schemaContext);
111
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();
116
117         dataTreeService = dataTree;
118     }
119
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);
125
126         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
127                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
128                 .build();
129         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
130         transaction.ready();
131
132         transaction.ready();
133     }
134
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);
140
141         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
142                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
143                 .build();
144
145         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
146         transaction.ready();
147     }
148
149     @Test
150     public void testMultipleCursorsFromOneTx() throws Exception {
151         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
152         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
153
154         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID),
155                 true, Collections.emptyList());
156
157         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
158         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
159         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
160
161         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
162                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
163                 .build();
164
165         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
166         cursor.close();
167
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")
175                         .build())
176                 .build();
177
178         newCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), innerContainer);
179         newCursor.close();
180         transaction.ready();
181         transaction.submit();
182
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);
187
188         final ContainerNode dataAfter =
189                 (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
190         assertEquals(innerContainer, dataAfter);
191         verifyNoMoreInteractions(mockedDataTreeListener);
192     }
193
194     @Test
195     public void testSingleShardListener() throws Exception {
196         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
197         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
198
199         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true,
200                 Collections.emptyList());
201
202         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
203         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
204         writeCrossShardContainer(transaction);
205
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);
210
211         final ContainerNode dataAfter =
212                 (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
213         assertEquals(crossShardContainer.getChild(
214                 TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), dataAfter);
215
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));
221
222         verifyNoMoreInteractions(mockedDataTreeListener);
223     }
224
225     @Test
226     public void testMultipleShards() throws Exception {
227         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
228         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
229
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();
236
237         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID),
238                 true, Collections.emptyList());
239
240         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
241         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
242         writeCrossShardContainer(transaction);
243
244         final ContainerNode testContainerVerificationNode = ImmutableContainerNodeBuilder.create()
245                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
246                 .build();
247
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();
255
256         assertTrue(capturedSubtrees.get(2).size() == 1);
257         assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
258         assertEquals(testContainerVerificationNode, capturedSubtrees.get(2).get(TEST_ID));
259
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));
264
265         verifyNoMoreInteractions(mockedDataTreeListener);
266     }
267
268     @Test
269     public void testMultipleWritesIntoSingleShard() throws Exception {
270         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
271         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
272
273         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID),
274                 true, Collections.emptyList());
275
276         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
277         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
278         writeCrossShardContainer(transaction);
279
280         final DOMDataTreeShardWriteTransaction newTx = producer.createTransaction();
281         final DOMDataTreeWriteCursor newCursor = newTx.createCursor(ROOT_ID);
282
283         newCursor.delete(TestModel.TEST_PATH.getLastPathArgument());
284     }
285
286     @Test
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());
292
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));
296
297         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
298
299         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
300         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
301         cursor.enter(TestModel.TEST_PATH.getLastPathArgument());
302
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();
309
310         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerNodeBuilder =
311                 ImmutableContainerNodeBuilder.create();
312         final ContainerNode containerNode =
313                 containerNodeBuilder
314                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
315                         .withChild(shardedValue1)
316                         .withChild(shardedValue2)
317                         .build();
318
319         final DOMDataTreeShardWriteTransaction mockedTx = Mockito.mock(DOMDataTreeShardWriteTransaction.class);
320         doReturn(mockedTx).when(mockedProducer).createTransaction();
321
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();
326
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));
331
332         cursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), containerNode);
333         cursor.enter(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
334
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())
339                 .build();
340
341         cursor.write(TestModel.ANOTHER_SHARD_PATH.getLastPathArgument(), lowerShardContainer);
342         cursor.close();
343         transaction.ready();
344         transaction.submit().get();
345
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();
351
352     }
353
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();
361
362
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")
368                         .build())
369                 .build();
370
371         final ContainerNode containerNode =
372                 ImmutableContainerNodeBuilder.create()
373                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
374                         .withChild(shardedValue1)
375                         .withChild(shardedValue2)
376                         .withChild(lowerShardContainer)
377                         .build();
378
379         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
380                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
381                 .withChild(containerNode)
382                 .build();
383
384         return testContainer;
385     }
386
387     private void writeCrossShardContainer(final DOMDataTreeShardWriteTransaction transaction) throws Exception {
388         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
389
390         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), crossShardContainer);
391
392         cursor.close();
393         transaction.ready();
394         transaction.submit().get();
395     }
396 }