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
9 package org.opendaylight.mdsal.dom.broker;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
16 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.util.concurrent.ForwardingExecutorService;
20 import com.google.common.util.concurrent.ListeningExecutorService;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.List;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 import java.util.concurrent.TimeUnit;
30 import javax.annotation.Nonnull;
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
35 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
36 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
41 import org.opendaylight.mdsal.dom.broker.util.TestModel;
42 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
43 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
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;
59 public class DOMDataTreeListenerTest {
61 private SchemaContext schemaContext;
62 private AbstractDOMDataBroker domBroker;
63 private ListeningExecutorService executor;
64 private ExecutorService futureExecutor;
65 private CommitExecutorService commitExecutor;
67 private static final DataContainerChild<?, ?> OUTER_LIST =
68 ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
69 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
72 private static final DataContainerChild<?, ?> OUTER_LIST_2 =
73 ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
74 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
77 private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
78 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
79 .withChild(OUTER_LIST)
82 private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
83 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
84 .withChild(OUTER_LIST_2)
87 private static final DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
88 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
90 private static final DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
91 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
94 public void setupStore() {
95 final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
96 MoreExecutors.newDirectExecutorService());
97 final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
98 MoreExecutors.newDirectExecutorService());
99 schemaContext = TestModel.createTestContext();
101 operStore.onGlobalContextUpdated(schemaContext);
102 configStore.onGlobalContextUpdated(schemaContext);
104 final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
106 .put(CONFIGURATION, configStore)
107 .put(OPERATIONAL, operStore)
110 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
111 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB",
112 DOMDataTreeListenerTest.class);
113 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
114 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
115 domBroker = new SerializedDOMDataBroker(stores, executor);
119 public void tearDown() {
120 if (executor != null) {
121 executor.shutdownNow();
124 if (futureExecutor != null) {
125 futureExecutor.shutdownNow();
130 public void writeContainerEmptyTreeTest() throws InterruptedException {
131 final CountDownLatch latch = new CountDownLatch(1);
133 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
134 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
135 dataTreeChangeService);
137 final TestDataTreeListener listener = new TestDataTreeListener(latch);
138 final ListenerRegistration<TestDataTreeListener> listenerReg =
139 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
141 final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
142 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
145 latch.await(5, TimeUnit.SECONDS);
147 assertEquals(1, listener.getReceivedChanges().size());
148 final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
149 assertEquals(1, changes.size());
151 final DataTreeCandidate candidate = changes.iterator().next();
152 assertNotNull(candidate);
153 final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
154 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
159 public void replaceContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
160 final CountDownLatch latch = new CountDownLatch(2);
162 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
163 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
164 dataTreeChangeService);
166 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
167 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
168 writeTx.commit().get();
170 final TestDataTreeListener listener = new TestDataTreeListener(latch);
171 final ListenerRegistration<TestDataTreeListener> listenerReg =
172 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
173 writeTx = domBroker.newWriteOnlyTransaction();
174 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
177 latch.await(5, TimeUnit.SECONDS);
179 assertEquals(2, listener.getReceivedChanges().size());
180 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
181 assertEquals(1, changes.size());
183 DataTreeCandidate candidate = changes.iterator().next();
184 assertNotNull(candidate);
185 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
186 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
188 changes = listener.getReceivedChanges().get(1);
189 assertEquals(1, changes.size());
191 candidate = changes.iterator().next();
192 assertNotNull(candidate);
193 candidateRoot = candidate.getRootNode();
194 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
199 public void deleteContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
200 final CountDownLatch latch = new CountDownLatch(2);
202 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
203 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
204 dataTreeChangeService);
206 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
207 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
208 writeTx.commit().get();
210 final TestDataTreeListener listener = new TestDataTreeListener(latch);
211 final ListenerRegistration<TestDataTreeListener> listenerReg =
212 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
214 writeTx = domBroker.newWriteOnlyTransaction();
215 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
218 latch.await(5, TimeUnit.SECONDS);
220 assertEquals(2, listener.getReceivedChanges().size());
221 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
222 assertEquals(1, changes.size());
224 DataTreeCandidate candidate = changes.iterator().next();
225 assertNotNull(candidate);
226 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
227 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
229 changes = listener.getReceivedChanges().get(1);
230 assertEquals(1, changes.size());
232 candidate = changes.iterator().next();
233 assertNotNull(candidate);
234 candidateRoot = candidate.getRootNode();
235 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
240 public void replaceChildListContainerInTreeTest() throws ExecutionException, InterruptedException {
241 final CountDownLatch latch = new CountDownLatch(2);
243 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
244 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
245 dataTreeChangeService);
247 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
248 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
249 writeTx.commit().get();
251 final TestDataTreeListener listener = new TestDataTreeListener(latch);
252 final ListenerRegistration<TestDataTreeListener> listenerReg =
253 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
255 writeTx = domBroker.newWriteOnlyTransaction();
256 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
259 latch.await(5, TimeUnit.SECONDS);
261 assertEquals(2, listener.getReceivedChanges().size());
262 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
263 assertEquals(1, changes.size());
265 DataTreeCandidate candidate = changes.iterator().next();
266 assertNotNull(candidate);
267 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
268 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
270 changes = listener.getReceivedChanges().get(1);
271 assertEquals(1, changes.size());
273 candidate = changes.iterator().next();
274 assertNotNull(candidate);
275 candidateRoot = candidate.getRootNode();
276 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
277 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
278 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
279 assertNotNull(modifiedChild);
280 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
285 public void rootModificationChildListenerTest() throws ExecutionException, InterruptedException {
286 final CountDownLatch latch = new CountDownLatch(2);
288 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
289 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
290 dataTreeChangeService);
292 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
293 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
294 writeTx.commit().get();
296 final TestDataTreeListener listener = new TestDataTreeListener(latch);
297 final ListenerRegistration<TestDataTreeListener> listenerReg =
298 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
300 writeTx = domBroker.newWriteOnlyTransaction();
301 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
302 writeTx.commit().get();
304 latch.await(1, TimeUnit.SECONDS);
306 assertEquals(2, listener.getReceivedChanges().size());
307 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
308 assertEquals(1, changes.size());
310 DataTreeCandidate candidate = changes.iterator().next();
311 assertNotNull(candidate);
312 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
313 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
315 changes = listener.getReceivedChanges().get(1);
316 assertEquals(1, changes.size());
318 candidate = changes.iterator().next();
319 assertNotNull(candidate);
320 candidateRoot = candidate.getRootNode();
321 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
326 public void listEntryChangeNonRootRegistrationTest() throws ExecutionException, InterruptedException {
327 final CountDownLatch latch = new CountDownLatch(2);
329 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
330 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
331 dataTreeChangeService);
333 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
334 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
335 writeTx.commit().get();
337 final TestDataTreeListener listener = new TestDataTreeListener(latch);
338 final ListenerRegistration<TestDataTreeListener> listenerReg =
339 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
341 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
342 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
343 TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
344 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
345 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
346 TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
347 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
348 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
349 TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
351 final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
352 final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
353 final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
355 final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
356 .withChild(outerListEntry2)
357 .withChild(outerListEntry3)
360 writeTx = domBroker.newWriteOnlyTransaction();
361 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
362 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
364 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
368 latch.await(5, TimeUnit.SECONDS);
370 assertEquals(2, listener.getReceivedChanges().size());
371 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
372 assertEquals(1, changes.size());
374 DataTreeCandidate candidate = changes.iterator().next();
375 assertNotNull(candidate);
376 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
377 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
379 changes = listener.getReceivedChanges().get(1);
380 assertEquals(1, changes.size());
382 candidate = changes.iterator().next();
383 assertNotNull(candidate);
384 candidateRoot = candidate.getRootNode();
385 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
386 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
387 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
388 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
389 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
390 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
391 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
395 private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
396 final NormalizedNode<?, ?> expectedAfter,
397 final ModificationType expectedMod,
398 final DataTreeCandidateNode candidateNode) {
399 if (expectedBefore != null) {
400 assertTrue(candidateNode.getDataBefore().isPresent());
401 assertEquals(expectedBefore, candidateNode.getDataBefore().get());
403 assertFalse(candidateNode.getDataBefore().isPresent());
406 if (expectedAfter != null) {
407 assertTrue(candidateNode.getDataAfter().isPresent());
408 assertEquals(expectedAfter, candidateNode.getDataAfter().get());
410 assertFalse(candidateNode.getDataAfter().isPresent());
413 assertEquals(expectedMod, candidateNode.getModificationType());
416 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
417 final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
418 .get(DOMDataTreeChangeService.class);
419 if (extension == null) {
422 DOMDataTreeChangeService dataTreeChangeService = null;
423 if (extension instanceof DOMDataTreeChangeService) {
424 dataTreeChangeService = (DOMDataTreeChangeService) extension;
426 return dataTreeChangeService;
429 static class CommitExecutorService extends ForwardingExecutorService {
431 ExecutorService delegate;
433 CommitExecutorService(final ExecutorService delegate) {
434 this.delegate = delegate;
438 protected ExecutorService delegate() {
443 static class TestDataTreeListener implements DOMDataTreeChangeListener {
445 private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
446 private final CountDownLatch latch;
448 TestDataTreeListener(final CountDownLatch latch) {
453 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
454 receivedChanges.add(changes);
458 public List<Collection<DataTreeCandidate>> getReceivedChanges() {
459 return receivedChanges;