Add DOMRpcProviderService bulk registration
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / DOMRpcRouter.java
1 /*
2  * Copyright (c) 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 com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.annotations.VisibleForTesting;
15 import com.google.common.collect.ClassToInstanceMap;
16 import com.google.common.collect.Collections2;
17 import com.google.common.collect.ImmutableClassToInstanceMap;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableList.Builder;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.common.collect.ImmutableSet;
22 import com.google.common.collect.MapDifference;
23 import com.google.common.collect.MapDifference.ValueDifference;
24 import com.google.common.collect.Maps;
25 import com.google.common.collect.Sets;
26 import com.google.common.util.concurrent.Futures;
27 import com.google.common.util.concurrent.ListenableFuture;
28 import com.google.common.util.concurrent.ThreadFactoryBuilder;
29 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Map.Entry;
37 import java.util.Optional;
38 import java.util.Set;
39 import java.util.concurrent.ExecutorService;
40 import java.util.concurrent.Executors;
41 import java.util.concurrent.ThreadFactory;
42 import javax.inject.Inject;
43 import javax.inject.Singleton;
44 import org.checkerframework.checker.lock.qual.GuardedBy;
45 import org.eclipse.jdt.annotation.NonNull;
46 import org.eclipse.jdt.annotation.NonNullByDefault;
47 import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension;
48 import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension.AvailabilityListener;
49 import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
50 import org.opendaylight.mdsal.dom.api.DOMActionInstance;
51 import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
52 import org.opendaylight.mdsal.dom.api.DOMActionProviderService;
53 import org.opendaylight.mdsal.dom.api.DOMActionProviderServiceExtension;
54 import org.opendaylight.mdsal.dom.api.DOMActionResult;
55 import org.opendaylight.mdsal.dom.api.DOMActionService;
56 import org.opendaylight.mdsal.dom.api.DOMActionServiceExtension;
57 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
58 import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
59 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
60 import org.opendaylight.mdsal.dom.api.DOMRpcImplementation;
61 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
62 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationRegistration;
63 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
64 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
65 import org.opendaylight.mdsal.dom.api.DOMRpcService;
66 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
67 import org.opendaylight.mdsal.dom.spi.AbstractDOMRpcImplementationRegistration;
68 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
69 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
70 import org.opendaylight.yangtools.concepts.AbstractRegistration;
71 import org.opendaylight.yangtools.concepts.ListenerRegistration;
72 import org.opendaylight.yangtools.concepts.ObjectRegistration;
73 import org.opendaylight.yangtools.yang.common.QName;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
75 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
78 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
79 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
80 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
83
84 @Singleton
85 public final class DOMRpcRouter extends AbstractRegistration
86         implements DOMRpcRouterServices, EffectiveModelContextListener {
87     private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setNameFormat(
88             "DOMRpcRouter-listener-%s").setDaemon(true).build();
89
90     private final ExecutorService listenerNotifier = Executors.newSingleThreadExecutor(THREAD_FACTORY);
91     private final @NonNull DOMActionProviderService actionProviderService = new ActionProviderServiceFacade();
92     private final @NonNull DOMActionService actionService = new ActionServiceFacade();
93     private final @NonNull DOMRpcProviderService rpcProviderService = new RpcProviderServiceFacade();
94     private final @NonNull DOMRpcService rpcService = new RpcServiceFacade();
95
96     @GuardedBy("this")
97     private Collection<Registration<?>> listeners = Collections.emptyList();
98
99     @GuardedBy("this")
100     private Collection<ActionRegistration<?>> actionListeners = Collections.emptyList();
101
102     private volatile DOMRpcRoutingTable routingTable = DOMRpcRoutingTable.EMPTY;
103
104     private volatile DOMActionRoutingTable actionRoutingTable = DOMActionRoutingTable.EMPTY;
105
106     private ListenerRegistration<?> listenerRegistration;
107
108     @Inject
109     public static DOMRpcRouter newInstance(final DOMSchemaService schemaService) {
110         final DOMRpcRouter rpcRouter = new DOMRpcRouter();
111         rpcRouter.listenerRegistration = schemaService.registerSchemaContextListener(rpcRouter);
112         return rpcRouter;
113     }
114
115     @Override
116     public DOMActionService getActionService() {
117         return actionService;
118     }
119
120     @Override
121     public DOMActionProviderService getActionProviderService() {
122         return actionProviderService;
123     }
124
125     @Override
126     public DOMRpcService getRpcService() {
127         return rpcService;
128     }
129
130     @Override
131     public DOMRpcProviderService getRpcProviderService() {
132         return rpcProviderService;
133     }
134
135     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
136             justification = "https://github.com/spotbugs/spotbugs/issues/811")
137     private synchronized void removeRpcImplementation(final DOMRpcImplementation implementation,
138             final Set<DOMRpcIdentifier> rpcs) {
139         final DOMRpcRoutingTable oldTable = routingTable;
140         final DOMRpcRoutingTable newTable = (DOMRpcRoutingTable) oldTable.remove(implementation, rpcs);
141         routingTable = newTable;
142
143         listenerNotifier.execute(() -> notifyRemoved(newTable, implementation));
144     }
145
146     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
147             justification = "https://github.com/spotbugs/spotbugs/issues/811")
148     private synchronized void removeRpcImplementations(final Map<DOMRpcIdentifier, DOMRpcImplementation> map) {
149         final DOMRpcRoutingTable oldTable = routingTable;
150         final DOMRpcRoutingTable newTable = (DOMRpcRoutingTable) oldTable.removeAll(map);
151         routingTable = newTable;
152
153         listenerNotifier.execute(() -> notifyRemoved(newTable, map.values()));
154     }
155
156     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
157             justification = "https://github.com/spotbugs/spotbugs/issues/811")
158     private synchronized void removeActionImplementation(final DOMActionImplementation implementation,
159             final Set<DOMActionInstance> actions) {
160         final DOMActionRoutingTable oldTable = actionRoutingTable;
161         final DOMActionRoutingTable newTable = (DOMActionRoutingTable) oldTable.remove(implementation, actions);
162         actionRoutingTable = newTable;
163
164         listenerNotifier.execute(() -> notifyActionChanged(newTable, implementation));
165     }
166
167     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
168             justification = "https://github.com/spotbugs/spotbugs/issues/811")
169     private synchronized void removeListener(final ListenerRegistration<? extends DOMRpcAvailabilityListener> reg) {
170         listeners = ImmutableList.copyOf(Collections2.filter(listeners, input -> !reg.equals(input)));
171     }
172
173     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
174             justification = "https://github.com/spotbugs/spotbugs/issues/811")
175     private synchronized void removeActionListener(final ListenerRegistration<? extends AvailabilityListener> reg) {
176         actionListeners = ImmutableList.copyOf(Collections2.filter(actionListeners, input -> !reg.equals(input)));
177     }
178
179     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
180             justification = "https://github.com/spotbugs/spotbugs/issues/811")
181     private synchronized void notifyAdded(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
182         for (Registration<?> l : listeners) {
183             l.addRpc(newTable, impl);
184         }
185     }
186
187     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
188             justification = "https://github.com/spotbugs/spotbugs/issues/811")
189     private synchronized void notifyAdded(final DOMRpcRoutingTable newTable,
190             final Collection<? extends DOMRpcImplementation> impls) {
191         for (Registration<?> l : listeners) {
192             for (DOMRpcImplementation impl : impls) {
193                 l.addRpc(newTable, impl);
194             }
195         }
196     }
197
198     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
199             justification = "https://github.com/spotbugs/spotbugs/issues/811")
200     private synchronized void notifyRemoved(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
201         for (Registration<?> l : listeners) {
202             l.removeRpc(newTable, impl);
203         }
204     }
205
206     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
207             justification = "https://github.com/spotbugs/spotbugs/issues/811")
208     private synchronized void notifyRemoved(final DOMRpcRoutingTable newTable,
209             final Collection<? extends DOMRpcImplementation> impls) {
210         for (Registration<?> l : listeners) {
211             for (DOMRpcImplementation impl : impls) {
212                 l.removeRpc(newTable, impl);
213             }
214         }
215     }
216
217     private synchronized void notifyActionChanged(final DOMActionRoutingTable newTable,
218             final DOMActionImplementation impl) {
219         for (ActionRegistration<?> l : actionListeners) {
220             l.actionChanged(newTable, impl);
221         }
222     }
223
224     @Override
225     public synchronized void onModelContextUpdated(final EffectiveModelContext newModelContext) {
226         final DOMRpcRoutingTable oldTable = routingTable;
227         final DOMRpcRoutingTable newTable = (DOMRpcRoutingTable) oldTable.setSchemaContext(newModelContext);
228         routingTable = newTable;
229
230         final DOMActionRoutingTable oldActionTable = actionRoutingTable;
231         final DOMActionRoutingTable newActionTable =
232                 (DOMActionRoutingTable) oldActionTable.setSchemaContext(newModelContext);
233         actionRoutingTable = newActionTable;
234     }
235
236     @Override
237     protected void removeRegistration() {
238         if (listenerRegistration != null) {
239             listenerRegistration.close();
240             listenerRegistration = null;
241         }
242         listenerNotifier.shutdown();
243     }
244
245     @VisibleForTesting
246     synchronized Collection<?> listeners() {
247         return listeners;
248     }
249
250     @VisibleForTesting
251     DOMRpcRoutingTable routingTable() {
252         return routingTable;
253     }
254
255     private static final class Registration<T extends DOMRpcAvailabilityListener>
256         extends AbstractListenerRegistration<T> {
257
258         private Map<QName, Set<YangInstanceIdentifier>> prevRpcs;
259         private DOMRpcRouter router;
260
261         Registration(final DOMRpcRouter router, final T listener, final Map<QName, Set<YangInstanceIdentifier>> rpcs) {
262             super(listener);
263             this.router = requireNonNull(router);
264             this.prevRpcs = requireNonNull(rpcs);
265         }
266
267         @Override
268         protected void removeRegistration() {
269             router.removeListener(this);
270             router = null;
271         }
272
273         void initialTable() {
274             final Collection<DOMRpcIdentifier> added = new ArrayList<>();
275             for (Entry<QName, Set<YangInstanceIdentifier>> e : prevRpcs.entrySet()) {
276                 added.addAll(Collections2.transform(e.getValue(), i -> DOMRpcIdentifier.create(e.getKey(), i)));
277             }
278             if (!added.isEmpty()) {
279                 getInstance().onRpcAvailable(added);
280             }
281         }
282
283         void addRpc(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
284             final T l = getInstance();
285             if (!l.acceptsImplementation(impl)) {
286                 return;
287             }
288
289             final Map<QName, Set<YangInstanceIdentifier>> rpcs = verifyNotNull(newTable.getOperations(l));
290             final MapDifference<QName, Set<YangInstanceIdentifier>> diff = Maps.difference(prevRpcs, rpcs);
291
292             final Collection<DOMRpcIdentifier> added = new ArrayList<>();
293             for (Entry<QName, Set<YangInstanceIdentifier>> e : diff.entriesOnlyOnRight().entrySet()) {
294                 added.addAll(Collections2.transform(e.getValue(), i -> DOMRpcIdentifier.create(e.getKey(), i)));
295             }
296             for (Entry<QName, ValueDifference<Set<YangInstanceIdentifier>>> e : diff.entriesDiffering().entrySet()) {
297                 for (YangInstanceIdentifier i : Sets.difference(e.getValue().rightValue(), e.getValue().leftValue())) {
298                     added.add(DOMRpcIdentifier.create(e.getKey(), i));
299                 }
300             }
301
302             prevRpcs = rpcs;
303             if (!added.isEmpty()) {
304                 l.onRpcAvailable(added);
305             }
306         }
307
308         void removeRpc(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
309             final T l = getInstance();
310             if (!l.acceptsImplementation(impl)) {
311                 return;
312             }
313
314             final Map<QName, Set<YangInstanceIdentifier>> rpcs = verifyNotNull(newTable.getOperations(l));
315             final MapDifference<QName, Set<YangInstanceIdentifier>> diff = Maps.difference(prevRpcs, rpcs);
316
317             final Collection<DOMRpcIdentifier> removed = new ArrayList<>();
318             for (Entry<QName, Set<YangInstanceIdentifier>> e : diff.entriesOnlyOnLeft().entrySet()) {
319                 removed.addAll(Collections2.transform(e.getValue(), i -> DOMRpcIdentifier.create(e.getKey(), i)));
320             }
321             for (Entry<QName, ValueDifference<Set<YangInstanceIdentifier>>> e : diff.entriesDiffering().entrySet()) {
322                 for (YangInstanceIdentifier i : Sets.difference(e.getValue().leftValue(), e.getValue().rightValue())) {
323                     removed.add(DOMRpcIdentifier.create(e.getKey(), i));
324                 }
325             }
326
327             prevRpcs = rpcs;
328             if (!removed.isEmpty()) {
329                 l.onRpcUnavailable(removed);
330             }
331         }
332     }
333
334     private static final class ActionRegistration<T extends AvailabilityListener>
335         extends AbstractListenerRegistration<T> {
336
337         private Map<Absolute, Set<DOMDataTreeIdentifier>> prevActions;
338         private DOMRpcRouter router;
339
340         ActionRegistration(final DOMRpcRouter router, final T listener,
341                 final Map<Absolute, Set<DOMDataTreeIdentifier>> actions) {
342             super(listener);
343             this.router = requireNonNull(router);
344             this.prevActions = requireNonNull(actions);
345         }
346
347         @Override
348         protected void removeRegistration() {
349             router.removeActionListener(this);
350             router = null;
351         }
352
353         void initialTable() {
354             final Collection<DOMActionInstance> added = new ArrayList<>();
355             for (Entry<Absolute, Set<DOMDataTreeIdentifier>> e : prevActions.entrySet()) {
356                 added.addAll(Collections2.transform(e.getValue(), i -> DOMActionInstance.of(e.getKey(), i)));
357             }
358             if (!added.isEmpty()) {
359                 getInstance().onActionsChanged(ImmutableSet.of(), ImmutableSet.copyOf(added));
360             }
361         }
362
363         void actionChanged(final DOMActionRoutingTable newTable, final DOMActionImplementation impl) {
364             final T l = getInstance();
365             if (!l.acceptsImplementation(impl)) {
366                 return;
367             }
368
369             final Map<Absolute, Set<DOMDataTreeIdentifier>> actions = verifyNotNull(newTable.getOperations(l));
370             final MapDifference<Absolute, Set<DOMDataTreeIdentifier>> diff = Maps.difference(prevActions, actions);
371
372             final Set<DOMActionInstance> removed = new HashSet<>();
373             final Set<DOMActionInstance> added = new HashSet<>();
374
375             for (Entry<Absolute, Set<DOMDataTreeIdentifier>> e : diff.entriesOnlyOnLeft().entrySet()) {
376                 removed.addAll(Collections2.transform(e.getValue(), i -> DOMActionInstance.of(e.getKey(), i)));
377             }
378
379             for (Entry<Absolute, Set<DOMDataTreeIdentifier>> e : diff.entriesOnlyOnRight().entrySet()) {
380                 added.addAll(Collections2.transform(e.getValue(), i -> DOMActionInstance.of(e.getKey(), i)));
381             }
382
383             for (Entry<Absolute, ValueDifference<Set<DOMDataTreeIdentifier>>> e : diff.entriesDiffering().entrySet()) {
384                 for (DOMDataTreeIdentifier i : Sets.difference(e.getValue().leftValue(), e.getValue().rightValue())) {
385                     removed.add(DOMActionInstance.of(e.getKey(), i));
386                 }
387
388                 for (DOMDataTreeIdentifier i : Sets.difference(e.getValue().rightValue(), e.getValue().leftValue())) {
389                     added.add(DOMActionInstance.of(e.getKey(), i));
390                 }
391             }
392
393             prevActions = actions;
394             if (!removed.isEmpty() || !added.isEmpty()) {
395                 l.onActionsChanged(removed, added);
396             }
397         }
398     }
399
400     @NonNullByDefault
401     private final class ActionAvailabilityFacade implements DOMActionAvailabilityExtension {
402         @Override
403         public <T extends AvailabilityListener> ListenerRegistration<T> registerAvailabilityListener(final T listener) {
404             synchronized (DOMRpcRouter.this) {
405                 final ActionRegistration<T> ret = new ActionRegistration<>(DOMRpcRouter.this,
406                     listener, actionRoutingTable.getOperations(listener));
407                 final Builder<ActionRegistration<?>> b = ImmutableList.builder();
408                 b.addAll(actionListeners);
409                 b.add(ret);
410                 actionListeners = b.build();
411
412                 listenerNotifier.execute(ret::initialTable);
413                 return ret;
414             }
415         }
416     }
417
418     @NonNullByDefault
419     private final class ActionServiceFacade implements DOMActionService {
420         private final ClassToInstanceMap<DOMActionServiceExtension> extensions = ImmutableClassToInstanceMap.of(
421             DOMActionAvailabilityExtension.class, new ActionAvailabilityFacade());
422
423         @Override
424         public ClassToInstanceMap<DOMActionServiceExtension> getExtensions() {
425             return extensions;
426         }
427
428         @Override
429         public ListenableFuture<? extends DOMActionResult> invokeAction(final Absolute type,
430                 final DOMDataTreeIdentifier path, final ContainerNode input) {
431             final DOMActionRoutingTableEntry entry = (DOMActionRoutingTableEntry) actionRoutingTable.getEntry(type);
432             if (entry == null) {
433                 return Futures.immediateFailedFuture(
434                     new DOMActionNotAvailableException("No implementation of Action %s available", type));
435             }
436
437             return OperationInvocation.invoke(entry, type, path, requireNonNull(input));
438         }
439     }
440
441     @NonNullByDefault
442     private final class ActionProviderServiceFacade implements DOMActionProviderService {
443         @Override
444         public ClassToInstanceMap<DOMActionProviderServiceExtension> getExtensions() {
445             return ImmutableClassToInstanceMap.of();
446         }
447
448         @Override
449         public <T extends DOMActionImplementation> ObjectRegistration<T> registerActionImplementation(
450             final T implementation, final Set<DOMActionInstance> instances) {
451
452             synchronized (DOMRpcRouter.this) {
453                 final DOMActionRoutingTable oldTable = actionRoutingTable;
454                 final DOMActionRoutingTable newTable = (DOMActionRoutingTable) oldTable.add(implementation, instances);
455                 actionRoutingTable = newTable;
456
457                 listenerNotifier.execute(() -> notifyActionChanged(newTable, implementation));
458             }
459
460             return new AbstractObjectRegistration<>(implementation) {
461                 @Override
462                 protected void removeRegistration() {
463                     removeActionImplementation(getInstance(), instances);
464                 }
465             };
466         }
467     }
468
469     private final class RpcServiceFacade implements DOMRpcService {
470         @Override
471         public ListenableFuture<? extends DOMRpcResult> invokeRpc(final QName type, final NormalizedNode<?, ?> input) {
472             final AbstractDOMRpcRoutingTableEntry entry = (AbstractDOMRpcRoutingTableEntry) routingTable.getEntry(type);
473             if (entry == null) {
474                 return Futures.immediateFailedFuture(
475                     new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available", type));
476             }
477
478             return OperationInvocation.invoke(entry, requireNonNull(input));
479         }
480
481         @Override
482         public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(final T listener) {
483             synchronized (DOMRpcRouter.this) {
484                 final Registration<T> ret = new Registration<>(DOMRpcRouter.this, listener,
485                     routingTable.getOperations(listener));
486                 final Builder<Registration<?>> b = ImmutableList.builder();
487                 b.addAll(listeners);
488                 b.add(ret);
489                 listeners = b.build();
490
491                 listenerNotifier.execute(ret::initialTable);
492                 return ret;
493             }
494         }
495     }
496
497     private final class RpcProviderServiceFacade implements DOMRpcProviderService {
498         @Override
499         public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(
500                 final T implementation, final DOMRpcIdentifier... rpcs) {
501             return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs));
502         }
503
504         @Override
505         public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(
506                 final T implementation, final Set<DOMRpcIdentifier> rpcs) {
507
508             synchronized (DOMRpcRouter.this) {
509                 final DOMRpcRoutingTable oldTable = routingTable;
510                 final DOMRpcRoutingTable newTable = (DOMRpcRoutingTable) oldTable.add(implementation, rpcs);
511                 routingTable = newTable;
512
513                 listenerNotifier.execute(() -> notifyAdded(newTable, implementation));
514             }
515
516             return new AbstractDOMRpcImplementationRegistration<>(implementation) {
517                 @Override
518                 protected void removeRegistration() {
519                     removeRpcImplementation(getInstance(), rpcs);
520                 }
521             };
522         }
523
524         @Override
525         public org.opendaylight.yangtools.concepts.Registration registerRpcImplementations(
526                 final Map<DOMRpcIdentifier, DOMRpcImplementation> map) {
527             final ImmutableMap<DOMRpcIdentifier, DOMRpcImplementation> defensive = ImmutableMap.copyOf(map);
528             checkArgument(!map.isEmpty());
529
530             synchronized (DOMRpcRouter.this) {
531                 final DOMRpcRoutingTable oldTable = routingTable;
532                 final DOMRpcRoutingTable newTable = (DOMRpcRoutingTable) oldTable.addAll(defensive);
533                 routingTable = newTable;
534
535                 listenerNotifier.execute(() -> notifyAdded(newTable, defensive.values()));
536             }
537
538             return new AbstractRegistration() {
539                 @Override
540                 protected void removeRegistration() {
541                     removeRpcImplementations(defensive);
542                 }
543             };
544         }
545     }
546
547     static final class OperationInvocation {
548         private static final Logger LOG = LoggerFactory.getLogger(OperationInvocation.class);
549
550         static ListenableFuture<? extends DOMActionResult> invoke(final DOMActionRoutingTableEntry entry,
551                 final Absolute type, final DOMDataTreeIdentifier path, final ContainerNode input) {
552             return entry.getImplementations(path).get(0).invokeAction(type, path, input);
553         }
554
555         static ListenableFuture<? extends DOMRpcResult> invoke(final AbstractDOMRpcRoutingTableEntry entry,
556                 final NormalizedNode<?, ?> input) {
557             if (entry instanceof UnknownDOMRpcRoutingTableEntry) {
558                 return Futures.immediateFailedFuture(
559                     new DOMRpcImplementationNotAvailableException("%s is not resolved to an RPC", entry.getType()));
560             } else if (entry instanceof RoutedDOMRpcRoutingTableEntry) {
561                 return invokeRoutedRpc((RoutedDOMRpcRoutingTableEntry) entry, input);
562             } else if (entry instanceof GlobalDOMRpcRoutingTableEntry) {
563                 return invokeGlobalRpc((GlobalDOMRpcRoutingTableEntry) entry, input);
564             }
565
566             return Futures.immediateFailedFuture(
567                 new DOMRpcImplementationNotAvailableException("Unsupported RPC entry."));
568         }
569
570         private static ListenableFuture<? extends DOMRpcResult> invokeRoutedRpc(
571                 final RoutedDOMRpcRoutingTableEntry entry, final NormalizedNode<?, ?> input) {
572             final Optional<NormalizedNode<?, ?>> maybeKey = NormalizedNodes.findNode(input,
573                 entry.getRpcId().getContextReference());
574
575             // Routing key is present, attempt to deliver as a routed RPC
576             if (maybeKey.isPresent()) {
577                 final NormalizedNode<?, ?> key = maybeKey.get();
578                 final Object value = key.getValue();
579                 if (value instanceof YangInstanceIdentifier) {
580                     final YangInstanceIdentifier iid = (YangInstanceIdentifier) value;
581
582                     // Find a DOMRpcImplementation for a specific iid
583                     final List<DOMRpcImplementation> specificImpls = entry.getImplementations(iid);
584                     if (specificImpls != null) {
585                         return specificImpls.get(0)
586                             .invokeRpc(DOMRpcIdentifier.create(entry.getType(), iid), input);
587                     }
588
589                     LOG.debug("No implementation for context {} found will now look for wildcard id", iid);
590
591                     // Find a DOMRpcImplementation for a wild card. Usually remote-rpc-connector would register an
592                     // implementation this way
593                     final List<DOMRpcImplementation> mayBeRemoteImpls =
594                         entry.getImplementations(YangInstanceIdentifier.empty());
595
596                     if (mayBeRemoteImpls != null) {
597                         return mayBeRemoteImpls.get(0)
598                             .invokeRpc(DOMRpcIdentifier.create(entry.getType(), iid), input);
599                     }
600
601                 } else {
602                     LOG.warn("Ignoring wrong context value {}", value);
603                 }
604             }
605
606             final List<DOMRpcImplementation> impls = entry.getImplementations(null);
607             if (impls != null) {
608                 return impls.get(0).invokeRpc(entry.getRpcId(), input);
609             }
610
611             return Futures.immediateFailedFuture(
612                 new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available",
613                     entry.getType()));
614         }
615
616         private static ListenableFuture<? extends DOMRpcResult> invokeGlobalRpc(
617                 final GlobalDOMRpcRoutingTableEntry entry, final NormalizedNode<?, ?> input) {
618             return entry.getImplementations(YangInstanceIdentifier.empty()).get(0).invokeRpc(entry.getRpcId(), input);
619         }
620     }
621 }