checkStyleViolationSeverity=error implemented for mdsal-dom-broker
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / DOMDataTreeListenerTest.java
1 /*
2  * Copyright (c) 2015 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
9 package org.opendaylight.mdsal.dom.broker;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
16 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
17
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.util.concurrent.ForwardingExecutorService;
20 import com.google.common.util.concurrent.ListeningExecutorService;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.List;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.TimeUnit;
29 import javax.annotation.Nonnull;
30 import org.junit.After;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
34 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
35 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
36 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
41 import org.opendaylight.mdsal.dom.broker.util.TestModel;
42 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
43 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
44 import org.opendaylight.yangtools.concepts.ListenerRegistration;
45 import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
46 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
49 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
53 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
55 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
56 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58
59 public class DOMDataTreeListenerTest {
60
61     private SchemaContext schemaContext;
62     private AbstractDOMDataBroker domBroker;
63     private ListeningExecutorService executor;
64     private ExecutorService futureExecutor;
65     private CommitExecutorService commitExecutor;
66
67     private static final DataContainerChild<?, ?> OUTER_LIST =
68             ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
69             .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
70             .build();
71
72     private static final DataContainerChild<?, ?> OUTER_LIST_2 =
73             ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
74             .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
75             .build();
76
77     private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
78             .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
79             .withChild(OUTER_LIST)
80             .build();
81
82     private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
83             .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
84             .withChild(OUTER_LIST_2)
85             .build();
86
87     private static final DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
88             LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
89
90     private static final DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
91             LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
92
93     @Before
94     public void setupStore() throws Exception {
95         final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
96                 MoreExecutors.newDirectExecutorService());
97         final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
98                 MoreExecutors.newDirectExecutorService());
99         schemaContext = TestModel.createTestContext();
100
101         operStore.onGlobalContextUpdated(schemaContext);
102         configStore.onGlobalContextUpdated(schemaContext);
103
104         final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
105                 DOMStore>builder()
106                 .put(CONFIGURATION, configStore)
107                 .put(OPERATIONAL, operStore)
108                 .build();
109
110         commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
111         futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
112         executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
113                 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
114         domBroker = new SerializedDOMDataBroker(stores, executor);
115     }
116
117     @After
118     public void tearDown() {
119         if (executor != null) {
120             executor.shutdownNow();
121         }
122
123         if (futureExecutor != null) {
124             futureExecutor.shutdownNow();
125         }
126     }
127
128     @Test
129     public void writeContainerEmptyTreeTest() throws InterruptedException {
130         final CountDownLatch latch = new CountDownLatch(1);
131
132         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
133         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
134                 dataTreeChangeService);
135
136         final TestDataTreeListener listener = new TestDataTreeListener(latch);
137         final ListenerRegistration<TestDataTreeListener> listenerReg =
138                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
139
140         final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
141         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
142         writeTx.submit();
143
144         latch.await(5, TimeUnit.SECONDS);
145
146         assertEquals(1, listener.getReceivedChanges().size());
147         final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
148         assertEquals(1, changes.size());
149
150         final DataTreeCandidate candidate = changes.iterator().next();
151         assertNotNull(candidate);
152         final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
153         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
154         listenerReg.close();
155     }
156
157     @Test
158     public void replaceContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
159         final CountDownLatch latch = new CountDownLatch(2);
160
161         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
162         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
163                 dataTreeChangeService);
164
165         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
166         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
167         writeTx.submit().checkedGet();
168
169         final TestDataTreeListener listener = new TestDataTreeListener(latch);
170         final ListenerRegistration<TestDataTreeListener> listenerReg =
171                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
172         writeTx = domBroker.newWriteOnlyTransaction();
173         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
174         writeTx.submit();
175
176         latch.await(5, TimeUnit.SECONDS);
177
178         assertEquals(2, listener.getReceivedChanges().size());
179         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
180         assertEquals(1, changes.size());
181
182         DataTreeCandidate candidate = changes.iterator().next();
183         assertNotNull(candidate);
184         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
185         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
186
187         changes = listener.getReceivedChanges().get(1);
188         assertEquals(1, changes.size());
189
190         candidate = changes.iterator().next();
191         assertNotNull(candidate);
192         candidateRoot = candidate.getRootNode();
193         checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
194         listenerReg.close();
195     }
196
197     @Test
198     public void deleteContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
199         final CountDownLatch latch = new CountDownLatch(2);
200
201         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
202         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
203                 dataTreeChangeService);
204
205         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
206         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
207         writeTx.submit().checkedGet();
208
209         final TestDataTreeListener listener = new TestDataTreeListener(latch);
210         final ListenerRegistration<TestDataTreeListener> listenerReg =
211                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
212
213         writeTx = domBroker.newWriteOnlyTransaction();
214         writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
215         writeTx.submit();
216
217         latch.await(5, TimeUnit.SECONDS);
218
219         assertEquals(2, listener.getReceivedChanges().size());
220         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
221         assertEquals(1, changes.size());
222
223         DataTreeCandidate candidate = changes.iterator().next();
224         assertNotNull(candidate);
225         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
226         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
227
228         changes = listener.getReceivedChanges().get(1);
229         assertEquals(1, changes.size());
230
231         candidate = changes.iterator().next();
232         assertNotNull(candidate);
233         candidateRoot = candidate.getRootNode();
234         checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
235         listenerReg.close();
236     }
237
238     @Test
239     public void replaceChildListContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
240         final CountDownLatch latch = new CountDownLatch(2);
241
242         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
243         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
244                 dataTreeChangeService);
245
246         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
247         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
248         writeTx.submit().checkedGet();
249
250         final TestDataTreeListener listener = new TestDataTreeListener(latch);
251         final ListenerRegistration<TestDataTreeListener> listenerReg =
252                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
253
254         writeTx = domBroker.newWriteOnlyTransaction();
255         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
256         writeTx.submit();
257
258         latch.await(5, TimeUnit.SECONDS);
259
260         assertEquals(2, listener.getReceivedChanges().size());
261         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
262         assertEquals(1, changes.size());
263
264         DataTreeCandidate candidate = changes.iterator().next();
265         assertNotNull(candidate);
266         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
267         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
268
269         changes = listener.getReceivedChanges().get(1);
270         assertEquals(1, changes.size());
271
272         candidate = changes.iterator().next();
273         assertNotNull(candidate);
274         candidateRoot = candidate.getRootNode();
275         checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
276         final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
277                 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
278         assertNotNull(modifiedChild);
279         checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
280         listenerReg.close();
281     }
282
283     @Test
284     public void rootModificationChildListenerTest() throws InterruptedException, TransactionCommitFailedException {
285         final CountDownLatch latch = new CountDownLatch(2);
286
287         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
288         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
289                 dataTreeChangeService);
290
291         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
292         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
293         writeTx.submit().checkedGet();
294
295         final TestDataTreeListener listener = new TestDataTreeListener(latch);
296         final ListenerRegistration<TestDataTreeListener> listenerReg =
297                 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
298
299         writeTx = domBroker.newWriteOnlyTransaction();
300         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
301         writeTx.submit().checkedGet();
302
303         latch.await(1, TimeUnit.SECONDS);
304
305         assertEquals(2, listener.getReceivedChanges().size());
306         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
307         assertEquals(1, changes.size());
308
309         DataTreeCandidate candidate = changes.iterator().next();
310         assertNotNull(candidate);
311         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
312         checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
313
314         changes = listener.getReceivedChanges().get(1);
315         assertEquals(1, changes.size());
316
317         candidate = changes.iterator().next();
318         assertNotNull(candidate);
319         candidateRoot = candidate.getRootNode();
320         checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
321         listenerReg.close();
322     }
323
324     @Test
325     public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, TransactionCommitFailedException {
326         final CountDownLatch latch = new CountDownLatch(2);
327
328         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
329         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
330                 dataTreeChangeService);
331
332         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
333         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
334         writeTx.submit().checkedGet();
335
336         final TestDataTreeListener listener = new TestDataTreeListener(latch);
337         final ListenerRegistration<TestDataTreeListener> listenerReg =
338                 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
339
340         final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
341                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
342                         TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
343         final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
344                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
345                         TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
346         final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
347                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
348                         TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
349
350         final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
351         final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
352         final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
353
354         final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
355                 .withChild(outerListEntry2)
356                 .withChild(outerListEntry3)
357                 .build();
358
359         writeTx = domBroker.newWriteOnlyTransaction();
360         writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
361         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
362                 outerListEntry2);
363         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
364                 outerListEntry3);
365         writeTx.submit();
366
367         latch.await(5, TimeUnit.SECONDS);
368
369         assertEquals(2, listener.getReceivedChanges().size());
370         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
371         assertEquals(1, changes.size());
372
373         DataTreeCandidate candidate = changes.iterator().next();
374         assertNotNull(candidate);
375         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
376         checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
377
378         changes = listener.getReceivedChanges().get(1);
379         assertEquals(1, changes.size());
380
381         candidate = changes.iterator().next();
382         assertNotNull(candidate);
383         candidateRoot = candidate.getRootNode();
384         checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
385         final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
386         checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
387         final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
388         checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
389         final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
390         checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
391         listenerReg.close();
392     }
393
394     private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
395                                     final NormalizedNode<?, ?> expectedAfter,
396                                     final ModificationType expectedMod,
397                                     final DataTreeCandidateNode candidateNode) {
398         if (expectedBefore != null) {
399             assertTrue(candidateNode.getDataBefore().isPresent());
400             assertEquals(expectedBefore, candidateNode.getDataBefore().get());
401         } else {
402             assertFalse(candidateNode.getDataBefore().isPresent());
403         }
404
405         if (expectedAfter != null) {
406             assertTrue(candidateNode.getDataAfter().isPresent());
407             assertEquals(expectedAfter, candidateNode.getDataAfter().get());
408         } else {
409             assertFalse(candidateNode.getDataAfter().isPresent());
410         }
411
412         assertEquals(expectedMod, candidateNode.getModificationType());
413     }
414
415     private DOMDataTreeChangeService getDOMDataTreeChangeService() {
416         final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
417                 .get(DOMDataTreeChangeService.class);
418         if (extension == null) {
419             return null;
420         }
421         DOMDataTreeChangeService dataTreeChangeService = null;
422         if (extension instanceof DOMDataTreeChangeService) {
423             dataTreeChangeService = (DOMDataTreeChangeService) extension;
424         }
425         return dataTreeChangeService;
426     }
427
428     static class CommitExecutorService extends ForwardingExecutorService {
429
430         ExecutorService delegate;
431
432         CommitExecutorService(final ExecutorService delegate) {
433             this.delegate = delegate;
434         }
435
436         @Override
437         protected ExecutorService delegate() {
438             return delegate;
439         }
440     }
441
442     static class TestDataTreeListener implements DOMDataTreeChangeListener {
443
444         private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
445         private final CountDownLatch latch;
446
447         TestDataTreeListener(final CountDownLatch latch) {
448             this.latch = latch;
449         }
450
451         @Override
452         public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
453             receivedChanges.add(changes);
454             latch.countDown();
455         }
456
457         public List<Collection<DataTreeCandidate>> getReceivedChanges() {
458             return receivedChanges;
459         }
460     }
461 }