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.Collection;
23 import java.util.List;
24 import java.util.concurrent.CountDownLatch;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.TimeUnit;
29 import org.junit.After;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
33 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
34 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
38 import org.opendaylight.mdsal.dom.broker.util.TestModel;
39 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
40 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
41 import org.opendaylight.yangtools.concepts.ListenerRegistration;
42 import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
43 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
46 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
50 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
52 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
53 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
55 public class DOMDataTreeListenerTest extends AbstractDatastoreTest {
57 private AbstractDOMDataBroker domBroker;
58 private ListeningExecutorService executor;
59 private ExecutorService futureExecutor;
60 private CommitExecutorService commitExecutor;
62 private static final MapNode OUTER_LIST = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
63 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
66 private static final MapNode OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
67 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
70 private static final NormalizedNode TEST_CONTAINER = Builders.containerBuilder()
71 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
72 .withChild(OUTER_LIST)
75 private static final NormalizedNode TEST_CONTAINER_2 = Builders.containerBuilder()
76 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
77 .withChild(OUTER_LIST_2)
80 private static final DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
81 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
83 private static final DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
84 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
87 public void setupStore() {
88 final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
89 MoreExecutors.newDirectExecutorService());
90 final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
91 MoreExecutors.newDirectExecutorService());
93 operStore.onModelContextUpdated(SCHEMA_CONTEXT);
94 configStore.onModelContextUpdated(SCHEMA_CONTEXT);
96 final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
98 .put(CONFIGURATION, configStore)
99 .put(OPERATIONAL, operStore)
102 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
103 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB",
104 DOMDataTreeListenerTest.class);
105 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
106 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
107 domBroker = new SerializedDOMDataBroker(stores, executor);
111 public void tearDown() {
112 if (executor != null) {
113 executor.shutdownNow();
116 if (futureExecutor != null) {
117 futureExecutor.shutdownNow();
122 public void writeContainerEmptyTreeTest() throws InterruptedException {
123 final CountDownLatch latch = new CountDownLatch(1);
125 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
126 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
127 dataTreeChangeService);
129 final TestDataTreeListener listener = new TestDataTreeListener(latch);
130 final ListenerRegistration<TestDataTreeListener> listenerReg =
131 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
133 final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
134 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
137 latch.await(5, TimeUnit.SECONDS);
139 assertEquals(1, listener.getReceivedChanges().size());
140 final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
141 assertEquals(1, changes.size());
143 final DataTreeCandidate candidate = changes.iterator().next();
144 assertNotNull(candidate);
145 final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
146 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
151 public void replaceContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
152 final CountDownLatch latch = new CountDownLatch(2);
154 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
155 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
156 dataTreeChangeService);
158 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
159 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
160 writeTx.commit().get();
162 final TestDataTreeListener listener = new TestDataTreeListener(latch);
163 final ListenerRegistration<TestDataTreeListener> listenerReg =
164 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
165 writeTx = domBroker.newWriteOnlyTransaction();
166 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
169 latch.await(5, TimeUnit.SECONDS);
171 assertEquals(2, listener.getReceivedChanges().size());
172 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
173 assertEquals(1, changes.size());
175 DataTreeCandidate candidate = changes.iterator().next();
176 assertNotNull(candidate);
177 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
178 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
180 changes = listener.getReceivedChanges().get(1);
181 assertEquals(1, changes.size());
183 candidate = changes.iterator().next();
184 assertNotNull(candidate);
185 candidateRoot = candidate.getRootNode();
186 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
191 public void deleteContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
192 final CountDownLatch latch = new CountDownLatch(2);
194 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
195 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
196 dataTreeChangeService);
198 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
199 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
200 writeTx.commit().get();
202 final TestDataTreeListener listener = new TestDataTreeListener(latch);
203 final ListenerRegistration<TestDataTreeListener> listenerReg =
204 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
206 writeTx = domBroker.newWriteOnlyTransaction();
207 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
210 latch.await(5, TimeUnit.SECONDS);
212 assertEquals(2, listener.getReceivedChanges().size());
213 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
214 assertEquals(1, changes.size());
216 DataTreeCandidate candidate = changes.iterator().next();
217 assertNotNull(candidate);
218 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
219 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
221 changes = listener.getReceivedChanges().get(1);
222 assertEquals(1, changes.size());
224 candidate = changes.iterator().next();
225 assertNotNull(candidate);
226 candidateRoot = candidate.getRootNode();
227 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
232 public void replaceChildListContainerInTreeTest() throws ExecutionException, InterruptedException {
233 final CountDownLatch latch = new CountDownLatch(2);
235 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
236 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
237 dataTreeChangeService);
239 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
240 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
241 writeTx.commit().get();
243 final TestDataTreeListener listener = new TestDataTreeListener(latch);
244 final ListenerRegistration<TestDataTreeListener> listenerReg =
245 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
247 writeTx = domBroker.newWriteOnlyTransaction();
248 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
251 latch.await(5, TimeUnit.SECONDS);
253 assertEquals(2, listener.getReceivedChanges().size());
254 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
255 assertEquals(1, changes.size());
257 DataTreeCandidate candidate = changes.iterator().next();
258 assertNotNull(candidate);
259 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
260 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
262 changes = listener.getReceivedChanges().get(1);
263 assertEquals(1, changes.size());
265 candidate = changes.iterator().next();
266 assertNotNull(candidate);
267 candidateRoot = candidate.getRootNode();
268 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
269 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
270 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME)).get();
271 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
276 public void rootModificationChildListenerTest() throws ExecutionException, InterruptedException {
277 final CountDownLatch latch = new CountDownLatch(2);
279 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
280 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
281 dataTreeChangeService);
283 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
284 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
285 writeTx.commit().get();
287 final TestDataTreeListener listener = new TestDataTreeListener(latch);
288 final ListenerRegistration<TestDataTreeListener> listenerReg =
289 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
291 writeTx = domBroker.newWriteOnlyTransaction();
292 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
293 writeTx.commit().get();
295 latch.await(1, TimeUnit.SECONDS);
297 assertEquals(2, listener.getReceivedChanges().size());
298 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
299 assertEquals(1, changes.size());
301 DataTreeCandidate candidate = changes.iterator().next();
302 assertNotNull(candidate);
303 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
304 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
306 changes = listener.getReceivedChanges().get(1);
307 assertEquals(1, changes.size());
309 candidate = changes.iterator().next();
310 assertNotNull(candidate);
311 candidateRoot = candidate.getRootNode();
312 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
317 public void listEntryChangeNonRootRegistrationTest() throws ExecutionException, InterruptedException {
318 final CountDownLatch latch = new CountDownLatch(2);
320 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
321 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
322 dataTreeChangeService);
324 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
325 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
326 writeTx.commit().get();
328 final TestDataTreeListener listener = new TestDataTreeListener(latch);
329 final ListenerRegistration<TestDataTreeListener> listenerReg =
330 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
332 final NodeIdentifierWithPredicates outerListEntryId1 =
333 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
334 final NodeIdentifierWithPredicates outerListEntryId2 =
335 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
336 final NodeIdentifierWithPredicates outerListEntryId3 =
337 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
339 final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
340 final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
341 final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
343 final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
344 .withChild(outerListEntry2)
345 .withChild(outerListEntry3)
348 writeTx = domBroker.newWriteOnlyTransaction();
349 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
350 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
352 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
356 latch.await(5, TimeUnit.SECONDS);
358 assertEquals(2, listener.getReceivedChanges().size());
359 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
360 assertEquals(1, changes.size());
362 DataTreeCandidate candidate = changes.iterator().next();
363 assertNotNull(candidate);
364 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
365 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
367 changes = listener.getReceivedChanges().get(1);
368 assertEquals(1, changes.size());
370 candidate = changes.iterator().next();
371 assertNotNull(candidate);
372 candidateRoot = candidate.getRootNode();
373 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
374 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1).get();
375 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
376 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2).get();
377 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
378 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3).get();
379 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
383 private static void checkChange(final NormalizedNode expectedBefore, final NormalizedNode expectedAfter,
384 final ModificationType expectedMod, final DataTreeCandidateNode candidateNode) {
385 if (expectedBefore != null) {
386 assertTrue(candidateNode.getDataBefore().isPresent());
387 assertEquals(expectedBefore, candidateNode.getDataBefore().get());
389 assertFalse(candidateNode.getDataBefore().isPresent());
392 if (expectedAfter != null) {
393 assertTrue(candidateNode.getDataAfter().isPresent());
394 assertEquals(expectedAfter, candidateNode.getDataAfter().get());
396 assertFalse(candidateNode.getDataAfter().isPresent());
399 assertEquals(expectedMod, candidateNode.getModificationType());
402 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
403 return domBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
406 static class CommitExecutorService extends ForwardingExecutorService {
408 ExecutorService delegate;
410 CommitExecutorService(final ExecutorService delegate) {
411 this.delegate = delegate;
415 protected ExecutorService delegate() {
420 static class TestDataTreeListener implements DOMDataTreeChangeListener {
421 private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
422 private final CountDownLatch latch;
424 TestDataTreeListener(final CountDownLatch latch) {
429 public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
430 receivedChanges.add(changes);
435 public void onInitialData() {
439 List<Collection<DataTreeCandidate>> getReceivedChanges() {
440 return receivedChanges;