Bug 1355: Fixed incorrect input keyword use in rpc
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / impl / connect / dom / DomToBindingRpcForwarder.java
1 package org.opendaylight.controller.sal.binding.impl.connect.dom;
2
3 import static com.google.common.base.Preconditions.checkArgument;
4 import static com.google.common.base.Preconditions.checkState;
5
6 import com.google.common.base.Function;
7 import com.google.common.collect.FluentIterable;
8 import com.google.common.collect.ImmutableSet;
9 import com.google.common.util.concurrent.Futures;
10 import com.google.common.util.concurrent.ListenableFuture;
11 import java.lang.ref.WeakReference;
12 import java.lang.reflect.InvocationHandler;
13 import java.lang.reflect.Method;
14 import java.lang.reflect.Proxy;
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.WeakHashMap;
19 import java.util.concurrent.Callable;
20 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
21 import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
22 import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
23 import org.opendaylight.controller.sal.core.api.Broker;
24 import org.opendaylight.controller.sal.core.api.RpcImplementation;
25 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
26 import org.opendaylight.yangtools.concepts.CompositeObjectRegistration;
27 import org.opendaylight.yangtools.concepts.ObjectRegistration;
28
29 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.opendaylight.yangtools.yang.binding.RpcService;
33 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
34 import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
35 import org.opendaylight.yangtools.yang.binding.BindingMapping;
36 import org.opendaylight.yangtools.yang.common.QName;
37 import org.opendaylight.yangtools.yang.common.RpcResult;
38 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
39 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 class DomToBindingRpcForwarder implements RpcImplementation, InvocationHandler {
44
45     private final Logger LOG = LoggerFactory.getLogger(DomToBindingRpcForwarder.class);
46
47     private final Set<QName> supportedRpcs;
48     private final WeakReference<Class<? extends RpcService>> rpcServiceType;
49     private Set<org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration> registrations;
50     private final Map<QName, RpcInvocationStrategy> strategiesByQName = new HashMap<>();
51     private final WeakHashMap<Method, RpcInvocationStrategy> strategiesByMethod = new WeakHashMap<>();
52     private final RpcService proxy;
53     private ObjectRegistration<?> forwarderRegistration;
54     private boolean registrationInProgress = false;
55
56     private final RpcProvisionRegistry biRpcRegistry;
57     private final RpcProviderRegistry baRpcRegistry;
58     private final RpcProviderRegistryImpl baRpcRegistryImpl;
59
60     private final Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier;
61
62     private final static Method EQUALS_METHOD;
63
64     static {
65         try {
66             EQUALS_METHOD = Object.class.getMethod("equals", Object.class);
67         } catch (Exception e) {
68             throw new RuntimeException(e);
69         }
70     }
71
72     public DomToBindingRpcForwarder(final Class<? extends RpcService> service, final BindingIndependentMappingService mappingService,
73         final RpcProvisionRegistry biRpcRegistry, final RpcProviderRegistry baRpcRegistry) {
74         this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
75         this.supportedRpcs = mappingService.getRpcQNamesFor(service);
76
77         toDOMInstanceIdentifier = new Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() {
78
79             @Override
80             public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(final InstanceIdentifier<?> input) {
81                 return mappingService.toDataDom(input);
82             }
83         };
84
85         this.biRpcRegistry = biRpcRegistry;
86         this.baRpcRegistry = baRpcRegistry;
87         baRpcRegistryImpl = (RpcProviderRegistryImpl) baRpcRegistry;
88
89         Class<?> cls = rpcServiceType.get();
90         ClassLoader clsLoader = cls.getClassLoader();
91         proxy =(RpcService) Proxy.newProxyInstance(clsLoader, new Class<?>[] { cls }, this);
92         createStrategies(mappingService);
93     }
94
95     /**
96      * Constructor for Routed RPC Forwareder.
97      *
98      * @param service
99      * @param context
100      */
101     public DomToBindingRpcForwarder(final Class<? extends RpcService> service,
102         final Class<? extends BaseIdentity> context, final BindingIndependentMappingService mappingService,
103         final RpcProvisionRegistry biRpcRegistry, final RpcProviderRegistry baRpcRegistry) {
104         this(service, mappingService, biRpcRegistry, baRpcRegistry);
105
106         final ImmutableSet.Builder<Broker.RoutedRpcRegistration> registrationsBuilder = ImmutableSet.builder();
107         try {
108             for (QName rpc : supportedRpcs) {
109                 registrationsBuilder.add(biRpcRegistry.addRoutedRpcImplementation(rpc, this));
110             }
111             createDefaultDomForwarder();
112         } catch (Exception e) {
113             LOG.error("Could not forward Rpcs of type {}", service.getName(), e);
114         }
115         registrations = registrationsBuilder.build();
116     }
117
118
119
120     private void createStrategies(final BindingIndependentMappingService mappingService) {
121         try {
122             for (QName rpc : supportedRpcs) {
123                 RpcInvocationStrategy strategy = createInvocationStrategy(rpc, rpcServiceType.get(), mappingService);
124                 strategiesByMethod.put(strategy.targetMethod, strategy);
125                 strategiesByQName.put(rpc, strategy);
126             }
127         } catch (Exception e) {
128             LOG.error("Could not forward Rpcs of type {}", rpcServiceType.get(), e);
129         }
130
131     }
132
133     /**
134      * Registers RPC Forwarder to DOM Broker,
135      * this means Binding Aware Broker has implementation of RPC
136      * which is registered to it.
137      *
138      * If RPC Forwarder was previously registered to DOM Broker
139      * or to Bidning Broker this method is noop to prevent
140      * creating forwarding loop.
141      *
142      */
143     public void registerToDOMBroker() {
144         if(!registrationInProgress && forwarderRegistration == null) {
145             registrationInProgress = true;
146             CompositeObjectRegistration.CompositeObjectRegistrationBuilder<DomToBindingRpcForwarder> builder = CompositeObjectRegistration.builderFor(this);
147             try {
148                 for (QName rpc : supportedRpcs) {
149                     builder.add(biRpcRegistry.addRpcImplementation(rpc, this));
150                 }
151             } catch (Exception e) {
152                 LOG.error("Could not forward Rpcs of type {}", rpcServiceType.get(), e);
153             }
154             this.forwarderRegistration = builder.toInstance();
155             registrationInProgress = false;
156         }
157     }
158
159
160     public void registerPaths(final Class<? extends BaseIdentity> context,
161         final Class<? extends RpcService> service, final Set<InstanceIdentifier<?>> set) {
162         QName ctx = BindingReflections.findQName(context);
163         for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
164             toDOMInstanceIdentifier)) {
165             for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
166                 reg.registerPath(ctx, path);
167             }
168         }
169     }
170
171     @Override
172     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
173         if (EQUALS_METHOD.equals(method)) {
174             return false;
175         }
176         RpcInvocationStrategy strategy = strategiesByMethod.get(method);
177         checkState(strategy != null);
178         checkArgument(args.length <= 2);
179         if (args.length == 1) {
180             checkArgument(args[0] instanceof DataObject);
181             return strategy.forwardToDomBroker((DataObject) args[0]);
182         }
183         return strategy.forwardToDomBroker(null);
184     }
185
186     public void removePaths(final Class<? extends BaseIdentity> context, final Class<? extends RpcService> service,
187         final Set<InstanceIdentifier<?>> set) {
188         QName ctx = BindingReflections.findQName(context);
189         for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
190             toDOMInstanceIdentifier)) {
191             for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
192                 reg.unregisterPath(ctx, path);
193             }
194         }
195     }
196
197     @Override
198     public Set<QName> getSupportedRpcs() {
199         return supportedRpcs;
200     }
201
202     @SuppressWarnings({ "unchecked", "rawtypes" })
203     public void createDefaultDomForwarder() {
204         if (baRpcRegistryImpl != null) {
205             Class<?> cls = rpcServiceType.get();
206             ClassLoader clsLoader = cls.getClassLoader();
207             RpcService proxy = (RpcService) Proxy.newProxyInstance(clsLoader, new Class<?>[] { cls }, this);
208
209             RpcRouter rpcRouter = baRpcRegistryImpl.getRpcRouter(rpcServiceType.get());
210             rpcRouter.registerDefaultService(proxy);
211         }
212     }
213
214     @Override
215     public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode domInput) {
216         checkArgument(rpc != null);
217         checkArgument(domInput != null);
218
219         Class<? extends RpcService> rpcType = rpcServiceType.get();
220         checkState(rpcType != null);
221         RpcService rpcService = baRpcRegistry.getRpcService(rpcType);
222         checkState(rpcService != null);
223         CompositeNode domUnwrappedInput = domInput.getFirstCompositeByName(QName.create(rpc, "input"));
224
225         try {
226             return Futures.immediateFuture(resolveInvocationStrategy(rpc).invokeOn(rpcService, domUnwrappedInput));
227         } catch (Exception e) {
228             return Futures.immediateFailedFuture(e);
229         }
230     }
231
232     private RpcInvocationStrategy resolveInvocationStrategy(final QName rpc) {
233         return strategiesByQName.get(rpc);
234     }
235
236     private RpcInvocationStrategy createInvocationStrategy(final QName rpc,
237         final Class<? extends RpcService> rpcType, final BindingIndependentMappingService mappingService) throws Exception {
238         return ClassLoaderUtils.withClassLoader(rpcType.getClassLoader(), new Callable<RpcInvocationStrategy>() {
239             @Override
240             public RpcInvocationStrategy call() throws Exception {
241                 String methodName = BindingMapping.getMethodName(rpc);
242                 Method targetMethod = null;
243                 for (Method possibleMethod : rpcType.getMethods()) {
244                     if (possibleMethod.getName().equals(methodName)
245                         && BindingReflections.isRpcMethod(possibleMethod)) {
246                         targetMethod = possibleMethod;
247                         break;
248                     }
249                 }
250                 checkState(targetMethod != null, "Rpc method not found");
251                 return new RpcInvocationStrategy(rpc, targetMethod, mappingService, biRpcRegistry);
252             }
253
254         });
255     }
256
257     /**
258      * Registers RPC Forwarder to Binding Broker,
259      * this means DOM Broekr has implementation of RPC
260      * which is registered to it.
261      *
262      * If RPC Forwarder was previously registered to DOM Broker
263      * or to Bidning Broker this method is noop to prevent
264      * creating forwarding loop.
265      *
266      */
267     public void registerToBindingBroker() {
268         if(!registrationInProgress && forwarderRegistration == null) {
269             try {
270                 registrationInProgress = true;
271                 this.forwarderRegistration = baRpcRegistry.addRpcImplementation((Class)rpcServiceType.get(), proxy);
272             } catch (Exception e) {
273                 LOG.error("Unable to forward RPCs for {}",rpcServiceType.get(),e);
274             } finally {
275                 registrationInProgress = false;
276             }
277         }
278     }
279 }