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