70111ec7d24f579ec9ddf130424aa494e8e3b127
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / ShardedDOMDataTreeTest.java
1 package org.opendaylight.mdsal.dom.broker;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertNotNull;
5 import static org.junit.Assert.assertTrue;
6 import static org.junit.Assert.fail;
7 import static org.mockito.Matchers.anyCollection;
8 import static org.mockito.Matchers.anyMap;
9 import static org.mockito.Mockito.doNothing;
10 import static org.mockito.Mockito.timeout;
11 import static org.mockito.Mockito.verify;
12 import static org.mockito.Mockito.verifyNoMoreInteractions;
13
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.Executors;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.mockito.ArgumentCaptor;
23 import org.mockito.Captor;
24 import org.mockito.Mockito;
25 import org.mockito.MockitoAnnotations;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
32 import org.opendaylight.mdsal.dom.broker.util.TestModel;
33 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard;
34 import org.opendaylight.yangtools.concepts.ListenerRegistration;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 public class ShardedDOMDataTreeTest {
49
50     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
51
52     private static SchemaContext schemaContext = null;
53
54     static {
55         try {
56             schemaContext = TestModel.createTestContext();
57         } catch (final ReactorException e) {
58             LOG.error("Unable to create schema context for TestModel", e);
59         }
60     }
61
62     private static final DOMDataTreeIdentifier ROOT_ID =
63             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
64     private static final DOMDataTreeIdentifier TEST_ID =
65             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
66
67     private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
68             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
69
70     private InMemoryDOMDataTreeShard rootShard;
71
72     private ShardedDOMDataTree dataTreeService;
73     private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
74
75     private final ExecutorService executor = Executors.newSingleThreadExecutor();
76
77     @Captor
78     private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
79     @Captor
80     private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
81
82     private final ContainerNode crossShardContainer = createCrossShardContainer();
83
84     @Before
85     public void setUp() throws Exception {
86         MockitoAnnotations.initMocks(this);
87
88         rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1, 1);
89         rootShard.onGlobalContextUpdated(schemaContext);
90
91         final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
92         final DOMDataTreeProducer shardRegProducer = dataTree.createProducer(Collections.singletonList(ROOT_ID));
93         rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard, shardRegProducer);
94         shardRegProducer.close();
95
96         dataTreeService = dataTree;
97     }
98
99     @Test(expected = IllegalArgumentException.class)
100     public void testProducerPathContention() throws Exception {
101         final DOMDataTreeProducer p1 = dataTreeService.createProducer(Collections.singletonList(ROOT_ID));
102         final DOMDataTreeProducer p2 = dataTreeService.createProducer(Collections.singletonList(TEST_ID));
103     }
104
105     @Test
106     public void testSingleShardWrite() throws Exception {
107         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
108         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
109
110         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
111
112         final DOMDataTreeProducer producer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID));
113         DOMDataTreeCursorAwareTransaction tx = producer.createTransaction(false);
114         DOMDataTreeWriteCursor cursor = tx.createCursor(ROOT_ID);
115         assertNotNull(cursor);
116
117         cursor.write(TEST_ID.getRootIdentifier().getLastPathArgument(), crossShardContainer);
118
119         try {
120             tx.submit().checkedGet();
121             fail("There's still an open cursor");
122         } catch (final IllegalStateException e) {
123             assertTrue(e.getMessage().contains("cursor open"));
124         }
125
126         cursor.close();
127         tx.submit().checkedGet();
128
129         tx = producer.createTransaction(false);
130         cursor = tx.createCursor(TEST_ID);
131         assertNotNull(cursor);
132
133         cursor.delete(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
134         cursor.close();
135         tx.submit().checkedGet();
136
137         verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
138         final List<Collection<DataTreeCandidate>> capturedValue = captorForChanges.getAllValues();
139         assertTrue(capturedValue.size() == 2);
140
141         final ContainerNode capturedChange = (ContainerNode) capturedValue.get(0).iterator().next().getRootNode().getDataAfter().get();
142         final ContainerNode innerContainerVerify = (ContainerNode) crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get();
143         assertEquals(innerContainerVerify, capturedChange);
144
145         verifyNoMoreInteractions(mockedDataTreeListener);
146     }
147
148     @Test
149     public void testMultipleProducerCursorCreation() throws Exception {
150
151         final DOMDataTreeProducer rootProducer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID));
152         DOMDataTreeCursorAwareTransaction rootTx = rootProducer.createTransaction(false);
153         //check if we can create cursor where the new producer will be
154         DOMDataTreeWriteCursor rootTxCursor = rootTx.createCursor(INNER_CONTAINER_ID);
155         assertNotNull(rootTxCursor);
156         rootTxCursor.close();
157
158         try {
159             rootProducer.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
160             fail("Should've failed there is still a tx open");
161         } catch (final IllegalStateException e) {
162             assertTrue(e.getMessage().contains("open"));
163         }
164
165         assertTrue(rootTx.cancel());
166
167         final DOMDataTreeProducer innerContainerProducer = rootProducer.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
168
169         rootTx = rootProducer.createTransaction(false);
170         try {
171             rootTx.createCursor(INNER_CONTAINER_ID);
172             fail("Subtree should not be available to this producer");
173         } catch (final IllegalArgumentException e) {
174             assertTrue(e.getMessage().contains("delegated to child producer"));
175         }
176
177         rootTxCursor = rootTx.createCursor(TEST_ID);
178         assertNotNull(rootTxCursor);
179         try {
180             rootTxCursor.enter(INNER_CONTAINER_ID.getRootIdentifier().getLastPathArgument());
181             fail("Cursor should not have this subtree available");
182         } catch (final IllegalArgumentException e) {
183             assertTrue(e.getMessage().contains("not available to this cursor"));
184         }
185
186         try {
187             rootTxCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(),
188                     ImmutableContainerNodeBuilder.create()
189                             .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
190                             .build());
191             fail("Cursor should not have this subtree available");
192         } catch (final IllegalArgumentException e) {
193             assertTrue(e.getMessage().contains("not available to this cursor"));
194         }
195
196         final DOMDataTreeCursorAwareTransaction innerShardTx = innerContainerProducer.createTransaction(false);
197         final DOMDataTreeWriteCursor innerShardCursor = innerShardTx.createCursor(INNER_CONTAINER_ID);
198         assertNotNull(innerShardCursor);
199     }
200
201     private ContainerNode createCrossShardContainer() {
202         final LeafNode<String> shardedValue1 =
203                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
204         final LeafNode<String> shardedValue2 =
205                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
206
207
208         final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
209                 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
210                 .withChild(ImmutableLeafNodeBuilder.create()
211                         .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
212                         .withValue("testing-value")
213                         .build())
214                 .build();
215
216         final ContainerNode containerNode =
217                 ImmutableContainerNodeBuilder.create()
218                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
219                         .withChild(shardedValue1)
220                         .withChild(shardedValue2)
221                         .withChild(lowerShardContainer)
222                         .build();
223
224         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
225                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
226                 .withChild(containerNode)
227                 .build();
228
229         return testContainer;
230     }
231 }