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.mdsal.dom.broker;
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.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
15 import static org.opendaylight.mdsal.common.api.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.mdsal.common.api.LogicalDatastoreType;
33 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
34 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
38 import org.opendaylight.mdsal.dom.broker.util.TestModel;
39 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
40 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
41 import org.opendaylight.yangtools.concepts.ListenerRegistration;
42 import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
43 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
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;
56 public class DOMDataTreeListenerTest extends AbstractDatastoreTest {
58 private AbstractDOMDataBroker domBroker;
59 private ListeningExecutorService executor;
60 private ExecutorService futureExecutor;
61 private CommitExecutorService commitExecutor;
63 private static final DataContainerChild<?, ?> OUTER_LIST =
64 ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
65 .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
68 private static final DataContainerChild<?, ?> OUTER_LIST_2 =
69 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 final DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
84 LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
86 private static final DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
87 LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
90 public void setupStore() {
91 final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
92 MoreExecutors.newDirectExecutorService());
93 final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
94 MoreExecutors.newDirectExecutorService());
96 operStore.onModelContextUpdated(SCHEMA_CONTEXT);
97 configStore.onModelContextUpdated(SCHEMA_CONTEXT);
99 final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
101 .put(CONFIGURATION, configStore)
102 .put(OPERATIONAL, operStore)
105 commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
106 futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB",
107 DOMDataTreeListenerTest.class);
108 executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
109 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
110 domBroker = new SerializedDOMDataBroker(stores, executor);
114 public void tearDown() {
115 if (executor != null) {
116 executor.shutdownNow();
119 if (futureExecutor != null) {
120 futureExecutor.shutdownNow();
125 public void writeContainerEmptyTreeTest() throws InterruptedException {
126 final CountDownLatch latch = new CountDownLatch(1);
128 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
129 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
130 dataTreeChangeService);
132 final TestDataTreeListener listener = new TestDataTreeListener(latch);
133 final ListenerRegistration<TestDataTreeListener> listenerReg =
134 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
136 final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
137 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
140 latch.await(5, TimeUnit.SECONDS);
142 assertEquals(1, listener.getReceivedChanges().size());
143 final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
144 assertEquals(1, changes.size());
146 final DataTreeCandidate candidate = changes.iterator().next();
147 assertNotNull(candidate);
148 final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
149 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
154 public void replaceContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
155 final CountDownLatch latch = new CountDownLatch(2);
157 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
158 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
159 dataTreeChangeService);
161 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
162 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
163 writeTx.commit().get();
165 final TestDataTreeListener listener = new TestDataTreeListener(latch);
166 final ListenerRegistration<TestDataTreeListener> listenerReg =
167 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
168 writeTx = domBroker.newWriteOnlyTransaction();
169 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
172 latch.await(5, TimeUnit.SECONDS);
174 assertEquals(2, listener.getReceivedChanges().size());
175 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
176 assertEquals(1, changes.size());
178 DataTreeCandidate candidate = changes.iterator().next();
179 assertNotNull(candidate);
180 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
181 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
183 changes = listener.getReceivedChanges().get(1);
184 assertEquals(1, changes.size());
186 candidate = changes.iterator().next();
187 assertNotNull(candidate);
188 candidateRoot = candidate.getRootNode();
189 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
194 public void deleteContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
195 final CountDownLatch latch = new CountDownLatch(2);
197 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
198 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
199 dataTreeChangeService);
201 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
202 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
203 writeTx.commit().get();
205 final TestDataTreeListener listener = new TestDataTreeListener(latch);
206 final ListenerRegistration<TestDataTreeListener> listenerReg =
207 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
209 writeTx = domBroker.newWriteOnlyTransaction();
210 writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
213 latch.await(5, TimeUnit.SECONDS);
215 assertEquals(2, listener.getReceivedChanges().size());
216 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
217 assertEquals(1, changes.size());
219 DataTreeCandidate candidate = changes.iterator().next();
220 assertNotNull(candidate);
221 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
222 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
224 changes = listener.getReceivedChanges().get(1);
225 assertEquals(1, changes.size());
227 candidate = changes.iterator().next();
228 assertNotNull(candidate);
229 candidateRoot = candidate.getRootNode();
230 checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
235 public void replaceChildListContainerInTreeTest() throws ExecutionException, InterruptedException {
236 final CountDownLatch latch = new CountDownLatch(2);
238 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
239 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
240 dataTreeChangeService);
242 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
243 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
244 writeTx.commit().get();
246 final TestDataTreeListener listener = new TestDataTreeListener(latch);
247 final ListenerRegistration<TestDataTreeListener> listenerReg =
248 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
250 writeTx = domBroker.newWriteOnlyTransaction();
251 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
254 latch.await(5, TimeUnit.SECONDS);
256 assertEquals(2, listener.getReceivedChanges().size());
257 Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
258 assertEquals(1, changes.size());
260 DataTreeCandidate candidate = changes.iterator().next();
261 assertNotNull(candidate);
262 DataTreeCandidateNode candidateRoot = candidate.getRootNode();
263 checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
265 changes = listener.getReceivedChanges().get(1);
266 assertEquals(1, changes.size());
268 candidate = changes.iterator().next();
269 assertNotNull(candidate);
270 candidateRoot = candidate.getRootNode();
271 checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
272 final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
273 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME)).get();
274 checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
279 public void rootModificationChildListenerTest() throws ExecutionException, InterruptedException {
280 final CountDownLatch latch = new CountDownLatch(2);
282 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
283 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
284 dataTreeChangeService);
286 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
287 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
288 writeTx.commit().get();
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.commit().get();
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 ExecutionException, InterruptedException {
321 final CountDownLatch latch = new CountDownLatch(2);
323 final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
324 assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
325 dataTreeChangeService);
327 DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
328 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
329 writeTx.commit().get();
331 final TestDataTreeListener listener = new TestDataTreeListener(latch);
332 final ListenerRegistration<TestDataTreeListener> listenerReg =
333 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
335 final NodeIdentifierWithPredicates outerListEntryId1 =
336 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
337 final NodeIdentifierWithPredicates outerListEntryId2 =
338 NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
339 final NodeIdentifierWithPredicates outerListEntryId3 =
340 NodeIdentifierWithPredicates.of(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).get();
378 checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
379 final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2).get();
380 checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
381 final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3).get();
382 checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
386 private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
387 final NormalizedNode<?, ?> expectedAfter,
388 final ModificationType expectedMod,
389 final 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 return domBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
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;