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