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