0deaddea71f3d1883071222ba916d02241e5fd15
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / DOMRpcRouterTest.java
1 /*
2  * Copyright (c) 2016, 2017 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.hamcrest.CoreMatchers.instanceOf;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertNotEquals;
14 import static org.junit.Assert.assertNotNull;
15 import static org.junit.Assert.assertThrows;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.doCallRealMethod;
18 import static org.mockito.Mockito.doNothing;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.mock;
21 import static org.mockito.Mockito.timeout;
22 import static org.mockito.Mockito.verify;
23 import static org.opendaylight.mdsal.dom.broker.TestUtils.getTestRpcImplementation;
24
25 import com.google.common.util.concurrent.Futures;
26 import com.google.common.util.concurrent.ListenableFuture;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.RejectedExecutionException;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.mockito.junit.MockitoJUnitRunner;
35 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
36 import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension;
37 import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension.AvailabilityListener;
38 import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
39 import org.opendaylight.mdsal.dom.api.DOMActionInstance;
40 import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
41 import org.opendaylight.mdsal.dom.api.DOMActionResult;
42 import org.opendaylight.mdsal.dom.api.DOMActionService;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
44 import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
45 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
46 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
47 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
48 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
49 import org.opendaylight.yangtools.concepts.Registration;
50 import org.opendaylight.yangtools.yang.common.QName;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
52 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
53 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
54 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
55
56 @RunWith(MockitoJUnitRunner.StrictStubs.class)
57 public class DOMRpcRouterTest {
58     private static final YangInstanceIdentifier BAZ_PATH_BAD = YangInstanceIdentifier.of(
59         new NodeIdentifier(Actions.FOO), NodeIdentifierWithPredicates.of(Actions.FOO, Actions.BAR, "bad"));
60     private static final YangInstanceIdentifier BAZ_PATH_GOOD = YangInstanceIdentifier.of(
61         new NodeIdentifier(Actions.FOO), NodeIdentifierWithPredicates.of(Actions.FOO, Actions.BAR, "good"));
62
63     private static final DOMActionImplementation IMPL =
64         (type, path, input) -> Futures.immediateFuture(new SimpleDOMActionResult(
65             Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(Actions.OUTPUT)).build()));
66
67     @Test
68     public void registerRpcImplementation() {
69         try (DOMRpcRouter rpcRouter = rpcsRouter()) {
70             assertOperationKeys(rpcRouter);
71
72             final Registration fooReg = rpcRouter.rpcProviderService().registerRpcImplementation(
73                 getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.FOO, null));
74             assertOperationKeys(rpcRouter, Rpcs.FOO);
75
76             final Registration barReg = rpcRouter.rpcProviderService().registerRpcImplementation(
77                 getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.BAR, null));
78             assertOperationKeys(rpcRouter, Rpcs.FOO, Rpcs.BAR);
79
80             fooReg.close();
81             assertOperationKeys(rpcRouter, Rpcs.BAR);
82             barReg.close();
83             assertOperationKeys(rpcRouter);
84         }
85     }
86
87     @Test
88     public void registerRpcImplementations() {
89         try (DOMRpcRouter rpcRouter = rpcsRouter()) {
90             assertOperationKeys(rpcRouter);
91
92             final Registration fooReg = rpcRouter.rpcProviderService().registerRpcImplementations(
93                 Map.of(DOMRpcIdentifier.create(Rpcs.FOO, null), getTestRpcImplementation()));
94             assertOperationKeys(rpcRouter, Rpcs.FOO);
95
96             final Registration barReg = rpcRouter.rpcProviderService().registerRpcImplementations(
97                 Map.of(
98                     DOMRpcIdentifier.create(Rpcs.BAR, null), getTestRpcImplementation(),
99                     DOMRpcIdentifier.create(Rpcs.BAZ, null), getTestRpcImplementation()));
100             assertOperationKeys(rpcRouter, Rpcs.FOO, Rpcs.BAR, Rpcs.BAZ);
101
102             fooReg.close();
103             assertOperationKeys(rpcRouter, Rpcs.BAR, Rpcs.BAZ);
104             barReg.close();
105             assertOperationKeys(rpcRouter);
106         }
107     }
108
109
110     private static void assertOperationKeys(final DOMRpcRouter router, final QName... keys) {
111         assertEquals(Set.of(keys), router.routingTable().getOperations().keySet());
112     }
113
114     @Test
115     public void testFailedInvokeRpc() {
116         try (var rpcRouter = rpcsRouter()) {
117             final ListenableFuture<?> future = rpcRouter.rpcService().invokeRpc(Rpcs.FOO, null);
118             final Throwable cause = assertThrows(ExecutionException.class, () -> Futures.getDone(future)).getCause();
119             assertThat(cause, instanceOf(DOMRpcImplementationNotAvailableException.class));
120             assertEquals("No implementation of RPC (rpcs)foo available", cause.getMessage());
121         }
122     }
123
124     @Test
125     public void testRpcListener() {
126         try (var rpcRouter = new DOMRpcRouter()) {
127             assertEquals(List.of(), rpcRouter.listeners());
128
129             final var listener = mock(DOMRpcAvailabilityListener.class);
130             doCallRealMethod().when(listener).acceptsImplementation(any());
131             doNothing().when(listener).onRpcAvailable(any());
132             doNothing().when(listener).onRpcUnavailable(any());
133
134             final var reg = rpcRouter.rpcService().registerRpcListener(listener);
135             assertNotNull(reg);
136             assertEquals(List.of(reg), rpcRouter.listeners());
137
138             final var implReg = rpcRouter.rpcProviderService().registerRpcImplementation(
139                 getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.FOO, null));
140             verify(listener, timeout(1000)).onRpcAvailable(any());
141
142             implReg.close();
143             verify(listener, timeout(1000)).onRpcUnavailable(any());
144
145             reg.close();
146             assertEquals(List.of(), rpcRouter.listeners());
147         }
148     }
149
150     @Test
151     public void testActionListener() {
152         try (var rpcRouter = new DOMRpcRouter()) {
153             assertEquals(List.of(), rpcRouter.actionListeners());
154
155             final var listener = mock(AvailabilityListener.class);
156             final var availability = rpcRouter.actionService().extension(DOMActionAvailabilityExtension.class);
157             assertNotNull(availability);
158             final var reg = availability.registerAvailabilityListener(listener);
159             assertNotNull(reg);
160             assertEquals(List.of(reg), rpcRouter.actionListeners());
161
162             // FIXME: register implementation and verify notification
163
164             reg.close();
165             assertEquals(List.of(), rpcRouter.actionListeners());
166         }
167     }
168
169     @Test
170     public void onGlobalContextUpdated() {
171         try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
172             final DOMRpcRoutingTable routingTableOriginal = rpcRouter.routingTable();
173             rpcRouter.onModelContextUpdated(TestModel.createTestContext());
174             assertNotEquals(routingTableOriginal, rpcRouter.routingTable());
175         }
176     }
177
178     @Test
179     public void testClose() {
180         final var reg = mock(Registration.class);
181         doNothing().when(reg).close();
182         final DOMSchemaService schema = mock(DOMSchemaService.class);
183         doReturn(reg).when(schema).registerSchemaContextListener(any());
184
185         final var rpcRouter = new DOMRpcRouter(schema);
186         rpcRouter.close();
187
188         final var svc = rpcRouter.rpcProviderService();
189         assertThrows(RejectedExecutionException.class, () -> svc.registerRpcImplementation(getTestRpcImplementation(),
190             DOMRpcIdentifier.create(Rpcs.FOO, null)));
191     }
192
193     @Test
194     public void testActionInstanceRouting() throws ExecutionException {
195         try (var rpcRouter = actionsRouter()) {
196             final var actionProvider = rpcRouter.actionProviderService();
197             assertNotNull(actionProvider);
198             final var actionConsumer = rpcRouter.actionService();
199             assertNotNull(actionConsumer);
200
201             try (var reg = actionProvider.registerActionImplementation(IMPL,
202                 DOMActionInstance.of(Actions.BAZ_TYPE, LogicalDatastoreType.OPERATIONAL, BAZ_PATH_GOOD))) {
203
204                 assertAvailable(actionConsumer, BAZ_PATH_GOOD);
205                 assertUnavailable(actionConsumer, BAZ_PATH_BAD);
206             }
207
208             assertUnavailable(actionConsumer, BAZ_PATH_BAD);
209             assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
210         }
211     }
212
213     @Test
214     public void testActionDatastoreRouting() throws ExecutionException {
215         try (var rpcRouter = actionsRouter()) {
216             final var actionProvider = rpcRouter.actionProviderService();
217             assertNotNull(actionProvider);
218             final var actionConsumer = rpcRouter.actionService();
219             assertNotNull(actionConsumer);
220
221             try (var reg = actionProvider.registerActionImplementation(IMPL,
222                 DOMActionInstance.of(Actions.BAZ_TYPE, LogicalDatastoreType.OPERATIONAL,
223                     YangInstanceIdentifier.of()))) {
224
225                 assertAvailable(actionConsumer, BAZ_PATH_GOOD);
226                 assertAvailable(actionConsumer, BAZ_PATH_BAD);
227             }
228
229             assertUnavailable(actionConsumer, BAZ_PATH_BAD);
230             assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
231         }
232     }
233
234     private static DOMRpcRouter actionsRouter() {
235         final DOMRpcRouter router = new DOMRpcRouter();
236         router.onModelContextUpdated(Actions.CONTEXT);
237         return router;
238     }
239
240     private static DOMRpcRouter rpcsRouter() {
241         final DOMRpcRouter router = new DOMRpcRouter();
242         router.onModelContextUpdated(Rpcs.CONTEXT);
243         return router;
244     }
245
246     private static void assertAvailable(final DOMActionService actionService, final YangInstanceIdentifier path)
247             throws ExecutionException {
248         final DOMActionResult result = Futures.getDone(invokeBaz(actionService, path));
249         assertEquals(List.of(), result.getErrors());
250     }
251
252     private static void assertUnavailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
253         final ListenableFuture<? extends DOMActionResult> future = invokeBaz(actionService, path);
254         final ExecutionException ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future));
255         assertThat(ex.getCause(), instanceOf(DOMActionNotAvailableException.class));
256     }
257
258     private static ListenableFuture<? extends DOMActionResult> invokeBaz(final DOMActionService actionService,
259             final YangInstanceIdentifier path) {
260         return actionService.invokeAction(Actions.BAZ_TYPE,
261             DOMDataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, path),
262             Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(Actions.INPUT)).build());
263     }
264 }