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