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.controller.md.sal.dom.broker.impl;
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;
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.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
37 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
38 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
39 import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
40 import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
41 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
42 import org.opendaylight.yangtools.concepts.ListenerRegistration;
43 import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
44 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
47 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
51 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
53 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
54 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
55 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
57 public class DOMDataTreeListenerTest {
59 private SchemaContext schemaContext;
60 private AbstractDOMDataBroker domBroker;
61 private ListeningExecutorService executor;
62 private ExecutorService futureExecutor;
63 private CommitExecutorService commitExecutor;
65 private static final DataContainerChild<?, ?> OUTER_LIST = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
66 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1)).build();
68 private static final DataContainerChild<?, ?> OUTER_LIST_2 = ImmutableNodes
69 .mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
70 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2)).build();
72 private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
73 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)).withChild(OUTER_LIST)
76 private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
77 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)).withChild(OUTER_LIST_2)
80 private static DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
81 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
83 private static DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
84 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
87 public void setupStore() {
88 InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.newDirectExecutorService());
89 InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.newDirectExecutorService());
90 schemaContext = TestModel.createTestContext();
92 operStore.onGlobalContextUpdated(schemaContext);
93 configStore.onGlobalContextUpdated(schemaContext);
95 final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
97 .put(CONFIGURATION, configStore) //
98 .put(OPERATIONAL, operStore) //
101 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
102 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB",
103 DOMDataTreeListenerTest.class);
104 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
105 TransactionCommitDeadlockException
106 .DEADLOCK_EXCEPTION_SUPPLIER,
108 domBroker = new SerializedDOMDataBroker(stores, executor);
112 public void tearDown() {
113 if (executor != null) {
114 executor.shutdownNow();
117 if (futureExecutor != null) {
118 futureExecutor.shutdownNow();
123 public void writeContainerEmptyTreeTest() throws InterruptedException {
124 CountDownLatch latch = new CountDownLatch(1);
126 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
127 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!", dataTreeChangeService);
129 final TestDataTreeListener listener = new TestDataTreeListener(latch);
130 final ListenerRegistration<TestDataTreeListener> listenerReg = dataTreeChangeService
131 .registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
133 final DOMDataWriteTransaction 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 DataTreeCandidate candidate = changes.iterator().next();
144 assertNotNull(candidate);
145 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
146 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
151 public void replaceContainerContainerInTreeTest() throws InterruptedException, ExecutionException {
152 final CountDownLatch latch = new CountDownLatch(2);
154 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
155 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!", dataTreeChangeService);
157 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
158 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
159 writeTx.commit().get();
161 final TestDataTreeListener listener = new TestDataTreeListener(latch);
162 final ListenerRegistration<TestDataTreeListener> listenerReg = dataTreeChangeService
163 .registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
164 writeTx = domBroker.newWriteOnlyTransaction();
165 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
168 latch.await(5, TimeUnit.SECONDS);
170 assertEquals(2, listener.getReceivedChanges().size());
171 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
172 assertEquals(1, changes.size());
174 DataTreeCandidate candidate = changes.iterator().next();
175 assertNotNull(candidate);
176 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
177 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
179 changes = listener.getReceivedChanges().get(1);
180 assertEquals(1, changes.size());
182 candidate = changes.iterator().next();
183 assertNotNull(candidate);
184 candidateRoot = candidate.getRootNode();
185 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
190 public void deleteContainerContainerInTreeTest() throws InterruptedException, ExecutionException {
191 final CountDownLatch latch = new CountDownLatch(2);
193 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
194 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!", dataTreeChangeService);
196 DOMDataWriteTransaction 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 = dataTreeChangeService
202 .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 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
212 assertEquals(1, changes.size());
214 DataTreeCandidate candidate = changes.iterator().next();
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.iterator().next();
223 assertNotNull(candidate);
224 candidateRoot = candidate.getRootNode();
225 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
230 public void replaceChildListContainerInTreeTest() throws InterruptedException, ExecutionException {
231 final CountDownLatch latch = new CountDownLatch(2);
233 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
234 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!", dataTreeChangeService);
236 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
237 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
238 writeTx.commit().get();
240 final TestDataTreeListener listener = new TestDataTreeListener(latch);
241 final ListenerRegistration<TestDataTreeListener> listenerReg = dataTreeChangeService
242 .registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
244 writeTx = domBroker.newWriteOnlyTransaction();
245 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
248 latch.await(5, TimeUnit.SECONDS);
250 assertEquals(2, listener.getReceivedChanges().size());
251 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
252 assertEquals(1, changes.size());
254 DataTreeCandidate candidate = changes.iterator().next();
255 assertNotNull(candidate);
256 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
257 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
259 changes = listener.getReceivedChanges().get(1);
260 assertEquals(1, changes.size());
262 candidate = changes.iterator().next();
263 assertNotNull(candidate);
264 candidateRoot = candidate.getRootNode();
265 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
266 final DataTreeCandidateNode modifiedChild = candidateRoot
267 .getModifiedChild(new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME)).get();
268 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
273 public void rootModificationChildListenerTest() throws InterruptedException, ExecutionException {
274 final CountDownLatch latch = new CountDownLatch(2);
276 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
277 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!", dataTreeChangeService);
279 DOMDataWriteTransaction 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 = dataTreeChangeService
285 .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 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
295 assertEquals(1, changes.size());
297 DataTreeCandidate candidate = changes.iterator().next();
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.iterator().next();
306 assertNotNull(candidate);
307 candidateRoot = candidate.getRootNode();
308 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
313 public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, ExecutionException {
314 final CountDownLatch latch = new CountDownLatch(2);
316 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
317 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!", dataTreeChangeService);
319 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
320 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
321 writeTx.commit().get();
323 final TestDataTreeListener listener = new TestDataTreeListener(latch);
324 final ListenerRegistration<TestDataTreeListener> listenerReg = dataTreeChangeService
325 .registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
327 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1
328 = new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME,
329 TestModel.ID_QNAME, 1);
330 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2
331 = new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME,
332 TestModel.ID_QNAME, 2);
333 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3
334 = new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME,
335 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).withChild(outerListEntry2)
342 .withChild(outerListEntry3).build();
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 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
356 assertEquals(1, changes.size());
358 DataTreeCandidate candidate = changes.iterator().next();
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.iterator().next();
367 assertNotNull(candidate);
368 candidateRoot = candidate.getRootNode();
369 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
370 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1).get();
371 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
372 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2).get();
373 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
374 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3).get();
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 if (expectedBefore != null) {
382 assertTrue(candidateNode.getDataBefore().isPresent());
383 assertEquals(expectedBefore, candidateNode.getDataBefore().get());
385 assertFalse(candidateNode.getDataBefore().isPresent());
388 if (expectedAfter != null) {
389 assertTrue(candidateNode.getDataAfter().isPresent());
390 assertEquals(expectedAfter, candidateNode.getDataAfter().get());
392 assertFalse(candidateNode.getDataAfter().isPresent());
395 assertEquals(expectedMod, candidateNode.getModificationType());
398 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
399 final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
400 if (extension == null) {
403 DOMDataTreeChangeService dataTreeChangeService = null;
404 if (extension instanceof DOMDataTreeChangeService) {
405 dataTreeChangeService = (DOMDataTreeChangeService) extension;
407 return dataTreeChangeService;
411 static class CommitExecutorService extends ForwardingExecutorService {
413 ExecutorService delegate;
415 CommitExecutorService(final ExecutorService delegate) {
416 this.delegate = delegate;
420 protected ExecutorService delegate() {
425 static class TestDataTreeListener implements DOMDataTreeChangeListener {
427 private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
428 private final CountDownLatch latch;
430 TestDataTreeListener(final CountDownLatch latch) {
435 public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
436 receivedChanges.add(changes);
440 public List<Collection<DataTreeCandidate>> getReceivedChanges() {
441 return receivedChanges;