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