Move AbstractDOMDataBroker to mdsal-dom-spi
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / DOMBrokerTest.java
1 /*
2  * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.dom.broker;
9
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;
16
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.Collections;
22 import java.util.Optional;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.RejectedExecutionException;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.atomic.AtomicReference;
29 import org.junit.After;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.mockito.Mockito;
33 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
34 import org.opendaylight.mdsal.common.api.ReadFailedException;
35 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
36 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
39 import org.opendaylight.mdsal.dom.spi.AbstractDOMDataBroker;
40 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
41 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
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.NodeIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
47 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
48
49 public class DOMBrokerTest extends AbstractDatastoreTest {
50
51     private AbstractDOMDataBroker domBroker;
52     private ListeningExecutorService executor;
53     private ExecutorService futureExecutor;
54     private CommitExecutorService commitExecutor;
55
56     @Before
57     public void setupStore() {
58         final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
59                 MoreExecutors.newDirectExecutorService());
60         final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
61                 MoreExecutors.newDirectExecutorService());
62
63         operStore.onModelContextUpdated(SCHEMA_CONTEXT);
64         configStore.onModelContextUpdated(SCHEMA_CONTEXT);
65
66         final ImmutableMap<LogicalDatastoreType, DOMStore> stores =
67                 ImmutableMap.<LogicalDatastoreType, DOMStore>builder()
68                 .put(CONFIGURATION, configStore)
69                 .put(OPERATIONAL, operStore)
70                 .build();
71
72         commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
73         futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB", DOMBrokerTest.class);
74         executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
75                 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
76         domBroker = new SerializedDOMDataBroker(stores, executor);
77     }
78
79     @After
80     public void tearDown() {
81         if (executor != null) {
82             executor.shutdownNow();
83         }
84
85         if (futureExecutor != null) {
86             futureExecutor.shutdownNow();
87         }
88     }
89
90     @Test(timeout = 10000)
91     public void testTransactionIsolation() throws InterruptedException, ExecutionException {
92         assertNotNull(domBroker);
93
94         final DOMDataTreeReadTransaction readTx = domBroker.newReadOnlyTransaction();
95         assertNotNull(readTx);
96
97         final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
98         assertNotNull(writeTx);
99
100         /**
101          * Writes /test in writeTx.
102          *
103          */
104         writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.newContainerBuilder()
105             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
106             .build());
107
108         /**
109          * Reads /test from readTx Read should return Absent.
110          *
111          */
112         final var readTxContainer = readTx.read(OPERATIONAL, TestModel.TEST_PATH);
113         assertEquals(Optional.empty(), readTxContainer.get());
114     }
115
116     @Test(timeout = 10000)
117     public void testTransactionCommit() throws InterruptedException, ExecutionException {
118         final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
119         assertNotNull(writeTx);
120         /**
121          * Writes /test in writeTx
122          *
123          */
124         writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.newContainerBuilder()
125             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
126             .build());
127
128         writeTx.commit().get();
129
130         final var afterCommitRead = domBroker.newReadOnlyTransaction().read(OPERATIONAL, TestModel.TEST_PATH).get();
131         assertTrue(afterCommitRead.isPresent());
132     }
133
134     @Test(expected = TransactionCommitFailedException.class)
135     @SuppressWarnings({"checkstyle:AvoidHidingCauseException", "checkstyle:IllegalThrows"})
136     public void testRejectedCommit() throws Throwable {
137         commitExecutor.delegate = Mockito.mock(ExecutorService.class);
138         Mockito.doThrow(new RejectedExecutionException("mock"))
139             .when(commitExecutor.delegate).execute(Mockito.any(Runnable.class));
140         Mockito.doNothing().when(commitExecutor.delegate).shutdown();
141         Mockito.doReturn(Collections.emptyList()).when(commitExecutor.delegate).shutdownNow();
142         Mockito.doReturn("").when(commitExecutor.delegate).toString();
143         Mockito.doReturn(Boolean.TRUE).when(commitExecutor.delegate)
144             .awaitTermination(Mockito.anyLong(), Mockito.any(TimeUnit.class));
145
146         final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
147         writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.newContainerBuilder()
148             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
149             .build());
150
151         try {
152             writeTx.commit().get(5, TimeUnit.SECONDS);
153         } catch (ExecutionException e) {
154             throw e.getCause();
155         }
156     }
157
158     @SuppressWarnings("checkstyle:IllegalCatch")
159     AtomicReference<Throwable> submitTxAsync(final DOMDataTreeWriteTransaction writeTx) {
160         final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
161         new Thread(() -> {
162             try {
163                 writeTx.commit();
164             } catch (final Throwable e) {
165                 caughtEx.set(e);
166             }
167         }).start();
168
169         return caughtEx;
170     }
171
172     @Test(expected = ReadFailedException.class)
173     @SuppressWarnings({"checkstyle:IllegalThrows", "checkstyle:AvoidHidingCauseException"})
174     public void basicTests() throws Throwable {
175         final DataContainerChild outerList = ImmutableNodes.newSystemMapBuilder()
176             .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
177             .withChild(TestUtils.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
178             .build();
179         final ContainerNode testContainer = ImmutableNodes.newContainerBuilder()
180                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
181                 .withChild(outerList)
182                 .build();
183
184         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
185         final DOMDataTreeReadTransaction readRx = domBroker.newReadOnlyTransaction();
186         assertNotNull(writeTx);
187         assertNotNull(readRx);
188         assertNotNull(((SerializedDOMDataBroker) domBroker).getCommitStatsTracker());
189
190         writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.newContainerBuilder()
191             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
192             .build());
193         writeTx.commit().get();
194         assertFalse(writeTx.cancel());
195
196         assertFalse(domBroker.newReadOnlyTransaction().exists(CONFIGURATION, TestModel.TEST_PATH).get());
197         assertTrue(domBroker.newReadOnlyTransaction().exists(OPERATIONAL, TestModel.TEST_PATH).get());
198         assertFalse(domBroker.newReadOnlyTransaction().exists(OPERATIONAL, TestModel.TEST2_PATH).get());
199
200         writeTx = domBroker.newWriteOnlyTransaction();
201         writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.newContainerBuilder()
202             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
203             .build());
204         writeTx.delete(OPERATIONAL, TestModel.TEST_PATH);
205         writeTx.commit().get();
206         assertFalse(domBroker.newReadOnlyTransaction().exists(OPERATIONAL, TestModel.TEST_PATH).get());
207         assertTrue(domBroker.newWriteOnlyTransaction().cancel());
208
209         writeTx = domBroker.newWriteOnlyTransaction();
210         writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.newContainerBuilder()
211             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
212             .build());
213         writeTx.merge(OPERATIONAL, TestModel.TEST_PATH, testContainer);
214         writeTx.commit().get();
215         assertTrue(domBroker.newReadOnlyTransaction().exists(OPERATIONAL, TestModel.TEST_PATH).get());
216         assertTrue(domBroker.newReadOnlyTransaction().read(OPERATIONAL, TestModel.TEST_PATH).get()
217                  .orElseThrow().toString().contains(testContainer.toString()));
218
219         readRx.read(OPERATIONAL, TestModel.TEST_PATH).get(); // init backing tx before close
220         readRx.close();
221
222         //Expected exception after close call
223
224         try {
225             readRx.read(OPERATIONAL, TestModel.TEST_PATH).get();
226         } catch (ExecutionException e) {
227             throw e.getCause();
228         }
229     }
230
231     @SuppressWarnings({"checkstyle:IllegalThrows", "checkstyle:IllegalCatch"})
232     @Test
233     public void closeTest() throws Exception {
234         final String testException = "TestException";
235
236         domBroker.setCloseable(() -> {
237             throw new Exception(testException);
238         });
239
240         try {
241             domBroker.close();
242         } catch (final Exception e) {
243             assertTrue(e.getMessage().contains(testException));
244         }
245     }
246
247     static class CommitExecutorService extends ForwardingExecutorService {
248
249         ExecutorService delegate;
250
251         CommitExecutorService(final ExecutorService delegate) {
252             this.delegate = delegate;
253         }
254
255         @Override
256         protected ExecutorService delegate() {
257             return delegate;
258         }
259     }
260 }