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 org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
18 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
20 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
21 import org.opendaylight.mdsal.dom.api.DOMDataWriteTransaction;
23 import com.google.common.collect.ImmutableMap;
24 import com.google.common.util.concurrent.ForwardingExecutorService;
25 import com.google.common.util.concurrent.ListeningExecutorService;
26 import com.google.common.util.concurrent.MoreExecutors;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.List;
30 import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors;
33 import java.util.concurrent.TimeUnit;
34 import javax.annotation.Nonnull;
35 import org.junit.After;
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
39 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
40 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
41 import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
42 import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
43 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
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 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
68 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
71 private static final DataContainerChild<?, ?> OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
72 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
75 private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
76 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
77 .withChild(OUTER_LIST)
80 private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
81 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
82 .withChild(OUTER_LIST_2)
85 private static DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
86 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
88 private static DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
89 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
92 public void setupStore() {
93 InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
94 MoreExecutors.newDirectExecutorService());
95 InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
96 MoreExecutors.newDirectExecutorService());
97 schemaContext = TestModel.createTestContext();
99 operStore.onGlobalContextUpdated(schemaContext);
100 configStore.onGlobalContextUpdated(schemaContext);
102 ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder() //
103 .put(CONFIGURATION, configStore) //
104 .put(OPERATIONAL, operStore) //
107 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
108 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
109 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
110 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
111 domBroker = new SerializedDOMDataBroker(stores, executor);
115 public void tearDown() {
116 if (executor != null) {
117 executor.shutdownNow();
120 if (futureExecutor != null) {
121 futureExecutor.shutdownNow();
126 public void writeContainerEmptyTreeTest() throws InterruptedException {
127 CountDownLatch latch = new CountDownLatch(1);
129 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
130 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
131 dataTreeChangeService);
133 final TestDataTreeListener listener = new TestDataTreeListener(latch);
134 final ListenerRegistration<TestDataTreeListener> listenerReg =
135 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
137 final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
138 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
141 latch.await(5, TimeUnit.SECONDS);
143 assertEquals(1, listener.getReceivedChanges().size());
144 final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
145 assertEquals(1, changes.size());
147 DataTreeCandidate candidate = changes.iterator().next();
148 assertNotNull(candidate);
149 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
150 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
155 public void replaceContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
156 CountDownLatch latch = new CountDownLatch(2);
158 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
159 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
160 dataTreeChangeService);
162 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
163 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
164 writeTx.submit().checkedGet();
166 final TestDataTreeListener listener = new TestDataTreeListener(latch);
167 final ListenerRegistration<TestDataTreeListener> listenerReg =
168 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
169 writeTx = domBroker.newWriteOnlyTransaction();
170 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
173 latch.await(5, TimeUnit.SECONDS);
175 assertEquals(2, listener.getReceivedChanges().size());
176 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
177 assertEquals(1, changes.size());
179 DataTreeCandidate candidate = changes.iterator().next();
180 assertNotNull(candidate);
181 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
182 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
184 changes = listener.getReceivedChanges().get(1);
185 assertEquals(1, changes.size());
187 candidate = changes.iterator().next();
188 assertNotNull(candidate);
189 candidateRoot = candidate.getRootNode();
190 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
195 public void deleteContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
196 CountDownLatch latch = new CountDownLatch(2);
198 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
199 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
200 dataTreeChangeService);
202 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
203 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
204 writeTx.submit().checkedGet();
206 final TestDataTreeListener listener = new TestDataTreeListener(latch);
207 final ListenerRegistration<TestDataTreeListener> listenerReg =
208 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
210 writeTx = domBroker.newWriteOnlyTransaction();
211 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
214 latch.await(5, TimeUnit.SECONDS);
216 assertEquals(2, listener.getReceivedChanges().size());
217 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
218 assertEquals(1, changes.size());
220 DataTreeCandidate candidate = changes.iterator().next();
221 assertNotNull(candidate);
222 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
223 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
225 changes = listener.getReceivedChanges().get(1);
226 assertEquals(1, changes.size());
228 candidate = changes.iterator().next();
229 assertNotNull(candidate);
230 candidateRoot = candidate.getRootNode();
231 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
236 public void replaceChildListContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
237 CountDownLatch latch = new CountDownLatch(2);
239 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
240 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
241 dataTreeChangeService);
243 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
244 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
245 writeTx.submit().checkedGet();
247 final TestDataTreeListener listener = new TestDataTreeListener(latch);
248 final ListenerRegistration<TestDataTreeListener> listenerReg =
249 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
251 writeTx = domBroker.newWriteOnlyTransaction();
252 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
255 latch.await(5, TimeUnit.SECONDS);
257 assertEquals(2, listener.getReceivedChanges().size());
258 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
259 assertEquals(1, changes.size());
261 DataTreeCandidate candidate = changes.iterator().next();
262 assertNotNull(candidate);
263 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
264 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
266 changes = listener.getReceivedChanges().get(1);
267 assertEquals(1, changes.size());
269 candidate = changes.iterator().next();
270 assertNotNull(candidate);
271 candidateRoot = candidate.getRootNode();
272 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
273 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
274 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
275 assertNotNull(modifiedChild);
276 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
281 public void rootModificationChildListenerTest() throws InterruptedException, TransactionCommitFailedException {
282 CountDownLatch latch = new CountDownLatch(2);
284 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
285 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
286 dataTreeChangeService);
288 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
289 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
290 writeTx.submit().checkedGet();
292 final TestDataTreeListener listener = new TestDataTreeListener(latch);
293 final ListenerRegistration<TestDataTreeListener> listenerReg =
294 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
296 writeTx = domBroker.newWriteOnlyTransaction();
297 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
298 writeTx.submit().checkedGet();
300 latch.await(1, TimeUnit.SECONDS);
302 assertEquals(2, listener.getReceivedChanges().size());
303 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
304 assertEquals(1, changes.size());
306 DataTreeCandidate candidate = changes.iterator().next();
307 assertNotNull(candidate);
308 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
309 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
311 changes = listener.getReceivedChanges().get(1);
312 assertEquals(1, changes.size());
314 candidate = changes.iterator().next();
315 assertNotNull(candidate);
316 candidateRoot = candidate.getRootNode();
317 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
322 public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, TransactionCommitFailedException {
323 CountDownLatch latch = new CountDownLatch(2);
325 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
326 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
327 dataTreeChangeService);
329 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
330 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
331 writeTx.submit().checkedGet();
333 final TestDataTreeListener listener = new TestDataTreeListener(latch);
334 final ListenerRegistration<TestDataTreeListener> listenerReg =
335 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
337 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
338 new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
339 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
340 new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
341 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
342 new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
344 final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
345 final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
346 final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
348 final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
349 .withChild(outerListEntry2)
350 .withChild(outerListEntry3)
353 writeTx = domBroker.newWriteOnlyTransaction();
354 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
355 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
357 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
361 latch.await(5, TimeUnit.SECONDS);
363 assertEquals(2, listener.getReceivedChanges().size());
364 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
365 assertEquals(1, changes.size());
367 DataTreeCandidate candidate = changes.iterator().next();
368 assertNotNull(candidate);
369 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
370 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
372 changes = listener.getReceivedChanges().get(1);
373 assertEquals(1, changes.size());
375 candidate = changes.iterator().next();
376 assertNotNull(candidate);
377 candidateRoot = candidate.getRootNode();
378 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
379 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
380 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
381 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
382 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
383 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
384 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
388 private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
389 final NormalizedNode<?, ?> expectedAfter,
390 final ModificationType expectedMod,
391 final DataTreeCandidateNode candidateNode) {
392 if (expectedBefore != null) {
393 assertTrue(candidateNode.getDataBefore().isPresent());
394 assertEquals(expectedBefore, candidateNode.getDataBefore().get());
396 assertFalse(candidateNode.getDataBefore().isPresent());
399 if (expectedAfter != null) {
400 assertTrue(candidateNode.getDataAfter().isPresent());
401 assertEquals(expectedAfter, candidateNode.getDataAfter().get());
403 assertFalse(candidateNode.getDataAfter().isPresent());
406 assertEquals(expectedMod, candidateNode.getModificationType());
409 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
410 final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
411 .get(DOMDataTreeChangeService.class);
412 if (extension == null) {
415 DOMDataTreeChangeService dataTreeChangeService = null;
416 if (extension instanceof DOMDataTreeChangeService) {
417 dataTreeChangeService = (DOMDataTreeChangeService) extension;
419 return dataTreeChangeService;
423 static class CommitExecutorService extends ForwardingExecutorService {
425 ExecutorService delegate;
427 public CommitExecutorService(final ExecutorService delegate) {
428 this.delegate = delegate;
432 protected ExecutorService delegate() {
437 static class TestDataTreeListener implements DOMDataTreeChangeListener {
439 private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
440 private final CountDownLatch latch;
442 public TestDataTreeListener(final CountDownLatch latch) {
447 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
448 receivedChanges.add(changes);
452 public List<Collection<DataTreeCandidate>> getReceivedChanges() {
453 return receivedChanges;