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