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.assertNotNull;
12 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
13 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
15 import com.google.common.collect.ImmutableMap;
16 import com.google.common.util.concurrent.ForwardingExecutorService;
17 import com.google.common.util.concurrent.ListeningExecutorService;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.CountDownLatch;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.ExecutorService;
24 import java.util.concurrent.Executors;
25 import java.util.concurrent.TimeUnit;
26 import org.junit.After;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
34 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
35 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
36 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
37 import org.opendaylight.yangtools.concepts.ListenerRegistration;
38 import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
39 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
42 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
45 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
46 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
47 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
48 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
49 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
51 public class DOMDataTreeListenerTest extends AbstractDatastoreTest {
53 private AbstractDOMDataBroker domBroker;
54 private ListeningExecutorService executor;
55 private ExecutorService futureExecutor;
56 private CommitExecutorService commitExecutor;
58 private static final MapNode OUTER_LIST = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
59 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
62 private static final MapNode OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
63 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
66 private static final NormalizedNode TEST_CONTAINER = Builders.containerBuilder()
67 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
68 .withChild(OUTER_LIST)
71 private static final NormalizedNode TEST_CONTAINER_2 = Builders.containerBuilder()
72 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
73 .withChild(OUTER_LIST_2)
76 private static final DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
77 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
79 private static final DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
80 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
83 public void setupStore() {
84 final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
85 MoreExecutors.newDirectExecutorService());
86 final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
87 MoreExecutors.newDirectExecutorService());
89 operStore.onModelContextUpdated(SCHEMA_CONTEXT);
90 configStore.onModelContextUpdated(SCHEMA_CONTEXT);
92 final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
94 .put(CONFIGURATION, configStore)
95 .put(OPERATIONAL, operStore)
98 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
99 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB",
100 DOMDataTreeListenerTest.class);
101 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
102 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
103 domBroker = new SerializedDOMDataBroker(stores, executor);
107 public void tearDown() {
108 if (executor != null) {
109 executor.shutdownNow();
112 if (futureExecutor != null) {
113 futureExecutor.shutdownNow();
118 public void writeContainerEmptyTreeTest() throws InterruptedException {
119 final CountDownLatch latch = new CountDownLatch(1);
121 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
122 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
123 dataTreeChangeService);
125 final TestDataTreeListener listener = new TestDataTreeListener(latch);
126 final ListenerRegistration<TestDataTreeListener> listenerReg =
127 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
129 final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
130 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
133 latch.await(5, TimeUnit.SECONDS);
135 assertEquals(1, listener.getReceivedChanges().size());
136 final List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
137 assertEquals(1, changes.size());
139 final DataTreeCandidate candidate = changes.get(0);
140 assertNotNull(candidate);
141 final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
142 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
147 public void replaceContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
148 final CountDownLatch latch = new CountDownLatch(2);
150 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
151 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
152 dataTreeChangeService);
154 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
155 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
156 writeTx.commit().get();
158 final TestDataTreeListener listener = new TestDataTreeListener(latch);
159 final ListenerRegistration<TestDataTreeListener> listenerReg =
160 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
161 writeTx = domBroker.newWriteOnlyTransaction();
162 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
165 latch.await(5, TimeUnit.SECONDS);
167 assertEquals(2, listener.getReceivedChanges().size());
168 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
169 assertEquals(1, changes.size());
171 DataTreeCandidate candidate = changes.get(0);
172 assertNotNull(candidate);
173 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
174 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
176 changes = listener.getReceivedChanges().get(1);
177 assertEquals(1, changes.size());
179 candidate = changes.get(0);
180 assertNotNull(candidate);
181 candidateRoot = candidate.getRootNode();
182 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
187 public void deleteContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
188 final CountDownLatch latch = new CountDownLatch(2);
190 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
191 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
192 dataTreeChangeService);
194 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
195 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
196 writeTx.commit().get();
198 final TestDataTreeListener listener = new TestDataTreeListener(latch);
199 final ListenerRegistration<TestDataTreeListener> listenerReg =
200 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
202 writeTx = domBroker.newWriteOnlyTransaction();
203 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
206 latch.await(5, TimeUnit.SECONDS);
208 assertEquals(2, listener.getReceivedChanges().size());
209 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
210 assertEquals(1, changes.size());
212 DataTreeCandidate candidate = changes.get(0);
213 assertNotNull(candidate);
214 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
215 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
217 changes = listener.getReceivedChanges().get(1);
218 assertEquals(1, changes.size());
220 candidate = changes.get(0);
221 assertNotNull(candidate);
222 candidateRoot = candidate.getRootNode();
223 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
228 public void replaceChildListContainerInTreeTest() throws ExecutionException, InterruptedException {
229 final CountDownLatch latch = new CountDownLatch(2);
231 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
232 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
233 dataTreeChangeService);
235 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
236 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
237 writeTx.commit().get();
239 final TestDataTreeListener listener = new TestDataTreeListener(latch);
240 final ListenerRegistration<TestDataTreeListener> listenerReg =
241 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
243 writeTx = domBroker.newWriteOnlyTransaction();
244 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
247 latch.await(5, TimeUnit.SECONDS);
249 assertEquals(2, listener.getReceivedChanges().size());
250 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
251 assertEquals(1, changes.size());
253 DataTreeCandidate candidate = changes.get(0);
254 assertNotNull(candidate);
255 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
256 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
258 changes = listener.getReceivedChanges().get(1);
259 assertEquals(1, changes.size());
261 candidate = changes.get(0);
262 assertNotNull(candidate);
263 candidateRoot = candidate.getRootNode();
264 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
265 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
266 new NodeIdentifier(TestModel.OUTER_LIST_QNAME));
267 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
272 public void rootModificationChildListenerTest() throws ExecutionException, InterruptedException {
273 final CountDownLatch latch = new CountDownLatch(2);
275 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
276 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
277 dataTreeChangeService);
279 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
280 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
281 writeTx.commit().get();
283 final TestDataTreeListener listener = new TestDataTreeListener(latch);
284 final ListenerRegistration<TestDataTreeListener> listenerReg =
285 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
287 writeTx = domBroker.newWriteOnlyTransaction();
288 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
289 writeTx.commit().get();
291 latch.await(1, TimeUnit.SECONDS);
293 assertEquals(2, listener.getReceivedChanges().size());
294 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
295 assertEquals(1, changes.size());
297 DataTreeCandidate candidate = changes.get(0);
298 assertNotNull(candidate);
299 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
300 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
302 changes = listener.getReceivedChanges().get(1);
303 assertEquals(1, changes.size());
305 candidate = changes.get(0);
306 assertNotNull(candidate);
307 candidateRoot = candidate.getRootNode();
308 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
313 public void listEntryChangeNonRootRegistrationTest() throws ExecutionException, InterruptedException {
314 final CountDownLatch latch = new CountDownLatch(2);
316 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
317 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
318 dataTreeChangeService);
320 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
321 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
322 writeTx.commit().get();
324 final TestDataTreeListener listener = new TestDataTreeListener(latch);
325 final ListenerRegistration<TestDataTreeListener> listenerReg =
326 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
328 final NodeIdentifierWithPredicates outerListEntryId1 =
329 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
330 final NodeIdentifierWithPredicates outerListEntryId2 =
331 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
332 final NodeIdentifierWithPredicates outerListEntryId3 =
333 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
335 final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
336 final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
337 final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
339 final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
340 .withChild(outerListEntry2)
341 .withChild(outerListEntry3)
344 writeTx = domBroker.newWriteOnlyTransaction();
345 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
346 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
348 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
352 latch.await(5, TimeUnit.SECONDS);
354 assertEquals(2, listener.getReceivedChanges().size());
355 List<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
356 assertEquals(1, changes.size());
358 DataTreeCandidate candidate = changes.get(0);
359 assertNotNull(candidate);
360 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
361 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
363 changes = listener.getReceivedChanges().get(1);
364 assertEquals(1, changes.size());
366 candidate = changes.get(0);
367 assertNotNull(candidate);
368 candidateRoot = candidate.getRootNode();
369 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
370 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
371 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
372 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
373 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
374 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
375 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
379 private static void checkChange(final NormalizedNode expectedBefore, final NormalizedNode expectedAfter,
380 final ModificationType expectedMod, final DataTreeCandidateNode candidateNode) {
381 assertEquals(expectedBefore, candidateNode.dataBefore());
382 assertEquals(expectedAfter, candidateNode.dataAfter());
383 assertEquals(expectedMod, candidateNode.modificationType());
386 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
387 return domBroker.extension(DOMDataTreeChangeService.class);
390 static class CommitExecutorService extends ForwardingExecutorService {
392 ExecutorService delegate;
394 CommitExecutorService(final ExecutorService delegate) {
395 this.delegate = delegate;
399 protected ExecutorService delegate() {
404 static class TestDataTreeListener implements DOMDataTreeChangeListener {
405 private final List<List<DataTreeCandidate>> receivedChanges = new ArrayList<>();
406 private final CountDownLatch latch;
408 TestDataTreeListener(final CountDownLatch latch) {
413 public void onDataTreeChanged(final List<DataTreeCandidate> changes) {
414 receivedChanges.add(changes);
419 public void onInitialData() {
423 List<List<DataTreeCandidate>> getReceivedChanges() {
424 return receivedChanges;