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