2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.dom.broker;
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;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.util.concurrent.ForwardingExecutorService;
19 import com.google.common.util.concurrent.ListeningExecutorService;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.ExecutorService;
26 import java.util.concurrent.Executors;
27 import java.util.concurrent.TimeUnit;
28 import org.junit.After;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
32 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
34 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
37 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
38 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
41 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
44 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
48 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
49 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
50 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
51 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
53 public class DOMDataTreeListenerTest extends AbstractDatastoreTest {
55 private AbstractDOMDataBroker domBroker;
56 private ListeningExecutorService executor;
57 private ExecutorService futureExecutor;
58 private CommitExecutorService commitExecutor;
60 private static final MapNode OUTER_LIST = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
61 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
64 private static final MapNode OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
65 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
68 private static final NormalizedNode TEST_CONTAINER = Builders.containerBuilder()
69 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
70 .withChild(OUTER_LIST)
73 private static final NormalizedNode TEST_CONTAINER_2 = Builders.containerBuilder()
74 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
75 .withChild(OUTER_LIST_2)
78 private static final DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
79 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
81 private static final DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
82 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
85 public void setupStore() {
86 final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
87 MoreExecutors.newDirectExecutorService());
88 final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
89 MoreExecutors.newDirectExecutorService());
91 operStore.onModelContextUpdated(SCHEMA_CONTEXT);
92 configStore.onModelContextUpdated(SCHEMA_CONTEXT);
94 final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
96 .put(CONFIGURATION, configStore)
97 .put(OPERATIONAL, operStore)
100 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
101 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB",
102 DOMDataTreeListenerTest.class);
103 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
104 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
105 domBroker = new SerializedDOMDataBroker(stores, executor);
109 public void tearDown() {
110 if (executor != null) {
111 executor.shutdownNow();
114 if (futureExecutor != null) {
115 futureExecutor.shutdownNow();
120 public void writeContainerEmptyTreeTest() throws InterruptedException {
121 final CountDownLatch latch = new CountDownLatch(1);
123 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
124 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
125 dataTreeChangeService);
127 final TestDataTreeListener listener = new TestDataTreeListener(latch);
128 final ListenerRegistration<TestDataTreeListener> listenerReg =
129 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
131 final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
132 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
135 latch.await(5, TimeUnit.SECONDS);
137 assertEquals(1, listener.getReceivedChanges().size());
138 final List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
139 assertEquals(1, changes.size());
141 final DataTreeCandidate candidate = changes.get(0);
142 assertNotNull(candidate);
143 final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
144 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
149 public void replaceContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
150 final CountDownLatch latch = new CountDownLatch(2);
152 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
153 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
154 dataTreeChangeService);
156 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
157 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
158 writeTx.commit().get();
160 final TestDataTreeListener listener = new TestDataTreeListener(latch);
161 final ListenerRegistration<TestDataTreeListener> listenerReg =
162 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
163 writeTx = domBroker.newWriteOnlyTransaction();
164 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
167 latch.await(5, TimeUnit.SECONDS);
169 assertEquals(2, listener.getReceivedChanges().size());
170 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
171 assertEquals(1, changes.size());
173 DataTreeCandidate candidate = changes.get(0);
174 assertNotNull(candidate);
175 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
176 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
178 changes = listener.getReceivedChanges().get(1);
179 assertEquals(1, changes.size());
181 candidate = changes.get(0);
182 assertNotNull(candidate);
183 candidateRoot = candidate.getRootNode();
184 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
189 public void deleteContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
190 final CountDownLatch latch = new CountDownLatch(2);
192 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
193 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
194 dataTreeChangeService);
196 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
197 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
198 writeTx.commit().get();
200 final TestDataTreeListener listener = new TestDataTreeListener(latch);
201 final ListenerRegistration<TestDataTreeListener> listenerReg =
202 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
204 writeTx = domBroker.newWriteOnlyTransaction();
205 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
208 latch.await(5, TimeUnit.SECONDS);
210 assertEquals(2, listener.getReceivedChanges().size());
211 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
212 assertEquals(1, changes.size());
214 DataTreeCandidate candidate = changes.get(0);
215 assertNotNull(candidate);
216 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
217 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
219 changes = listener.getReceivedChanges().get(1);
220 assertEquals(1, changes.size());
222 candidate = changes.get(0);
223 assertNotNull(candidate);
224 candidateRoot = candidate.getRootNode();
225 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
230 public void replaceChildListContainerInTreeTest() throws ExecutionException, InterruptedException {
231 final CountDownLatch latch = new CountDownLatch(2);
233 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
234 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
235 dataTreeChangeService);
237 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
238 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
239 writeTx.commit().get();
241 final TestDataTreeListener listener = new TestDataTreeListener(latch);
242 final ListenerRegistration<TestDataTreeListener> listenerReg =
243 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
245 writeTx = domBroker.newWriteOnlyTransaction();
246 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
249 latch.await(5, TimeUnit.SECONDS);
251 assertEquals(2, listener.getReceivedChanges().size());
252 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
253 assertEquals(1, changes.size());
255 DataTreeCandidate candidate = changes.get(0);
256 assertNotNull(candidate);
257 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
258 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
260 changes = listener.getReceivedChanges().get(1);
261 assertEquals(1, changes.size());
263 candidate = changes.get(0);
264 assertNotNull(candidate);
265 candidateRoot = candidate.getRootNode();
266 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
267 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
268 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME)).get();
269 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
274 public void rootModificationChildListenerTest() throws ExecutionException, InterruptedException {
275 final CountDownLatch latch = new CountDownLatch(2);
277 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
278 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
279 dataTreeChangeService);
281 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
282 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
283 writeTx.commit().get();
285 final TestDataTreeListener listener = new TestDataTreeListener(latch);
286 final ListenerRegistration<TestDataTreeListener> listenerReg =
287 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
289 writeTx = domBroker.newWriteOnlyTransaction();
290 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
291 writeTx.commit().get();
293 latch.await(1, TimeUnit.SECONDS);
295 assertEquals(2, listener.getReceivedChanges().size());
296 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
297 assertEquals(1, changes.size());
299 DataTreeCandidate candidate = changes.get(0);
300 assertNotNull(candidate);
301 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
302 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
304 changes = listener.getReceivedChanges().get(1);
305 assertEquals(1, changes.size());
307 candidate = changes.get(0);
308 assertNotNull(candidate);
309 candidateRoot = candidate.getRootNode();
310 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
315 public void listEntryChangeNonRootRegistrationTest() throws ExecutionException, InterruptedException {
316 final CountDownLatch latch = new CountDownLatch(2);
318 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
319 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
320 dataTreeChangeService);
322 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
323 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
324 writeTx.commit().get();
326 final TestDataTreeListener listener = new TestDataTreeListener(latch);
327 final ListenerRegistration<TestDataTreeListener> listenerReg =
328 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
330 final NodeIdentifierWithPredicates outerListEntryId1 =
331 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
332 final NodeIdentifierWithPredicates outerListEntryId2 =
333 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
334 final NodeIdentifierWithPredicates outerListEntryId3 =
335 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
337 final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
338 final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
339 final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
341 final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
342 .withChild(outerListEntry2)
343 .withChild(outerListEntry3)
346 writeTx = domBroker.newWriteOnlyTransaction();
347 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
348 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
350 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
354 latch.await(5, TimeUnit.SECONDS);
356 assertEquals(2, listener.getReceivedChanges().size());
357 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
358 assertEquals(1, changes.size());
360 DataTreeCandidate candidate = changes.get(0);
361 assertNotNull(candidate);
362 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
363 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
365 changes = listener.getReceivedChanges().get(1);
366 assertEquals(1, changes.size());
368 candidate = changes.get(0);
369 assertNotNull(candidate);
370 candidateRoot = candidate.getRootNode();
371 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
372 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1).get();
373 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
374 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2).get();
375 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
376 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3).get();
377 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
381 private static void checkChange(final NormalizedNode expectedBefore, final NormalizedNode expectedAfter,
382 final ModificationType expectedMod, final DataTreeCandidateNode candidateNode) {
383 if (expectedBefore != null) {
384 assertTrue(candidateNode.getDataBefore().isPresent());
385 assertEquals(expectedBefore, candidateNode.getDataBefore().get());
387 assertFalse(candidateNode.getDataBefore().isPresent());
390 if (expectedAfter != null) {
391 assertTrue(candidateNode.getDataAfter().isPresent());
392 assertEquals(expectedAfter, candidateNode.getDataAfter().get());
394 assertFalse(candidateNode.getDataAfter().isPresent());
397 assertEquals(expectedMod, candidateNode.getModificationType());
400 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
401 return domBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
404 static class CommitExecutorService extends ForwardingExecutorService {
406 ExecutorService delegate;
408 CommitExecutorService(final ExecutorService delegate) {
409 this.delegate = delegate;
413 protected ExecutorService delegate() {
418 static class TestDataTreeListener implements DOMDataTreeChangeListener {
419 private final List<List<DataTreeCandidate>> receivedChanges = new ArrayList<>();
420 private final CountDownLatch latch;
422 TestDataTreeListener(final CountDownLatch latch) {
427 public void onDataTreeChanged(final List<DataTreeCandidate> changes) {
428 receivedChanges.add(changes);
433 public void onInitialData() {
437 List<List<DataTreeCandidate>> getReceivedChanges() {
438 return receivedChanges;