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.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.TimeUnit;
29 import javax.annotation.Nonnull;
30 import org.junit.After;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
34 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
35 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
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() throws Exception {
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, DOMStore>builder()
105 .put(CONFIGURATION, configStore)
106 .put(OPERATIONAL, operStore)
109 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
110 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
111 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
112 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
113 domBroker = new SerializedDOMDataBroker(stores, executor);
117 public void tearDown() {
118 if (executor != null) {
119 executor.shutdownNow();
122 if (futureExecutor != null) {
123 futureExecutor.shutdownNow();
128 public void writeContainerEmptyTreeTest() throws InterruptedException {
129 final CountDownLatch latch = new CountDownLatch(1);
131 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
132 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
133 dataTreeChangeService);
135 final TestDataTreeListener listener = new TestDataTreeListener(latch);
136 final ListenerRegistration<TestDataTreeListener> listenerReg =
137 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
139 final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
140 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
143 latch.await(5, TimeUnit.SECONDS);
145 assertEquals(1, listener.getReceivedChanges().size());
146 final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
147 assertEquals(1, changes.size());
149 final DataTreeCandidate candidate = changes.iterator().next();
150 assertNotNull(candidate);
151 final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
152 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
157 public void replaceContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
158 final CountDownLatch latch = new CountDownLatch(2);
160 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
161 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
162 dataTreeChangeService);
164 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
165 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
166 writeTx.submit().checkedGet();
168 final TestDataTreeListener listener = new TestDataTreeListener(latch);
169 final ListenerRegistration<TestDataTreeListener> listenerReg =
170 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
171 writeTx = domBroker.newWriteOnlyTransaction();
172 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
175 latch.await(5, TimeUnit.SECONDS);
177 assertEquals(2, listener.getReceivedChanges().size());
178 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
179 assertEquals(1, changes.size());
181 DataTreeCandidate candidate = changes.iterator().next();
182 assertNotNull(candidate);
183 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
184 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
186 changes = listener.getReceivedChanges().get(1);
187 assertEquals(1, changes.size());
189 candidate = changes.iterator().next();
190 assertNotNull(candidate);
191 candidateRoot = candidate.getRootNode();
192 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
197 public void deleteContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
198 final CountDownLatch latch = new CountDownLatch(2);
200 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
201 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
202 dataTreeChangeService);
204 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
205 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
206 writeTx.submit().checkedGet();
208 final TestDataTreeListener listener = new TestDataTreeListener(latch);
209 final ListenerRegistration<TestDataTreeListener> listenerReg =
210 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
212 writeTx = domBroker.newWriteOnlyTransaction();
213 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
216 latch.await(5, TimeUnit.SECONDS);
218 assertEquals(2, listener.getReceivedChanges().size());
219 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
220 assertEquals(1, changes.size());
222 DataTreeCandidate candidate = changes.iterator().next();
223 assertNotNull(candidate);
224 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
225 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
227 changes = listener.getReceivedChanges().get(1);
228 assertEquals(1, changes.size());
230 candidate = changes.iterator().next();
231 assertNotNull(candidate);
232 candidateRoot = candidate.getRootNode();
233 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
238 public void replaceChildListContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
239 final CountDownLatch latch = new CountDownLatch(2);
241 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
242 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
243 dataTreeChangeService);
245 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
246 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
247 writeTx.submit().checkedGet();
249 final TestDataTreeListener listener = new TestDataTreeListener(latch);
250 final ListenerRegistration<TestDataTreeListener> listenerReg =
251 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
253 writeTx = domBroker.newWriteOnlyTransaction();
254 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
257 latch.await(5, TimeUnit.SECONDS);
259 assertEquals(2, listener.getReceivedChanges().size());
260 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
261 assertEquals(1, changes.size());
263 DataTreeCandidate candidate = changes.iterator().next();
264 assertNotNull(candidate);
265 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
266 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
268 changes = listener.getReceivedChanges().get(1);
269 assertEquals(1, changes.size());
271 candidate = changes.iterator().next();
272 assertNotNull(candidate);
273 candidateRoot = candidate.getRootNode();
274 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
275 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
276 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
277 assertNotNull(modifiedChild);
278 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
283 public void rootModificationChildListenerTest() throws InterruptedException, TransactionCommitFailedException {
284 final CountDownLatch latch = new CountDownLatch(2);
286 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
287 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
288 dataTreeChangeService);
290 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
291 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
292 writeTx.submit().checkedGet();
294 final TestDataTreeListener listener = new TestDataTreeListener(latch);
295 final ListenerRegistration<TestDataTreeListener> listenerReg =
296 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
298 writeTx = domBroker.newWriteOnlyTransaction();
299 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
300 writeTx.submit().checkedGet();
302 latch.await(1, TimeUnit.SECONDS);
304 assertEquals(2, listener.getReceivedChanges().size());
305 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
306 assertEquals(1, changes.size());
308 DataTreeCandidate candidate = changes.iterator().next();
309 assertNotNull(candidate);
310 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
311 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
313 changes = listener.getReceivedChanges().get(1);
314 assertEquals(1, changes.size());
316 candidate = changes.iterator().next();
317 assertNotNull(candidate);
318 candidateRoot = candidate.getRootNode();
319 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
324 public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, TransactionCommitFailedException {
325 final CountDownLatch latch = new CountDownLatch(2);
327 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
328 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
329 dataTreeChangeService);
331 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
332 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
333 writeTx.submit().checkedGet();
335 final TestDataTreeListener listener = new TestDataTreeListener(latch);
336 final ListenerRegistration<TestDataTreeListener> listenerReg =
337 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
339 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
340 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
341 TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
342 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
343 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
344 TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
345 final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
346 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
347 TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
349 final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
350 final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
351 final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
353 final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
354 .withChild(outerListEntry2)
355 .withChild(outerListEntry3)
358 writeTx = domBroker.newWriteOnlyTransaction();
359 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
360 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
362 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
366 latch.await(5, TimeUnit.SECONDS);
368 assertEquals(2, listener.getReceivedChanges().size());
369 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
370 assertEquals(1, changes.size());
372 DataTreeCandidate candidate = changes.iterator().next();
373 assertNotNull(candidate);
374 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
375 checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
377 changes = listener.getReceivedChanges().get(1);
378 assertEquals(1, changes.size());
380 candidate = changes.iterator().next();
381 assertNotNull(candidate);
382 candidateRoot = candidate.getRootNode();
383 checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
384 final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
385 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
386 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
387 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
388 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
389 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
393 private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
394 final NormalizedNode<?, ?> expectedAfter,
395 final ModificationType expectedMod,
396 final DataTreeCandidateNode candidateNode) {
397 if (expectedBefore != null) {
398 assertTrue(candidateNode.getDataBefore().isPresent());
399 assertEquals(expectedBefore, candidateNode.getDataBefore().get());
401 assertFalse(candidateNode.getDataBefore().isPresent());
404 if (expectedAfter != null) {
405 assertTrue(candidateNode.getDataAfter().isPresent());
406 assertEquals(expectedAfter, candidateNode.getDataAfter().get());
408 assertFalse(candidateNode.getDataAfter().isPresent());
411 assertEquals(expectedMod, candidateNode.getModificationType());
414 private DOMDataTreeChangeService getDOMDataTreeChangeService() {
415 final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
416 .get(DOMDataTreeChangeService.class);
417 if (extension == null) {
420 DOMDataTreeChangeService dataTreeChangeService = null;
421 if (extension instanceof DOMDataTreeChangeService) {
422 dataTreeChangeService = (DOMDataTreeChangeService) extension;
424 return dataTreeChangeService;
427 static class CommitExecutorService extends ForwardingExecutorService {
429 ExecutorService delegate;
431 public CommitExecutorService(final ExecutorService delegate) {
432 this.delegate = delegate;
436 protected ExecutorService delegate() {
441 static class TestDataTreeListener implements DOMDataTreeChangeListener {
443 private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
444 private final CountDownLatch latch;
446 public TestDataTreeListener(final CountDownLatch latch) {
451 public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
452 receivedChanges.add(changes);
456 public List<Collection<DataTreeCandidate>> getReceivedChanges() {
457 return receivedChanges;