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 junit.framework.Assert.assertEquals;
11 import static junit.framework.Assert.assertFalse;
12 import static junit.framework.Assert.assertNotNull;
13 import static junit.framework.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;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.util.concurrent.ForwardingExecutorService;
18 import com.google.common.util.concurrent.ListeningExecutorService;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.List;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.TimeUnit;
27 import javax.annotation.Nonnull;
28 import org.junit.After;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
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))
69 private static final DataContainerChild<?, ?> OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
70 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
73 private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
74 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
75 .withChild(OUTER_LIST)
78 private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
79 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
80 .withChild(OUTER_LIST_2)
83 private static DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
84 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
86 private static DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
87 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
90 public void setupStore() {
91 InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
92 MoreExecutors.newDirectExecutorService());
93 InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
94 MoreExecutors.newDirectExecutorService());
95 schemaContext = TestModel.createTestContext();
97 operStore.onGlobalContextUpdated(schemaContext);
98 configStore.onGlobalContextUpdated(schemaContext);
100 ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder() //
101 .put(CONFIGURATION, configStore) //
102 .put(OPERATIONAL, operStore) //
105 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
106 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
107 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
108 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
109 domBroker = new SerializedDOMDataBroker(stores, executor);
113 public void tearDown() {
114 if (executor != null) {
115 executor.shutdownNow();
118 if (futureExecutor != null) {
119 futureExecutor.shutdownNow();
124 public void writeContainerEmptyTreeTest() throws InterruptedException {
125 CountDownLatch latch = new CountDownLatch(1);
127 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
128 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
129 dataTreeChangeService);
131 final TestDataTreeListener listener = new TestDataTreeListener(latch);
132 final ListenerRegistration<TestDataTreeListener> listenerReg =
133 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
135 final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
136 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
139 latch.await(5, TimeUnit.SECONDS);
141 assertEquals(1, listener.getReceivedChanges().size());
142 final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
143 assertEquals(1, changes.size());
145 DataTreeCandidate candidate = changes.iterator().next();
146 assertNotNull(candidate);
147 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
148 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
153 public void replaceContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
154 CountDownLatch latch = new CountDownLatch(2);
156 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
157 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
158 dataTreeChangeService);
160 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
161 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
162 writeTx.submit().checkedGet();
164 final TestDataTreeListener listener = new TestDataTreeListener(latch);
165 final ListenerRegistration<TestDataTreeListener> listenerReg =
166 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
167 writeTx = domBroker.newWriteOnlyTransaction();
168 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
171 latch.await(5, TimeUnit.SECONDS);
173 assertEquals(2, listener.getReceivedChanges().size());
174 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
175 assertEquals(1, changes.size());
177 DataTreeCandidate candidate = changes.iterator().next();
178 assertNotNull(candidate);
179 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
180 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
182 changes = listener.getReceivedChanges().get(1);
183 assertEquals(1, changes.size());
185 candidate = changes.iterator().next();
186 assertNotNull(candidate);
187 candidateRoot = candidate.getRootNode();
188 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
193 public void deleteContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
194 CountDownLatch latch = new CountDownLatch(2);
196 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
197 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
198 dataTreeChangeService);
200 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
201 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
202 writeTx.submit().checkedGet();
204 final TestDataTreeListener listener = new TestDataTreeListener(latch);
205 final ListenerRegistration<TestDataTreeListener> listenerReg =
206 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
208 writeTx = domBroker.newWriteOnlyTransaction();
209 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
212 latch.await(5, TimeUnit.SECONDS);
214 assertEquals(2, listener.getReceivedChanges().size());
215 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
216 assertEquals(1, changes.size());
218 DataTreeCandidate candidate = changes.iterator().next();
219 assertNotNull(candidate);
220 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
221 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
223 changes = listener.getReceivedChanges().get(1);
224 assertEquals(1, changes.size());
226 candidate = changes.iterator().next();
227 assertNotNull(candidate);
228 candidateRoot = candidate.getRootNode();
229 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
234 public void replaceChildListContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
235 CountDownLatch latch = new CountDownLatch(2);
237 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
238 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
239 dataTreeChangeService);
241 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
242 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
243 writeTx.submit().checkedGet();
245 final TestDataTreeListener listener = new TestDataTreeListener(latch);
246 final ListenerRegistration<TestDataTreeListener> listenerReg =
247 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
249 writeTx = domBroker.newWriteOnlyTransaction();
250 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
253 latch.await(5, TimeUnit.SECONDS);
255 assertEquals(2, listener.getReceivedChanges().size());
256 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
257 assertEquals(1, changes.size());
259 DataTreeCandidate candidate = changes.iterator().next();
260 assertNotNull(candidate);
261 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
262 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
264 changes = listener.getReceivedChanges().get(1);
265 assertEquals(1, changes.size());
267 candidate = changes.iterator().next();
268 assertNotNull(candidate);
269 candidateRoot = candidate.getRootNode();
270 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
271 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
272 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
273 assertNotNull(modifiedChild);
274 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
279 public void rootModificationChildListenerTest() throws InterruptedException, TransactionCommitFailedException {
280 CountDownLatch latch = new CountDownLatch(2);
282 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
283 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
284 dataTreeChangeService);
286 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
287 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
288 writeTx.submit().checkedGet();
290 final TestDataTreeListener listener = new TestDataTreeListener(latch);
291 final ListenerRegistration<TestDataTreeListener> listenerReg =
292 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
294 writeTx = domBroker.newWriteOnlyTransaction();
295 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
296 writeTx.submit().checkedGet();
298 latch.await(1, TimeUnit.SECONDS);
300 assertEquals(2, listener.getReceivedChanges().size());
301 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
302 assertEquals(1, changes.size());
304 DataTreeCandidate candidate = changes.iterator().next();
305 assertNotNull(candidate);
306 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
307 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
309 changes = listener.getReceivedChanges().get(1);
310 assertEquals(1, changes.size());
312 candidate = changes.iterator().next();
313 assertNotNull(candidate);
314 candidateRoot = candidate.getRootNode();
315 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
320 public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, TransactionCommitFailedException {
321 CountDownLatch latch = new CountDownLatch(2);
323 DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
324 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
325 dataTreeChangeService);
327 DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
328 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
329 writeTx.submit().checkedGet();
331 final TestDataTreeListener listener = new TestDataTreeListener(latch);
332 final ListenerRegistration<TestDataTreeListener> listenerReg =
333 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
335 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
336 new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
337 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
338 new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
339 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
340 new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
342 final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
343 final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
344 final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
346 final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
347 .withChild(outerListEntry2)
348 .withChild(outerListEntry3)
351 writeTx = domBroker.newWriteOnlyTransaction();
352 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
353 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
355 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
359 latch.await(5, TimeUnit.SECONDS);
361 assertEquals(2, listener.getReceivedChanges().size());
362 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
363 assertEquals(1, changes.size());
365 DataTreeCandidate candidate = changes.iterator().next();
366 assertNotNull(candidate);
367 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
368 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
370 changes = listener.getReceivedChanges().get(1);
371 assertEquals(1, changes.size());
373 candidate = changes.iterator().next();
374 assertNotNull(candidate);
375 candidateRoot = candidate.getRootNode();
376 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
377 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
378 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
379 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
380 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
381 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
382 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
386 private static void checkChange(NormalizedNode<?, ?> expectedBefore,
387 NormalizedNode<?, ?> expectedAfter,
388 ModificationType expectedMod,
389 DataTreeCandidateNode candidateNode) {
390 if (expectedBefore != null) {
391 assertTrue(candidateNode.getDataBefore().isPresent());
392 assertEquals(expectedBefore, candidateNode.getDataBefore().get());
394 assertFalse(candidateNode.getDataBefore().isPresent());
397 if (expectedAfter != null) {
398 assertTrue(candidateNode.getDataAfter().isPresent());
399 assertEquals(expectedAfter, candidateNode.getDataAfter().get());
401 assertFalse(candidateNode.getDataAfter().isPresent());
404 assertEquals(expectedMod, candidateNode.getModificationType());
407 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
408 final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
409 .get(DOMDataTreeChangeService.class);
410 if (extension == null) {
413 DOMDataTreeChangeService dataTreeChangeService = null;
414 if (extension instanceof DOMDataTreeChangeService) {
415 dataTreeChangeService = (DOMDataTreeChangeService) extension;
417 return dataTreeChangeService;
421 static class CommitExecutorService extends ForwardingExecutorService {
423 ExecutorService delegate;
425 public CommitExecutorService(final ExecutorService delegate) {
426 this.delegate = delegate;
430 protected ExecutorService delegate() {
435 static class TestDataTreeListener implements DOMDataTreeChangeListener {
437 private List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
438 private CountDownLatch latch;
440 public TestDataTreeListener(final CountDownLatch latch) {
445 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
446 receivedChanges.add(changes);
450 public List<Collection<DataTreeCandidate>> getReceivedChanges() {
451 return receivedChanges;