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