Merge "Exception for URI /restconf/operations/module_name:rpc ended with slash"
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / sal / binding / impl / connect / dom / BindingIndependentConnector.java
1 /*
2  * Copyright (c) 2013 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.controller.sal.binding.impl.connect.dom;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12
13 import java.lang.ref.WeakReference;
14 import java.lang.reflect.InvocationHandler;
15 import java.lang.reflect.Method;
16 import java.lang.reflect.Proxy;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Set;
24 import java.util.WeakHashMap;
25 import java.util.concurrent.Callable;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentMap;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.Future;
30
31 import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
32 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
33 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
34 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
35 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
36 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
37 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
38 import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
39 import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
40 import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
41 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
42 import org.opendaylight.controller.sal.binding.api.NotificationProviderService.NotificationInterestListener;
43 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
44 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
45 import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider;
46 import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
47 import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
48 import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
49 import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl.GlobalRpcRegistrationListener;
50 import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl.RouterInstantiationListener;
51 import org.opendaylight.controller.sal.common.util.CommitHandlerTransactions;
52 import org.opendaylight.controller.sal.common.util.Rpcs;
53 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
54 import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
55 import org.opendaylight.controller.sal.core.api.Provider;
56 import org.opendaylight.controller.sal.core.api.RpcImplementation;
57 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
58 import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
59 import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
60 import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
61 import org.opendaylight.yangtools.concepts.ListenerRegistration;
62 import org.opendaylight.yangtools.concepts.Registration;
63 import org.opendaylight.yangtools.concepts.util.ClassLoaderUtils;
64 import org.opendaylight.yangtools.yang.binding.Augmentable;
65 import org.opendaylight.yangtools.yang.binding.Augmentation;
66 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
67 import org.opendaylight.yangtools.yang.binding.BindingMapping;
68 import org.opendaylight.yangtools.yang.binding.DataContainer;
69 import org.opendaylight.yangtools.yang.binding.DataObject;
70 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
71 import org.opendaylight.yangtools.yang.binding.Notification;
72 import org.opendaylight.yangtools.yang.binding.RpcService;
73 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
74 import org.opendaylight.yangtools.yang.common.QName;
75 import org.opendaylight.yangtools.yang.common.RpcError;
76 import org.opendaylight.yangtools.yang.common.RpcResult;
77 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
78 import org.opendaylight.yangtools.yang.data.api.Node;
79 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
80 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
81 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85 import com.google.common.base.Function;
86 import com.google.common.base.Optional;
87 import com.google.common.collect.FluentIterable;
88 import com.google.common.collect.ImmutableList;
89 import com.google.common.collect.ImmutableSet;
90 import com.google.common.collect.ImmutableSet.Builder;
91 import com.google.common.util.concurrent.Futures;
92
93 public class BindingIndependentConnector implements //
94         RuntimeDataProvider, //
95         Provider, //
96         AutoCloseable {
97
98
99
100     private final Logger LOG = LoggerFactory.getLogger(BindingIndependentConnector.class);
101
102     @SuppressWarnings("deprecation")
103     private static final InstanceIdentifier<? extends DataObject> ROOT = InstanceIdentifier.builder().toInstance();
104
105     private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier ROOT_BI = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
106             .builder().toInstance();
107
108     private final static Method EQUALS_METHOD;
109
110     private BindingIndependentMappingService mappingService;
111
112     private org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService;
113
114     private DataProviderService baDataService;
115
116     private final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions = new ConcurrentHashMap<>();
117     private final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions = new ConcurrentHashMap<>();
118
119     private final BindingToDomCommitHandler bindingToDomCommitHandler = new BindingToDomCommitHandler();
120     private final DomToBindingCommitHandler domToBindingCommitHandler = new DomToBindingCommitHandler();
121
122     private Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> baCommitHandlerRegistration;
123
124     private Registration<DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode>> biCommitHandlerRegistration;
125
126     private RpcProvisionRegistry biRpcRegistry;
127     private RpcProviderRegistry baRpcRegistry;
128
129     private ListenerRegistration<DomToBindingRpcForwardingManager> domToBindingRpcManager;
130     // private ListenerRegistration<BindingToDomRpcForwardingManager>
131     // bindingToDomRpcManager;
132
133     private final Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier = new Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() {
134
135         @Override
136         public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(InstanceIdentifier<?> input) {
137             return mappingService.toDataDom(input);
138         }
139
140     };
141
142     private Registration<DataReader<InstanceIdentifier<? extends DataObject>, DataObject>> baDataReaderRegistration;
143
144     private boolean rpcForwarding = false;
145
146     private boolean dataForwarding = false;
147
148     private boolean notificationForwarding = false;
149
150     private RpcProviderRegistryImpl baRpcRegistryImpl;
151
152     private NotificationProviderService baNotifyService;
153
154     private NotificationPublishService domNotificationService;
155
156     static {
157         try {
158             EQUALS_METHOD = Object.class.getMethod("equals", Object.class);
159         } catch (Exception e) {
160             throw new RuntimeException(e);
161         }
162     }
163
164     @Override
165     public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
166         try {
167             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
168             CompositeNode result = biDataService.readOperationalData(biPath);
169             return potentialAugmentationRead(path, biPath, result);
170         } catch (DeserializationException e) {
171             throw new IllegalStateException(e);
172         }
173     }
174
175     private DataObject potentialAugmentationRead(InstanceIdentifier<? extends DataObject> path,
176             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath, CompositeNode result)
177             throws DeserializationException {
178         Class<? extends DataObject> targetType = path.getTargetType();
179         if (Augmentation.class.isAssignableFrom(targetType)) {
180             path = mappingService.fromDataDom(biPath);
181             Class<? extends Augmentation<?>> augmentType = (Class<? extends Augmentation<?>>) targetType;
182             DataObject parentTo = mappingService.dataObjectFromDataDom(path, result);
183             if (parentTo instanceof Augmentable<?>) {
184                 return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType);
185             }
186         }
187         return mappingService.dataObjectFromDataDom(path, result);
188     }
189
190     @Override
191     public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
192         try {
193             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
194             CompositeNode result = biDataService.readConfigurationData(biPath);
195             return potentialAugmentationRead(path, biPath, result);
196         } catch (DeserializationException e) {
197             throw new IllegalStateException(e);
198         }
199     }
200
201     private DataModificationTransaction createBindingToDomTransaction(
202             DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
203         DataModificationTransaction target = biDataService.beginTransaction();
204         LOG.debug("Created DOM Transaction {} for {},", target.getIdentifier(),source.getIdentifier());
205         for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
206             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
207             target.removeConfigurationData(biEntry);
208             LOG.debug("Delete of Binding Configuration Data {} is translated to {}",entry,biEntry);
209         }
210         for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedOperationalData()) {
211             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
212             target.removeOperationalData(biEntry);
213             LOG.debug("Delete of Binding Operational Data {} is translated to {}",entry,biEntry);
214         }
215         for (Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedConfigurationData()
216                 .entrySet()) {
217             Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
218                     .toDataDom(entry);
219             target.putConfigurationData(biEntry.getKey(), biEntry.getValue());
220             LOG.debug("Update of Binding Configuration Data {} is translated to {}",entry,biEntry);
221         }
222         for (Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedOperationalData()
223                 .entrySet()) {
224             Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
225                     .toDataDom(entry);
226             target.putOperationalData(biEntry.getKey(), biEntry.getValue());
227             LOG.debug("Update of Binding Operational Data {} is translated to {}",entry,biEntry);
228         }
229
230         return target;
231     }
232
233     private org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction createDomToBindingTransaction(
234             DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
235         org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction target = baDataService
236                 .beginTransaction();
237         for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
238             try {
239
240                 InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
241                 target.removeConfigurationData(baEntry);
242             } catch (DeserializationException e) {
243                 LOG.error("Ommiting from BA transaction: {}.", entry, e);
244             }
245         }
246         for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) {
247             try {
248
249                 InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
250                 target.removeOperationalData(baEntry);
251             } catch (DeserializationException e) {
252                 LOG.error("Ommiting from BA transaction: {}.", entry, e);
253             }
254         }
255         for (Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
256                 .getUpdatedConfigurationData().entrySet()) {
257             try {
258                 InstanceIdentifier<?> baKey = mappingService.fromDataDom(entry.getKey());
259                 DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue());
260                 target.putConfigurationData(baKey, baData);
261             } catch (DeserializationException e) {
262                 LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
263             }
264         }
265         for (Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
266                 .getUpdatedOperationalData().entrySet()) {
267             try {
268
269                 InstanceIdentifier<?> baKey = mappingService.fromDataDom(entry.getKey());
270                 DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue());
271                 target.putOperationalData(baKey, baData);
272             } catch (DeserializationException e) {
273                 LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
274             }
275         }
276         return target;
277     }
278
279     public org.opendaylight.controller.sal.core.api.data.DataProviderService getBiDataService() {
280         return biDataService;
281     }
282
283     protected void setDomDataService(org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService) {
284         this.biDataService = biDataService;
285     }
286
287     public DataProviderService getBaDataService() {
288         return baDataService;
289     }
290
291     protected void setBindingDataService(DataProviderService baDataService) {
292         this.baDataService = baDataService;
293     }
294
295     public RpcProviderRegistry getRpcRegistry() {
296         return baRpcRegistry;
297     }
298
299     protected void setBindingRpcRegistry(RpcProviderRegistry rpcRegistry) {
300         this.baRpcRegistry = rpcRegistry;
301     }
302
303     public void startDataForwarding() {
304         checkState(!dataForwarding, "Connector is already forwarding data.");
305         baDataReaderRegistration = baDataService.registerDataReader(ROOT, this);
306         baCommitHandlerRegistration = baDataService.registerCommitHandler(ROOT, bindingToDomCommitHandler);
307         biCommitHandlerRegistration = biDataService.registerCommitHandler(ROOT_BI, domToBindingCommitHandler);
308         baDataService.registerCommitHandlerListener(domToBindingCommitHandler);
309         dataForwarding = true;
310     }
311
312     public void startRpcForwarding() {
313         if (baRpcRegistry != null && biRpcRegistry != null && baRpcRegistry instanceof RouteChangePublisher<?, ?>) {
314             checkState(!rpcForwarding, "Connector is already forwarding RPCs");
315             domToBindingRpcManager = baRpcRegistry.registerRouteChangeListener(new DomToBindingRpcForwardingManager());
316             if (baRpcRegistry instanceof RpcProviderRegistryImpl) {
317                 baRpcRegistryImpl = (RpcProviderRegistryImpl) baRpcRegistry;
318                 baRpcRegistryImpl.registerRouterInstantiationListener(domToBindingRpcManager.getInstance());
319                 baRpcRegistryImpl.registerGlobalRpcRegistrationListener(domToBindingRpcManager.getInstance());
320             }
321             rpcForwarding = true;
322         }
323     }
324
325     public void startNotificationForwarding() {
326         checkState(!notificationForwarding, "Connector is already forwarding notifications.");
327         if (baNotifyService != null && domNotificationService != null) {
328             baNotifyService.registerInterestListener(new DomToBindingNotificationForwarder());
329
330             notificationForwarding = true;
331         }
332     }
333
334     protected void setMappingService(BindingIndependentMappingService mappingService) {
335         this.mappingService = mappingService;
336     }
337
338     @Override
339     public Collection<ProviderFunctionality> getProviderFunctionality() {
340         return Collections.emptyList();
341     }
342
343     @Override
344     public void onSessionInitiated(ProviderSession session) {
345         setDomDataService(session.getService(org.opendaylight.controller.sal.core.api.data.DataProviderService.class));
346         setDomRpcRegistry(session.getService(RpcProvisionRegistry.class));
347
348     }
349
350     public <T extends RpcService> void onRpcRouterCreated(Class<T> serviceType, RpcRouter<T> router) {
351
352     }
353
354     public void setDomRpcRegistry(RpcProvisionRegistry registry) {
355         biRpcRegistry = registry;
356     }
357
358     @Override
359     public void close() throws Exception {
360         if (baCommitHandlerRegistration != null) {
361             baCommitHandlerRegistration.close();
362         }
363         if (biCommitHandlerRegistration != null) {
364             biCommitHandlerRegistration.close();
365         }
366
367     }
368
369     private class DomToBindingTransaction implements
370             DataCommitTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> {
371
372         private final org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing;
373         private final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification;
374
375         public DomToBindingTransaction(
376                 org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing,
377                 DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification) {
378             super();
379             this.backing = backing;
380             this.modification = modification;
381             bindingOpenedTransactions.put(backing.getIdentifier(), this);
382         }
383
384         @Override
385         public DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> getModification() {
386             return modification;
387         }
388
389         @Override
390         public RpcResult<Void> rollback() throws IllegalStateException {
391             // backing.cancel();
392             return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
393         }
394
395         @Override
396         public RpcResult<Void> finish() throws IllegalStateException {
397             Future<RpcResult<TransactionStatus>> result = backing.commit();
398             try {
399                 RpcResult<TransactionStatus> baResult = result.get();
400                 return Rpcs.<Void> getRpcResult(baResult.isSuccessful(), null, baResult.getErrors());
401             } catch (InterruptedException e) {
402                 throw new IllegalStateException("", e);
403             } catch (ExecutionException e) {
404                 throw new IllegalStateException("", e);
405             }
406         }
407     }
408
409     private class BindingToDomTransaction implements
410             DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> {
411
412         private final DataModificationTransaction backing;
413         private final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification;
414
415         public BindingToDomTransaction(DataModificationTransaction backing,
416                 DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
417             this.backing = backing;
418             this.modification = modification;
419             domOpenedTransactions.put(backing.getIdentifier(), this);
420         }
421
422         @Override
423         public DataModification<InstanceIdentifier<? extends DataObject>, DataObject> getModification() {
424             return modification;
425         }
426
427         @Override
428         public RpcResult<Void> finish() throws IllegalStateException {
429             Future<RpcResult<TransactionStatus>> result = backing.commit();
430             try {
431                 RpcResult<TransactionStatus> biResult = result.get();
432                 return Rpcs.<Void> getRpcResult(biResult.isSuccessful(), null, biResult.getErrors());
433             } catch (InterruptedException e) {
434                 throw new IllegalStateException("", e);
435             } catch (ExecutionException e) {
436                 throw new IllegalStateException("", e);
437             } finally {
438                 domOpenedTransactions.remove(backing.getIdentifier());
439             }
440         }
441
442         @Override
443         public RpcResult<Void> rollback() throws IllegalStateException {
444             domOpenedTransactions.remove(backing.getIdentifier());
445             return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
446         }
447     }
448
449     private class BindingToDomCommitHandler implements
450             DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> {
451
452         @Override
453         public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> requestCommit(
454                 DataModification<InstanceIdentifier<? extends DataObject>, DataObject> bindingTransaction) {
455
456             /**
457              * Transaction was created as DOM transaction, in that case we do
458              * not need to forward it back.
459              */
460             if (bindingOpenedTransactions.containsKey(bindingTransaction.getIdentifier())) {
461
462                 return CommitHandlerTransactions.allwaysSuccessfulTransaction(bindingTransaction);
463             }
464             DataModificationTransaction domTransaction = createBindingToDomTransaction(bindingTransaction);
465             BindingToDomTransaction wrapped = new BindingToDomTransaction(domTransaction, bindingTransaction);
466             LOG.trace("Forwarding Binding Transaction: {} as DOM Transaction: {} .", bindingTransaction.getIdentifier(),
467                     domTransaction.getIdentifier());
468             return wrapped;
469         }
470     }
471
472     private class DomToBindingCommitHandler implements //
473             RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>, //
474             DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> {
475
476         @Override
477         public void onRegister(DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
478
479             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = mappingService.toDataDom(registration
480                     .getPath());
481
482         }
483
484         @Override
485         public void onUnregister(DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
486             // NOOP for now
487             // FIXME: do registration based on only active commit handlers.
488         }
489
490         @Override
491         public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> requestCommit(
492                 DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domTransaction) {
493             Object identifier = domTransaction.getIdentifier();
494
495             /**
496              * We checks if the transcation was originated in this mapper. If it
497              * was originated in this mapper we are returing allways success
498              * commit hanlder to prevent creating loop in two-phase commit and
499              * duplicating data.
500              */
501             if (domOpenedTransactions.containsKey(identifier)) {
502                 return CommitHandlerTransactions.allwaysSuccessfulTransaction(domTransaction);
503             }
504
505             org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction baTransaction = createDomToBindingTransaction(domTransaction);
506             DomToBindingTransaction forwardedTransaction = new DomToBindingTransaction(baTransaction, domTransaction);
507             LOG.trace("Forwarding DOM Transaction: {} as Binding Transaction: {}.", domTransaction.getIdentifier(),
508                     baTransaction.getIdentifier());
509             return forwardedTransaction;
510         }
511     }
512
513     /**
514      * Manager responsible for instantiating forwarders responsible for
515      * forwarding of RPC invocations from DOM Broker to Binding Aware Broker
516      *
517      */
518     private class DomToBindingRpcForwardingManager implements
519             RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>,
520             RouterInstantiationListener,
521             GlobalRpcRegistrationListener {
522
523         private final Map<Class<? extends RpcService>, DomToBindingRpcForwarder> forwarders = new WeakHashMap<>();
524         private RpcProviderRegistryImpl registryImpl;
525
526         public RpcProviderRegistryImpl getRegistryImpl() {
527             return registryImpl;
528         }
529
530         public void setRegistryImpl(RpcProviderRegistryImpl registryImpl) {
531             this.registryImpl = registryImpl;
532         }
533
534         @Override
535         public void onGlobalRpcRegistered(Class<? extends RpcService> cls) {
536             getRpcForwarder(cls, null);
537         }
538
539         @Override
540         public void onGlobalRpcUnregistered(Class<? extends RpcService> cls) {
541             // NOOP
542         }
543
544         @Override
545         public void onRpcRouterCreated(RpcRouter<?> router) {
546             Class<? extends BaseIdentity> ctx = router.getContexts().iterator().next();
547             getRpcForwarder(router.getServiceType(), ctx);
548         }
549
550         @Override
551         public void onRouteChange(RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
552             for (Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements().entrySet()) {
553                 bindingRoutesAdded(entry);
554             }
555         }
556
557         private void bindingRoutesAdded(Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
558             Class<? extends BaseIdentity> context = entry.getKey().getRoutingContext();
559             Class<? extends RpcService> service = entry.getKey().getRpcService();
560             if (context != null) {
561                 getRpcForwarder(service, context).registerPaths(context, service, entry.getValue());
562             }
563         }
564
565         private DomToBindingRpcForwarder getRpcForwarder(Class<? extends RpcService> service,
566                 Class<? extends BaseIdentity> context) {
567             DomToBindingRpcForwarder potential = forwarders.get(service);
568             if (potential != null) {
569                 return potential;
570             }
571             if (context == null) {
572                 potential = new DomToBindingRpcForwarder(service);
573             } else {
574                 potential = new DomToBindingRpcForwarder(service, context);
575             }
576
577             forwarders.put(service, potential);
578             return potential;
579         }
580
581     }
582
583     private class DomToBindingRpcForwarder implements RpcImplementation, InvocationHandler {
584
585         private final Set<QName> supportedRpcs;
586         private final WeakReference<Class<? extends RpcService>> rpcServiceType;
587         private final Set<org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration> registrations;
588         private final Map<QName, RpcInvocationStrategy> strategiesByQName = new HashMap<>();
589         private final WeakHashMap<Method, RpcInvocationStrategy> strategiesByMethod = new WeakHashMap<>();
590
591         public DomToBindingRpcForwarder(Class<? extends RpcService> service) {
592             this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
593             this.supportedRpcs = mappingService.getRpcQNamesFor(service);
594             try {
595                 for (QName rpc : supportedRpcs) {
596                     RpcInvocationStrategy strategy = createInvocationStrategy(rpc, service);
597                     strategiesByMethod.put(strategy.targetMethod, strategy);
598                     strategiesByQName.put(rpc, strategy);
599                     biRpcRegistry.addRpcImplementation(rpc, this);
600                 }
601
602             } catch (Exception e) {
603                 LOG.error("Could not forward Rpcs of type {}", service.getName(),e);
604             }
605             registrations = ImmutableSet.of();
606         }
607
608         /**
609          * Constructor for Routed RPC Forwareder.
610          *
611          * @param service
612          * @param context
613          */
614         public DomToBindingRpcForwarder(Class<? extends RpcService> service, Class<? extends BaseIdentity> context) {
615             this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
616             this.supportedRpcs = mappingService.getRpcQNamesFor(service);
617             Builder<RoutedRpcRegistration> registrationsBuilder = ImmutableSet
618                     .<org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration> builder();
619             try {
620                 for (QName rpc : supportedRpcs) {
621                     RpcInvocationStrategy strategy = createInvocationStrategy(rpc, service);
622                     strategiesByMethod.put(strategy.targetMethod, strategy);
623                     strategiesByQName.put(rpc, strategy);
624                     registrationsBuilder.add(biRpcRegistry.addRoutedRpcImplementation(rpc, this));
625                 }
626                 createDefaultDomForwarder();
627             } catch (Exception e) {
628                 LOG.error("Could not forward Rpcs of type {}", service.getName(), e);
629             }
630             registrations = registrationsBuilder.build();
631         }
632
633         public void registerPaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
634                 Set<InstanceIdentifier<?>> set) {
635             QName ctx = BindingReflections.findQName(context);
636             for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
637                     toDOMInstanceIdentifier)) {
638                 for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
639                     reg.registerPath(ctx, path);
640                 }
641             }
642         }
643
644
645         @Override
646         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
647             if (EQUALS_METHOD.equals(method)) {
648                 return false;
649             }
650             RpcInvocationStrategy strategy = strategiesByMethod.get(method);
651             checkState(strategy != null);
652             checkArgument(args.length <= 2);
653             if (args.length == 1) {
654                 checkArgument(args[0] instanceof DataObject);
655                 return strategy.forwardToDomBroker((DataObject) args[0]);
656             }
657             return strategy.forwardToDomBroker(null);
658         }
659
660         public void removePaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
661                 Set<InstanceIdentifier<?>> set) {
662             QName ctx = BindingReflections.findQName(context);
663             for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
664                     toDOMInstanceIdentifier)) {
665                 for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
666                     reg.unregisterPath(ctx, path);
667                 }
668             }
669         }
670
671         @Override
672         public Set<QName> getSupportedRpcs() {
673             return supportedRpcs;
674         }
675
676         @SuppressWarnings({ "unchecked", "rawtypes" })
677         public void createDefaultDomForwarder() {
678             if (baRpcRegistryImpl != null) {
679                 Class<?> cls = rpcServiceType.get();
680                 ClassLoader clsLoader = cls.getClassLoader();
681                 RpcService proxy = (RpcService) Proxy.newProxyInstance(clsLoader, new Class<?>[] { cls }, this);
682
683                 RpcRouter rpcRouter = baRpcRegistryImpl.getRpcRouter(rpcServiceType.get());
684                 rpcRouter.registerDefaultService(proxy);
685             }
686         }
687
688         @Override
689         public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode domInput) {
690             checkArgument(rpc != null);
691             checkArgument(domInput != null);
692
693             Class<? extends RpcService> rpcType = rpcServiceType.get();
694             checkState(rpcType != null);
695             RpcService rpcService = baRpcRegistry.getRpcService(rpcType);
696             checkState(rpcService != null);
697             CompositeNode domUnwrappedInput = domInput.getFirstCompositeByName(QName.create(rpc, "input"));
698             try {
699                 return resolveInvocationStrategy(rpc).invokeOn(rpcService, domUnwrappedInput);
700             } catch (Exception e) {
701                 throw new IllegalStateException(e);
702             }
703         }
704
705         private RpcInvocationStrategy resolveInvocationStrategy(QName rpc) {
706             return strategiesByQName.get(rpc);
707         }
708
709         private RpcInvocationStrategy createInvocationStrategy(final QName rpc,
710                 final Class<? extends RpcService> rpcType) throws Exception {
711             return ClassLoaderUtils.withClassLoader(rpcType.getClassLoader(), new Callable<RpcInvocationStrategy>() {
712                 @Override
713                 public RpcInvocationStrategy call() throws Exception {
714                     String methodName = BindingMapping.getMethodName(rpc);
715                     Method targetMethod = null;
716                     for (Method possibleMethod : rpcType.getMethods()) {
717                         if (possibleMethod.getName().equals(methodName)
718                                 && BindingReflections.isRpcMethod(possibleMethod)) {
719                             targetMethod = possibleMethod;
720                             break;
721                         }
722                     }
723                     checkState(targetMethod != null, "Rpc method not found");
724                     Optional<Class<?>> outputClass = BindingReflections.resolveRpcOutputClass(targetMethod);
725                     Optional<Class<? extends DataContainer>> inputClass = BindingReflections
726                             .resolveRpcInputClass(targetMethod);
727
728                     RpcInvocationStrategy strategy = null;
729                     if (outputClass.isPresent()) {
730                         if (inputClass.isPresent()) {
731                             strategy = new DefaultInvocationStrategy(rpc, targetMethod, outputClass.get(), inputClass
732                                     .get());
733                         } else {
734                             strategy = new NoInputNoOutputInvocationStrategy(rpc, targetMethod);
735                         }
736                     } else if(inputClass.isPresent()){
737                         strategy = new NoOutputInvocationStrategy(rpc,targetMethod, inputClass.get());
738                     } else {
739                         strategy = new NoInputNoOutputInvocationStrategy(rpc,targetMethod);
740                     }
741                     return strategy;
742                 }
743
744             });
745         }
746     }
747
748     private abstract class RpcInvocationStrategy {
749
750         protected final Method targetMethod;
751         protected final QName rpc;
752
753         public RpcInvocationStrategy(QName rpc, Method targetMethod) {
754             this.targetMethod = targetMethod;
755             this.rpc = rpc;
756         }
757
758         public abstract Future<RpcResult<?>> forwardToDomBroker(DataObject input);
759
760         public abstract RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput)
761                 throws Exception;
762
763         public RpcResult<CompositeNode> invokeOn(RpcService rpcService, CompositeNode domInput) throws Exception {
764             return uncheckedInvoke(rpcService, domInput);
765         }
766     }
767
768     private class DefaultInvocationStrategy extends RpcInvocationStrategy {
769
770         @SuppressWarnings("rawtypes")
771         private final WeakReference<Class> inputClass;
772
773         @SuppressWarnings("rawtypes")
774         private final WeakReference<Class> outputClass;
775
776         @SuppressWarnings({ "rawtypes", "unchecked" })
777         public DefaultInvocationStrategy(QName rpc, Method targetMethod, Class<?> outputClass,
778                 Class<? extends DataContainer> inputClass) {
779             super(rpc, targetMethod);
780             this.outputClass = new WeakReference(outputClass);
781             this.inputClass = new WeakReference(inputClass);
782         }
783
784         @SuppressWarnings("unchecked")
785         @Override
786         public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
787             DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput);
788             Future<RpcResult<?>> futureResult = (Future<RpcResult<?>>) targetMethod.invoke(rpcService, bindingInput);
789             if (futureResult == null) {
790                 return Rpcs.getRpcResult(false);
791             }
792             RpcResult<?> bindingResult = futureResult.get();
793             final Object resultObj = bindingResult.getResult();
794             if (resultObj instanceof DataObject) {
795                 final CompositeNode output = mappingService.toDataDom((DataObject)resultObj);
796                 return Rpcs.getRpcResult(true, output, Collections.<RpcError>emptySet());
797             }
798             return Rpcs.getRpcResult(true);
799         }
800
801         @Override
802         public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
803             if(biRpcRegistry != null) {
804                 CompositeNode xml = mappingService.toDataDom(input);
805                 CompositeNode wrappedXml = ImmutableCompositeNode.create(rpc, ImmutableList.<Node<?>> of(xml));
806                 RpcResult<CompositeNode> result = biRpcRegistry.invokeRpc(rpc, wrappedXml);
807                 Object baResultValue = null;
808                 if (result.getResult() != null) {
809                     baResultValue = mappingService.dataObjectFromDataDom(outputClass.get(), result.getResult());
810                 }
811                 RpcResult<?> baResult = Rpcs.getRpcResult(result.isSuccessful(), baResultValue, result.getErrors());
812                 return Futures.<RpcResult<?>> immediateFuture(baResult);
813             }
814             return Futures.<RpcResult<?>> immediateFuture(Rpcs.getRpcResult(false));
815         }
816
817     }
818
819     private class NoInputNoOutputInvocationStrategy extends RpcInvocationStrategy {
820
821         public NoInputNoOutputInvocationStrategy(QName rpc, Method targetMethod) {
822             super(rpc, targetMethod);
823         }
824
825         @Override
826         public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
827             @SuppressWarnings("unchecked")
828             Future<RpcResult<Void>> result = (Future<RpcResult<Void>>) targetMethod.invoke(rpcService);
829             RpcResult<Void> bindingResult = result.get();
830             return Rpcs.getRpcResult(bindingResult.isSuccessful(), bindingResult.getErrors());
831         }
832
833         @Override
834         public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
835             return Futures.immediateFuture(null);
836         }
837     }
838
839     private class NoOutputInvocationStrategy extends RpcInvocationStrategy {
840
841
842         @SuppressWarnings("rawtypes")
843         private final WeakReference<Class> inputClass;
844
845         @SuppressWarnings({ "rawtypes", "unchecked" })
846         public NoOutputInvocationStrategy(QName rpc, Method targetMethod,
847                 Class<? extends DataContainer> inputClass) {
848             super(rpc,targetMethod);
849             this.inputClass = new WeakReference(inputClass);
850         }
851
852
853         @Override
854         public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
855             DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput);
856             Future<RpcResult<?>> result = (Future<RpcResult<?>>) targetMethod.invoke(rpcService, bindingInput);
857             if (result == null) {
858                 return Rpcs.getRpcResult(false);
859             }
860             RpcResult<?> bindingResult = result.get();
861             return Rpcs.getRpcResult(true);
862         }
863
864         @Override
865         public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
866             if(biRpcRegistry != null) {
867                 CompositeNode xml = mappingService.toDataDom(input);
868                 CompositeNode wrappedXml = ImmutableCompositeNode.create(rpc,ImmutableList.<Node<?>>of(xml));
869                 RpcResult<CompositeNode> result = biRpcRegistry.invokeRpc(rpc, wrappedXml);
870                 Object baResultValue = null;
871                 RpcResult<?> baResult = Rpcs.<Void>getRpcResult(result.isSuccessful(), null, result.getErrors());
872                 return Futures.<RpcResult<?>>immediateFuture(baResult);
873             }
874             return Futures.<RpcResult<?>>immediateFuture(Rpcs.getRpcResult(false));
875         }
876
877     }
878
879     public boolean isRpcForwarding() {
880         return rpcForwarding;
881     }
882
883     public boolean isDataForwarding() {
884         return dataForwarding;
885     }
886
887     public boolean isNotificationForwarding() {
888         return notificationForwarding;
889     }
890
891     public BindingIndependentMappingService getMappingService() {
892         return mappingService;
893     }
894
895     public void setBindingNotificationService(NotificationProviderService baService) {
896         this.baNotifyService = baService;
897
898     }
899
900     public void setDomNotificationService(NotificationPublishService domService) {
901         this.domNotificationService = domService;
902     }
903
904     private class DomToBindingNotificationForwarder implements NotificationInterestListener, NotificationListener {
905
906         private final ConcurrentMap<QName, WeakReference<Class<? extends Notification>>> notifications = new ConcurrentHashMap<>();
907         private final Set<QName> supportedNotifications = new HashSet<>();
908
909         @Override
910         public Set<QName> getSupportedNotifications() {
911             return Collections.unmodifiableSet(supportedNotifications);
912         }
913
914         @Override
915         public void onNotification(CompositeNode notification) {
916             QName qname = notification.getNodeType();
917             WeakReference<Class<? extends Notification>> potential = notifications.get(qname);
918             if (potential != null) {
919                 Class<? extends Notification> potentialClass = potential.get();
920                 if (potentialClass != null) {
921                     final DataContainer baNotification = mappingService.dataObjectFromDataDom(potentialClass,
922                             notification);
923
924                     if (baNotification instanceof Notification) {
925                         baNotifyService.publish((Notification) baNotification);
926                     }
927                 }
928             }
929         }
930
931         @Override
932         public void onNotificationSubscribtion(Class<? extends Notification> notificationType) {
933             QName qname = BindingReflections.findQName(notificationType);
934             if (qname != null) {
935                 WeakReference<Class<? extends Notification>> already = notifications.putIfAbsent(qname,
936                         new WeakReference<Class<? extends Notification>>(notificationType));
937                 if (already == null) {
938                     domNotificationService.addNotificationListener(qname, this);
939                     supportedNotifications.add(qname);
940                 }
941             }
942         }
943     }
944 }