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