Updated implementation of internal RPC Router for Binding-Aware Broker and added... 33/1933/4
authorTony Tkacik <ttkacik@cisco.com>
Wed, 16 Oct 2013 15:01:47 +0000 (17:01 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 16 Oct 2013 21:58:03 +0000 (21:58 +0000)
sal-binding-broker:
    Updated implementation of runtime code generator to adhere to RpcRouter contract,
    which is used by binding aware broker for introspection and configuration of runtime generated
    instances without need to use Reflection.

sal-binding-it:
    Added end-to-end integration test which tests following scenarios:
      - Registration of 2 Providers of Flow Service, 1 Consumer
        after registration 4 instances of Flow Service are existing:
         MD-SAL Northbound (runtime generated) - returned to the consumer.
         Optimized RpcRouter (runtime generated) - internal to MD-SAL
         2 implementations supplied by Provider.

      - Test verifies that implementations are not leaked to the Consumer

      - Routing of messages (calls):
         1. Provider 1. registers as provider for Node One, Consumer sends message to Node One, Provider 1. is invoked.
         2. Provider 2. registers as provider for Node Rwo, consumer sends message to Node Two, Provider 2. is invoked.
 3. Provider 1. unregisters as provider for Node One, Provider 2. registers as provider  for Node One,
            Consumer sends message to Node One, Provider 2. is invoked.

Change-Id: I101e26c190cd1770aaff1db974f9b0c341506482
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
16 files changed:
opendaylight/md-sal/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeGenerator.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RoutingPair.xtend
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.xtend [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.xtend [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/XtendHelper.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/BindingAwareBrokerImpl.xtend
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/OsgiProviderContext.xtend
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/osgi/ClassLoaderUtils.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcRouter.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/RuntimeCodeGeneratorTest.java
opendaylight/md-sal/sal-binding-it/pom.xml [new file with mode: 0644]
opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTestProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/RoutedServiceTest.java [new file with mode: 0644]

index 60e5a3d..478bba8 100644 (file)
@@ -26,6 +26,7 @@
         <!-- Binding Aware -->
         <module>sal-binding-api</module>
         <module>sal-binding-broker</module>
+        <module>sal-binding-it</module>
 
         <!-- Samples -->
         <module>samples</module>
index 0d78109..c8d6bcf 100644 (file)
@@ -84,5 +84,5 @@ public interface RuntimeCodeGenerator {
      * @return Instance of RpcService of provided serviceType which implements
      *         also {@link RpcRouter}<T> and {@link DelegateProxy}
      */
-    <T extends RpcService> T getRouterFor(Class<T> serviceType) throws IllegalArgumentException;
+    <T extends RpcService> RpcRouter<T> getRouterFor(Class<T> serviceType) throws IllegalArgumentException;
 }
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.xtend
new file mode 100644 (file)
index 0000000..b255504
--- /dev/null
@@ -0,0 +1,52 @@
+package org.opendaylight.controller.sal.binding.codegen.impl
+
+import org.opendaylight.yangtools.yang.binding.RpcService
+import org.opendaylight.controller.sal.binding.spi.RpcRouter
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.*
+import java.util.Set
+import java.util.HashMap
+import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable
+import org.opendaylight.yangtools.yang.binding.DataObject
+import static org.opendaylight.controller.sal.binding.codegen.impl.XtendHelper.*
+
+class RpcRouterCodegenInstance<T extends RpcService> implements RpcRouter<T> {
+    
+    @Property
+    val T invocationProxy
+    
+    @Property
+    val Class<T> rpcServiceType
+    
+    @Property
+    val Set<Class<? extends BaseIdentity>> contexts
+    
+    val routingTables = new HashMap<Class<? extends BaseIdentity>,RpcRoutingTableImpl<? extends BaseIdentity,?>>;
+    
+    
+    
+    @Property
+    var T defaultService
+    
+    new(Class<T> type,T routerImpl,Set<Class<? extends BaseIdentity>> contexts) {
+        _rpcServiceType = type
+        _invocationProxy = routerImpl
+        _contexts = contexts
+        
+        for(ctx : contexts) {
+            val table = XtendHelper.createRoutingTable(ctx)
+            invocationProxy.setRoutingTable(ctx,table.routes);
+            routingTables.put(ctx,table);
+        }
+    }
+    
+    override <C extends BaseIdentity> getRoutingTable(Class<C> table) {
+        routingTables.get(table) as RpcRoutingTable<C,T>
+    }
+    
+    override getService(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path) {
+        val table = getRoutingTable(context);
+        return table.getRoute(path);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.xtend
new file mode 100644 (file)
index 0000000..02da174
--- /dev/null
@@ -0,0 +1,49 @@
+package org.opendaylight.controller.sal.binding.codegen.impl
+
+import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+import org.opendaylight.yangtools.yang.binding.RpcService
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import java.util.Map
+import org.opendaylight.yangtools.yang.binding.DataObject
+import java.util.HashMap
+
+class RpcRoutingTableImpl<C extends BaseIdentity,S extends RpcService> implements RpcRoutingTable<C,S>{
+    
+    @Property
+    val Class<C> identifier;
+    
+    @Property
+    var S defaultRoute;
+    
+    @Property
+    val Map<InstanceIdentifier<? extends DataObject>,S> routes;
+    
+    new(Class<C> ident, Map<InstanceIdentifier<? extends DataObject>,S> route) {
+        _identifier = ident
+        _routes = route
+    }
+    
+    new(Class<C> ident) {
+        _identifier = ident
+        _routes = new HashMap
+    }
+    
+    
+    override getRoute(InstanceIdentifier nodeInstance) {
+        val ret = routes.get(nodeInstance);
+        if(ret !== null) {
+            return ret;
+        }
+        return defaultRoute;
+    }
+    
+    override removeRoute(InstanceIdentifier<? extends Object> path) {
+        routes.remove(path);
+    }
+    
+    @SuppressWarnings("rawtypes")
+    override updateRoute(InstanceIdentifier<? extends Object> path, S service) {
+        routes.put(path as InstanceIdentifier,service);
+    }
+}
\ No newline at end of file
index 3b3f419..90be6f3 100644 (file)
@@ -32,8 +32,11 @@ import java.util.Arrays
 
 import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.*
 import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
+import java.util.HashSet
+import java.io.ObjectOutputStream.PutField
+import static org.opendaylight.controller.sal.binding.impl.osgi.ClassLoaderUtils.*
 
-class RuntimeCodeGenerator {
+class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator {
 
     val ClassPool classPool;
 
@@ -41,7 +44,7 @@ class RuntimeCodeGenerator {
         classPool = pool;
     }
 
-    def <T extends RpcService> Class<? extends T> generateDirectProxy(Class<T> iface) {
+    override <T extends RpcService> getDirectProxyFor(Class<T> iface) {
         val supertype = iface.asCtClass
         val targetCls = createClass(iface.directProxyName, supertype) [
             field(DELEGATE_FIELD, iface);
@@ -49,43 +52,53 @@ class RuntimeCodeGenerator {
                 body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
             ]
         ]
-        return targetCls.toClass(iface.classLoader)
+        return targetCls.toClass(iface.classLoader).newInstance as T
     }
 
-    def <T extends RpcService> Class<? extends T> generateRouter(Class<T> iface) {
-        val supertype = iface.asCtClass
-        val targetCls = createClass(iface.routerName, supertype) [
-            //field(ROUTING_TABLE_FIELD,Map)
-            field(DELEGATE_FIELD, iface)
-            val contexts = new HashMap<String, Class<? extends BaseIdentity>>();
-            // We search for routing pairs and add fields
-            supertype.methods.filter[declaringClass == supertype && parameterTypes.size === 1].forEach [ method |
-                val routingPair = method.routingContextInput;
-                if (routingPair !== null)
-                    contexts.put(routingPair.context.routingTableField, routingPair.context);
-            ]
-            for (ctx : contexts.entrySet) {
-                field(ctx.key, Map)
-            }
-            implementMethodsFrom(supertype) [
-                if (parameterTypes.size === 1) {
-                    val routingPair = routingContextInput;
-                    val bodyTmp = '''
-                    {
-                        final «InstanceIdentifier.name» identifier = $1.«routingPair.getter.name»();
-                        «supertype.name» instance = («supertype.name») «routingPair.context.routingTableField».get(identifier);
-                        if(instance == null) {
-                           instance = «DELEGATE_FIELD»;
-                        }
-                        return ($r) instance.«it.name»($$);
-                    }'''
-                    body = bodyTmp
-                } else if (parameterTypes.size === 0) {
-                    body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
+    override <T extends RpcService> getRouterFor(Class<T> iface) {
+        val contexts = new HashSet<Class<? extends BaseIdentity>>
+
+        val instance = <T>withClassLoader(iface.classLoader) [ |
+            val supertype = iface.asCtClass
+            val targetCls = createClass(iface.routerName, supertype) [
+                //field(ROUTING_TABLE_FIELD,Map)
+                field(DELEGATE_FIELD, iface)
+                val ctxMap = new HashMap<String, Class<? extends BaseIdentity>>();
+                // We search for routing pairs and add fields
+                supertype.methods.filter[declaringClass == supertype && parameterTypes.size === 1].forEach [ method |
+                    val routingPair = method.routingContextInput;
+                    if (routingPair !== null) {
+                        ctxMap.put(routingPair.context.routingTableField, routingPair.context);
+                        contexts.add(routingPair.context)
+                    }
+                ]
+                for (ctx : ctxMap.entrySet) {
+                    field(ctx.key, Map)
                 }
+                implementMethodsFrom(supertype) [
+                    if (parameterTypes.size === 1) {
+                        val routingPair = routingContextInput;
+                        val bodyTmp = '''
+                        {
+                            final «InstanceIdentifier.name» identifier = $1.«routingPair.getter.name»()«IF routingPair.encapsulated».getValue()«ENDIF»;
+                            «supertype.name» instance = («supertype.name») «routingPair.context.routingTableField».get(identifier);
+                            if(instance == null) {
+                               instance = «DELEGATE_FIELD»;
+                            }
+                            if(instance == null) {
+                                throw new java.lang.IllegalStateException("No provider is processing supplied message");
+                            }
+                            return ($r) instance.«it.name»($$);
+                        }'''
+                        body = bodyTmp
+                    } else if (parameterTypes.size === 0) {
+                        body = '''return ($r) «DELEGATE_FIELD».«it.name»($$);'''
+                    }
+                ]
             ]
-        ]
-        return targetCls.toClass(iface.classLoader)
+            return targetCls.toClass(iface.classLoader).newInstance as T
+        ];
+        return new RpcRouterCodegenInstance(iface, instance, contexts);
     }
 
     def Class<?> generateListenerInvoker(Class<? extends NotificationListener> iface) {
@@ -121,17 +134,19 @@ class RuntimeCodeGenerator {
 
     private def RoutingPair getContextInstance(CtClass dataClass) {
         for (method : dataClass.methods) {
-            if (method.parameterTypes.size === 0 && method.name.startsWith("get")) {
+            if (method.name.startsWith("get") && method.parameterTypes.size === 0) {
                 for (annotation : method.availableAnnotations) {
                     if (annotation instanceof RoutingContext) {
-                        return new RoutingPair((annotation as RoutingContext).value, method)
+                        val encapsulated = !method.returnType.equals(InstanceIdentifier.asCtClass);
+                        
+                        return new RoutingPair((annotation as RoutingContext).value, method,encapsulated);
                     }
                 }
             }
         }
         for (iface : dataClass.interfaces) {
             val ret = getContextInstance(iface);
-            if (ret != null) return ret;
+            if(ret != null) return ret;
         }
         return null;
     }
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/XtendHelper.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/XtendHelper.java
new file mode 100644 (file)
index 0000000..21b48bb
--- /dev/null
@@ -0,0 +1,16 @@
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public class XtendHelper {
+
+    public static <C extends BaseIdentity> RpcRoutingTableImpl createRoutingTable(
+            Class<C> cls) {
+        return new RpcRoutingTableImpl<>(cls);
+    }
+    
+    public static String foo() {
+        return "Foo";
+    }
+}
index 2bae9f5..88e3e62 100644 (file)
@@ -33,13 +33,35 @@ import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcR
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
+import org.opendaylight.controller.sal.binding.spi.RpcRouter
+import java.util.concurrent.ConcurrentHashMap
+import static com.google.common.base.Preconditions.*
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+import com.google.common.collect.Multimap
+import com.google.common.collect.HashMultimap
+import static org.opendaylight.controller.sal.binding.impl.osgi.ClassLoaderUtils.*
 
 class BindingAwareBrokerImpl implements BindingAwareBroker {
     private static val log = LoggerFactory.getLogger(BindingAwareBrokerImpl)
 
     private val clsPool = ClassPool.getDefault()
     private var RuntimeCodeGenerator generator;
-    private Map<Class<? extends RpcService>, RpcProxyContext> managedProxies = new HashMap();
+
+    /**
+     * Map of all Managed Direct Proxies
+     * 
+     */
+    private val Map<Class<? extends RpcService>, RpcProxyContext> managedProxies = new ConcurrentHashMap();
+
+    /**
+     * 
+     * Map of all available Rpc Routers
+     * 
+     * 
+     */
+    private val Map<Class<? extends RpcService>, RpcRouter<? extends RpcService>> rpcRouters = new ConcurrentHashMap();
+
     private var NotificationBrokerImpl notifyBroker
     private var DataBrokerImpl dataBroker
     private var ServiceRegistration<NotificationProviderService> notifyBrokerRegistration
@@ -57,9 +79,9 @@ class BindingAwareBrokerImpl implements BindingAwareBroker {
         notifyBrokerRegistration = brokerBundleContext.registerService(NotificationProviderService, notifyBroker,
             brokerProperties)
         brokerBundleContext.registerService(NotificationService, notifyBroker, brokerProperties)
-        brokerBundleContext.registerService(DataProviderService,dataBroker,brokerProperties)
-        brokerBundleContext.registerService(DataBrokerService,dataBroker,brokerProperties)
-        
+        brokerBundleContext.registerService(DataProviderService, dataBroker, brokerProperties)
+        brokerBundleContext.registerService(DataBrokerService, dataBroker, brokerProperties)
+
     }
 
     def initGenerator() {
@@ -101,16 +123,16 @@ class BindingAwareBrokerImpl implements BindingAwareBroker {
      * 
      * If proxy class does not exist for supplied service class it will be generated automatically.
      */
-    def <T extends RpcService> getManagedDirectProxy(Class<T> service) {
+    private def <T extends RpcService> getManagedDirectProxy(Class<T> service) {
 
         var RpcProxyContext existing = null
-        if((existing = managedProxies.get(service)) != null) {
+        if ((existing = managedProxies.get(service)) != null) {
             return existing.proxy
         }
-        val proxyClass = generator.generateDirectProxy(service)
-        val rpcProxyCtx = new RpcProxyContext(proxyClass)
+        val proxyInstance = generator.getDirectProxyFor(service)
+        val rpcProxyCtx = new RpcProxyContext(proxyInstance.class)
         val properties = new Hashtable<String, String>()
-        rpcProxyCtx.proxy = proxyClass.newInstance as RpcService
+        rpcProxyCtx.proxy = proxyInstance as RpcService
 
         properties.salServiceType = SAL_SERVICE_TYPE_CONSUMER_PROXY
         rpcProxyCtx.registration = brokerBundleContext.registerService(service, rpcProxyCtx.proxy as T, properties)
@@ -125,22 +147,138 @@ class BindingAwareBrokerImpl implements BindingAwareBroker {
     def <T extends RpcService> registerRpcImplementation(Class<T> type, T service, OsgiProviderContext context,
         Hashtable<String, String> properties) {
         val proxy = getManagedDirectProxy(type)
-        if(proxy.delegate != null) {
-            throw new IllegalStateException("Service " + type + "is already registered");
-        }
+        checkState(proxy.delegate === null, "The Service for type {} is already registered", type)
+
         val osgiReg = context.bundleContext.registerService(type, service, properties);
         proxy.delegate = service;
         return new RpcServiceRegistrationImpl<T>(type, service, osgiReg);
     }
 
-    def <T extends RpcService> RpcRegistration<T> registerMountedRpcImplementation(Class<T> tyoe, T service, InstanceIdentifier<?> identifier,
-        OsgiProviderContext context, Hashtable<String, String> properties) {
+    def <T extends RpcService> RpcRegistration<T> registerMountedRpcImplementation(Class<T> type, T service,
+        InstanceIdentifier<?> identifier, OsgiProviderContext context) {
         throw new UnsupportedOperationException("TODO: auto-generated method stub")
     }
 
-    def <T extends RpcService> RoutedRpcRegistration<T> registerRoutedRpcImplementation(Class<T> type, T service, OsgiProviderContext context,
-        Hashtable<String, String> properties) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
+    def <T extends RpcService> RoutedRpcRegistration<T> registerRoutedRpcImplementation(Class<T> type, T service,
+        OsgiProviderContext context) {
+        val router = resolveRpcRouter(type);
+        checkState(router !== null)
+        return new RoutedRpcRegistrationImpl<T>(service, router, this)
+    }
+
+    private def <T extends RpcService> RpcRouter<T> resolveRpcRouter(Class<T> type) {
+
+        val router = rpcRouters.get(type);
+        if (router !== null) {
+            return router as RpcRouter<T>;
+        }
+
+        // We created Router
+        val newRouter = generator.getRouterFor(type);
+        checkState(newRouter !== null);
+        rpcRouters.put(type, newRouter);
+
+        // We create / update Direct Proxy for router
+        val proxy = getManagedDirectProxy(type);
+        proxy.delegate = newRouter.invocationProxy
+        return newRouter;
+
+    }
+
+    protected def <T extends RpcService> void registerPath(RoutedRpcRegistrationImpl<T> registration,
+        Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
+
+        val router = registration.router;
+        val paths = registration.registeredPaths;
+
+        val routingTable = router.getRoutingTable(context)
+        checkState(routingTable != null);
+
+        // Updating internal structure of registration
+        routingTable.updateRoute(path, registration.instance)
+        val success = paths.put(context, path);
+    }
+
+    protected def <T extends RpcService> void unregisterPath(RoutedRpcRegistrationImpl<T> registration,
+        Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
+
+        val router = registration.router;
+        val paths = registration.registeredPaths;
+
+        val routingTable = router.getRoutingTable(context)
+        checkState(routingTable != null);
+
+        // Updating internal structure of registration
+        val target = routingTable.getRoute(path)
+        checkState(target === registration.instance)
+        routingTable.removeRoute(path)
+        checkState(paths.remove(context, path));
+    }
+
+    protected def <T extends RpcService> void unregisterRoutedRpcService(RoutedRpcRegistrationImpl<T> registration) {
+
+        val router = registration.router;
+        val paths = registration.registeredPaths;
+
+        for (ctxMap : registration.registeredPaths.entries) {
+            val context = ctxMap.key
+            val routingTable = router.getRoutingTable(context)
+            val path = ctxMap.value
+            routingTable.removeRoute(path)
+
+        }
+    }
+}
+
+class RoutedRpcRegistrationImpl<T extends RpcService> extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
+
+    @Property
+    private val BindingAwareBrokerImpl broker;
+
+    @Property
+    private val RpcRouter<T> router;
+
+    @Property
+    private val Multimap<Class<? extends BaseIdentity>, InstanceIdentifier<?>> registeredPaths = HashMultimap.create();
+
+    private var closed = false;
+
+    new(T instance, RpcRouter<T> backingRouter, BindingAwareBrokerImpl broker) {
+        super(instance)
+        _router = backingRouter;
+        _broker = broker;
+    }
+
+    override protected removeRegistration() {
+        closed = true
+        broker.unregisterRoutedRpcService(this)
+    }
+
+    override registerInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> instance) {
+        registerPath(context, instance);
+    }
+
+    override unregisterInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> instance) {
+        unregisterPath(context, instance);
+    }
+
+    override getService() {
+        return instance;
+    }
+
+    override registerPath(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
+        checkClosed()
+        broker.registerPath(this, context, path);
+    }
+
+    override unregisterPath(Class<? extends BaseIdentity> context, InstanceIdentifier<? extends Object> path) {
+        checkClosed()
+        broker.unregisterPath(this, context, path);
+    }
+
+    private def checkClosed() {
+        if (closed)
+            throw new IllegalStateException("Registration was closed.");
     }
 
 }
index 29c3845..1be19c0 100644 (file)
@@ -20,6 +20,7 @@ import static extension org.opendaylight.controller.sal.binding.impl.osgi.Proper
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality
+import static com.google.common.base.Preconditions.*
 
 class OsgiProviderContext extends OsgiConsumerContext implements ProviderContext {
 
@@ -44,30 +45,33 @@ class OsgiProviderContext extends OsgiConsumerContext implements ProviderContext
     }
 
     override <T extends RpcService> addMountRpcImplementation(Class<T> type, InstanceIdentifier<?> mount, T implementation) throws IllegalStateException {
+        checkNotNull(type, "Service type should not be null")
+        checkNotNull(mount,"Path to the mount should not be null")
+        checkNotNull(implementation, "Service instance should not be null")
 
         val properties = new Hashtable<String, String>();
         properties.salServiceType = SAL_SERVICE_TYPE_PROVIDER
 
         // Fill requirements
-        val salReg = broker.registerMountedRpcImplementation(type, implementation, mount, this, properties)
+        val salReg = broker.registerMountedRpcImplementation(type, implementation, mount, this)
         registeredServices.put(type, salReg)
         return salReg;
     }
 
     override <T extends RpcService> addRoutedRpcImplementation(Class<T> type, T implementation) throws IllegalStateException {
-        val properties = new Hashtable<String, String>();
-        properties.salServiceType = SAL_SERVICE_TYPE_PROVIDER
-
-        // Fill requirements
-        val salReg = broker.registerRoutedRpcImplementation(type, implementation, this, properties)
+        checkNotNull(type, "Service type should not be null")
+        checkNotNull(implementation, "Service type should not be null")
+        
+        val salReg = broker.registerRoutedRpcImplementation(type, implementation, this)
         registeredServices.put(type, salReg)
         return salReg;
     }
 
     override registerFunctionality(ProviderFunctionality functionality) {
-    
+        // NOOP for now
     }
 
     override unregisterFunctionality(ProviderFunctionality functionality) {
+        // NOOP for now
     }
 }
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/osgi/ClassLoaderUtils.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/osgi/ClassLoaderUtils.java
new file mode 100644 (file)
index 0000000..ad0473e
--- /dev/null
@@ -0,0 +1,24 @@
+package org.opendaylight.controller.sal.binding.impl.osgi;
+
+
+
+import java.util.concurrent.Callable;
+import static com.google.common.base.Preconditions.*;
+
+public class ClassLoaderUtils {
+    
+    public static <V> V withClassLoader(ClassLoader cls,Callable<V> function) throws Exception {
+        checkNotNull(cls);
+        checkNotNull(function);
+        ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(cls);
+            V result = function.call();
+            Thread.currentThread().setContextClassLoader(oldCls);
+            return result;
+        } catch (Exception e) {
+            Thread.currentThread().setContextClassLoader(oldCls);
+            throw new Exception(e);
+        }
+    }
+}
\ No newline at end of file
index b7a42ed..38e309f 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.sal.binding.spi;
 
+import java.util.Set;
+
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.RpcService;
@@ -31,6 +33,16 @@ public interface RpcRouter<T extends RpcService> {
      * @return type of RpcService which is served by this instance of router.
      */
     Class<T> getRpcServiceType();
+    
+    
+    /**
+     * Returns a instance of T which is associated with this router instance
+     * and routes messages based on routing tables.
+     * 
+     * @return type of RpcService which is served by this instance of router.
+     */
+    T getInvocationProxy();
+    
 
     /**
      * Returns a routing table for particular route context
@@ -64,6 +76,8 @@ public interface RpcRouter<T extends RpcService> {
     /**
      * 
      */
-    void setDefaultService();
+    void setDefaultService(T service);
+
+    Set<Class<? extends BaseIdentity>> getContexts();
 
 }
index e0808b2..95ac22c 100644 (file)
@@ -13,6 +13,8 @@ import org.junit.Test;
 import static org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.*;
 
 import org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator;
+import org.opendaylight.controller.sal.binding.spi.RpcRouter;
+import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable;
 import org.opendaylight.controller.sal.binding.test.mock.FooService;
 import org.opendaylight.controller.sal.binding.test.mock.ReferencableObject;
 import org.opendaylight.controller.sal.binding.test.mock.ReferencableObjectKey;
@@ -39,25 +41,25 @@ public class RuntimeCodeGeneratorTest {
     
     @Test
     public void testGenerateDirectProxy() {
-        Class<? extends FooService> product = codeGenerator.generateDirectProxy(FooService.class);
+        FooService product = codeGenerator.getDirectProxyFor(FooService.class);
         assertNotNull(product);
     }
 
     @Test
     public void testGenerateRouter() throws Exception {
-        Class<? extends FooService> product = codeGenerator.generateRouter(FooService.class);
+        RpcRouter<FooService> product = codeGenerator.getRouterFor(FooService.class);
         assertNotNull(product);
-        assertNotNull(product.getSimpleName());
-        assertEquals("2 fields should be generated.",2,product.getFields().length);
+        assertNotNull(product.getInvocationProxy());
         
-        verifyRouting(product.newInstance());
+        assertEquals("2 fields should be generated.",2,product.getInvocationProxy().getClass().getFields().length);
+        
+        verifyRouting(product);
     }
 
-    private void verifyRouting(FooService product) {
-        Map<InstanceIdentifier<? extends Object>,FooService> routingTable = new HashMap<>();
-        setRoutingTable(product, BaseIdentity.class, routingTable);
+    private void verifyRouting(RpcRouter<FooService> product) {
+        assertNotNull("Routing table should be initialized",product.getRoutingTable(BaseIdentity.class));
         
-        assertSame("Returned routing table should be same instance",routingTable,getRoutingTable(product, BaseIdentity.class));
+        RpcRoutingTable<BaseIdentity, FooService> routingTable = product.getRoutingTable(BaseIdentity.class);
         
         int servicesCount = 2;
         int instancesPerService = 3;
@@ -70,11 +72,11 @@ public class RuntimeCodeGeneratorTest {
         
         for(int i = 0;i<service.length;i++) {
             for (InstanceIdentifier<?> instance : identifiers[i]) {
-                routingTable.put(instance, service[i]);
+                routingTable.updateRoute(instance, service[i]);
             }
         }
         
-        assertEquals("All instances should be registered.", servicesCount*instancesPerService, routingTable.size());
+        assertEquals("All instances should be registered.", servicesCount*instancesPerService, routingTable.getRoutes().size());
         
         SimpleInput[] instance_0_input = new SimpleInputImpl[] {
             new SimpleInputImpl(identifiers[0][0]),
@@ -90,16 +92,19 @@ public class RuntimeCodeGeneratorTest {
         
         // We test sending mock messages
         
-        product.simple(instance_0_input[0]);
+        product.getInvocationProxy().simple(instance_0_input[0]);
         verify(service[0]).simple(instance_0_input[0]);
         
-        product.simple(instance_0_input[1]);
-        product.simple(instance_0_input[2]);
+        product.getInvocationProxy().simple(instance_0_input[1]);
+        product.getInvocationProxy().simple(instance_0_input[2]);
         
         verify(service[0]).simple(instance_0_input[1]);
         verify(service[0]).simple(instance_0_input[2]);
         
-        product.simple(instance_1_input[0]);
+        
+        product.getInvocationProxy().simple(instance_1_input[0]);
+        
+        // We should have call to instance 1
         verify(service[1]).simple(instance_1_input[0]);
     }
 
diff --git a/opendaylight/md-sal/sal-binding-it/pom.xml b/opendaylight/md-sal/sal-binding-it/pom.xml
new file mode 100644 (file)
index 0000000..86b6626
--- /dev/null
@@ -0,0 +1,141 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>sal-parent</artifactId>
+    <groupId>org.opendaylight.controller</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>sal-binding-it</artifactId>
+  <scm>
+    <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+  </scm>
+
+  <properties>
+    <exam.version>3.0.0</exam.version>
+    <url.version>1.5.0</url.version>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.ops4j.pax.exam</groupId>
+        <artifactId>maven-paxexam-plugin</artifactId>
+        <version>1.2.4</version>
+        <executions>
+          <execution>
+            <id>generate-config</id>
+            <goals>
+              <goal>generate-depends-file</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <pluginManagement>
+      <plugins>
+        <!--This plugin's configuration is used to store Eclipse m2e settings 
+          only. It has no influence on the Maven build itself. -->
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>
+                      org.ops4j.pax.exam
+                    </groupId>
+                    <artifactId>
+                      maven-paxexam-plugin
+                    </artifactId>
+                    <versionRange>
+                      [1.2.4,)
+                    </versionRange>
+                    <goals>
+                      <goal>
+                        generate-depends-file
+                      </goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore></ignore>
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+      <artifactId>xtend-lib-osgi</artifactId>
+      <version>2.4.3</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-broker-impl</artifactId>
+      <version>1.0-SNAPSHOT</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-container-native</artifactId>
+      <version>${exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-junit4</artifactId>
+      <version>${exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-link-mvn</artifactId>
+      <version>${exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>equinoxSDK381</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <version>3.8.1.v20120830-144521</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>log4j-over-slf4j</artifactId>
+      <version>1.7.2</version>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+      <version>1.0.9</version>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>1.0.9</version>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.model</groupId>
+      <artifactId>model-flow-service</artifactId>
+      <version>1.0-SNAPSHOT</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java
new file mode 100644 (file)
index 0000000..cddf7e8
--- /dev/null
@@ -0,0 +1,131 @@
+package org.opendaylight.controller.test.sal.binding.it;
+
+import static org.ops4j.pax.exam.CoreOptions.*;
+
+import javax.inject.Inject;
+
+import org.junit.runner.RunWith;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+import org.osgi.framework.BundleContext;
+
+@RunWith(PaxExam.class)
+public abstract class AbstractTest {
+
+    public static final String CONTROLLER = "org.opendaylight.controller";
+    public static final String YANGTOOLS = "org.opendaylight.yangtools";
+    
+    public static final String CONTROLLER_MODELS = "org.opendaylight.controller.model";
+    public static final String YANGTOOLS_MODELS = "org.opendaylight.yangtools.model";
+    
+    
+    @Inject
+    BindingAwareBroker broker;
+
+    @Inject
+    BundleContext bundleContext;
+
+    
+    
+    public BindingAwareBroker getBroker() {
+        return broker;
+    }
+
+
+
+    public void setBroker(BindingAwareBroker broker) {
+        this.broker = broker;
+    }
+
+
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+
+
+    @Configuration
+    public Option[] config() {
+        return options(systemProperty("osgi.console").value("2401"), 
+                mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), //
+                mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), //
+                mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
+                mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
+                
+                // MD-SAL Dependencies
+                mavenBundle(YANGTOOLS, "concepts").versionAsInProject(),
+                mavenBundle(YANGTOOLS, "yang-binding").versionAsInProject(), //
+                mavenBundle(YANGTOOLS, "yang-common").versionAsInProject(), //
+                
+                mavenBundle(YANGTOOLS+".thirdparty", "xtend-lib-osgi").versionAsInProject(),
+                mavenBundle("com.google.guava", "guava").versionAsInProject(), //
+                mavenBundle("org.javassist", "javassist").versionAsInProject(),
+                
+                // MD SAL
+                mavenBundle(CONTROLLER, "sal-binding-api").versionAsInProject(), //
+                mavenBundle(CONTROLLER, "sal-binding-broker-impl").versionAsInProject(), //
+                mavenBundle(CONTROLLER, "sal-common").versionAsInProject(), //
+                mavenBundle(CONTROLLER, "sal-common-api").versionAsInProject(),
+                mavenBundle(CONTROLLER, "sal-common-util").versionAsInProject(), //
+                
+                // BASE Models
+                mavenBundle(YANGTOOLS_MODELS,"yang-ext").versionAsInProject(),
+                mavenBundle(YANGTOOLS_MODELS,"ietf-inet-types").versionAsInProject(),
+                mavenBundle(YANGTOOLS_MODELS,"ietf-yang-types").versionAsInProject(),
+                mavenBundle(YANGTOOLS_MODELS,"opendaylight-l2-types").versionAsInProject(),
+                mavenBundle(CONTROLLER_MODELS,"model-flow-base").versionAsInProject(),
+                mavenBundle(CONTROLLER_MODELS,"model-flow-service").versionAsInProject(),
+                mavenBundle(CONTROLLER_MODELS,"model-inventory").versionAsInProject(),
+                junitAndMockitoBundles()
+                );
+    }
+    
+    
+    public static Option junitAndMockitoBundles() {
+        return new DefaultCompositeOption(
+                // Repository required to load harmcrest (OSGi-fied version).
+                repository("http://repository.springsource.com/maven/bundles/external").id(
+                        "com.springsource.repository.bundles.external"),
+
+                // Mockito without Hamcrest and Objenesis
+                mavenBundle("org.mockito", "mockito-all", "1.9.5"),
+
+                // Hamcrest with a version matching the range expected by Mockito
+                //mavenBundle("org.hamcrest", "com.springsource.org.hamcrest.core", "1.1.0"),
+
+                // Objenesis with a version matching the range expected by Mockito
+                //wrappedBundle(mavenBundle("org.objenesis", "objenesis", "1.2"))
+                 //       .exports("*;version=1.2"),
+
+                // The default JUnit bundle also exports Hamcrest, but with an (incorrect) version of
+                // 4.9 which does not match the Mockito import. When deployed after the hamcrest bundles, it gets
+                // resolved correctly.
+                junitBundles(),
+
+                /*
+                 * Felix has implicit boot delegation enabled by default. It conflicts with Mockito:
+                 * java.lang.LinkageError: loader constraint violation in interface itable initialization:
+                 * when resolving method "org.osgi.service.useradmin.User$$EnhancerByMockitoWithCGLIB$$dd2f81dc
+                 * .newInstance(Lorg/mockito/cglib/proxy/Callback;)Ljava/lang/Object;" the class loader
+                 * (instance of org/mockito/internal/creation/jmock/SearchingClassLoader) of the current class,
+                 * org/osgi/service/useradmin/User$$EnhancerByMockitoWithCGLIB$$dd2f81dc, and the class loader
+                 * (instance of org/apache/felix/framework/BundleWiringImpl$BundleClassLoaderJava5) for interface
+                 * org/mockito/cglib/proxy/Factory have different Class objects for the type org/mockito/cglib/
+                 * proxy/Callback used in the signature
+                 *
+                 * So we disable the bootdelegation. this property has no effect on the other OSGi implementation.
+                 */
+                frameworkProperty("felix.bootdelegation.implicit").value("false")
+        );
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTestProvider.java b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTestProvider.java
new file mode 100644 (file)
index 0000000..6f1551f
--- /dev/null
@@ -0,0 +1,29 @@
+package org.opendaylight.controller.test.sal.binding.it;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public abstract class AbstractTestProvider implements BindingAwareProvider {
+
+    @Override
+    public Collection<? extends RpcService> getImplementations() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<? extends ProviderFunctionality> getFunctionality() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public void onSessionInitialized(ConsumerContext session) {
+        // Noop
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/RoutedServiceTest.java b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/RoutedServiceTest.java
new file mode 100644 (file)
index 0000000..ef60a2c
--- /dev/null
@@ -0,0 +1,199 @@
+package org.opendaylight.controller.test.sal.binding.it;
+
+import static org.junit.Assert.*;
+
+import java.math.BigInteger;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+import com.google.inject.Inject;
+
+import static org.mockito.Mockito.*;
+
+public class RoutedServiceTest extends AbstractTest {
+
+    private SalFlowService first;
+    private SalFlowService second;
+
+    private SalFlowService consumerService;
+
+    private RoutedRpcRegistration<SalFlowService> firstReg;
+    private RoutedRpcRegistration<SalFlowService> secondReg;
+
+    @Before
+    public void setUp() throws Exception {
+        first = mock(SalFlowService.class,"First Flow Service");
+        second = mock(SalFlowService.class,"Second Flow Service");
+    }
+
+    @Test
+    public void testServiceRegistration() {
+
+        assertNotNull(getBroker());
+
+        BindingAwareProvider provider1 = new AbstractTestProvider() {
+
+            @Override
+            public void onSessionInitiated(ProviderContext session) {
+                assertNotNull(session);
+                firstReg = session.addRoutedRpcImplementation(SalFlowService.class, first);
+            }
+        };
+
+        /**
+         * Register first provider, which register first implementation of 
+         * SalFlowService
+         * 
+         */
+        getBroker().registerProvider(provider1, getBundleContext());
+        assertNotNull("Registration should not be null", firstReg);
+        assertSame(first, firstReg.getInstance());
+        
+        BindingAwareProvider provider2 = new AbstractTestProvider() {
+
+            @Override
+            public void onSessionInitiated(ProviderContext session) {
+                assertNotNull(session);
+                secondReg = session.addRoutedRpcImplementation(SalFlowService.class, second);
+            }
+        };
+        getBroker().registerProvider(provider2, getBundleContext());
+        assertNotNull("Registration should not be null", firstReg);
+        assertNotSame(secondReg, firstReg);
+
+
+        BindingAwareConsumer consumer = new BindingAwareConsumer() {
+            @Override
+            public void onSessionInitialized(ConsumerContext session) {
+                consumerService = session.getRpcService(SalFlowService.class);
+            }
+        };
+        broker.registerConsumer(consumer, getBundleContext());
+
+        assertNotNull("MD-SAL instance of Flow Service should be returned", consumerService);
+        assertNotSame("Provider instance and consumer instance should not be same.", first, consumerService);
+
+        NodeRef nodeOne = createNodeRef("foo:node:1");
+
+
+        /**
+         * Provider 1 - register itself as provider for SalFlowService
+         * for nodeOne
+         */
+        firstReg.registerPath(NodeContext.class, nodeOne.getValue());
+
+        /**
+         * Consumer creates addFlow Message for node one and sends
+         * it to the MD-SAL
+         * 
+         */
+        AddFlowInput firstMessage = createSampleAddFlow(nodeOne,1);
+        consumerService.addFlow(firstMessage);
+        
+        /**
+         * We verify if implementation of first provider received same
+         * message from MD-SAL.
+         * 
+         */
+        verify(first).addFlow(firstMessage);
+        
+        /**
+         * Verifies that second instance was not invoked with first
+         * message
+         * 
+         */
+        verify(second, times(0)).addFlow(firstMessage);
+        
+        /**
+         * Second provider registers as provider for nodeTwo
+         * 
+         */
+        NodeRef nodeTwo = createNodeRef("foo:node:2");
+        secondReg.registerPath(NodeContext.class, nodeTwo.getValue());
+        
+        
+        /**
+         * Consumer sends message to nodeTwo for three times.
+         * Should be processed by second instance.
+         */
+        AddFlowInput secondMessage = createSampleAddFlow(nodeTwo,2);
+        consumerService.addFlow(secondMessage);
+        consumerService.addFlow(secondMessage);
+        consumerService.addFlow(secondMessage);
+        
+        /**
+         * We verify that second was invoked 3 times, with message
+         * two as parameter, first was invoked 0 times.
+         * 
+         */
+        verify(second, times(3)).addFlow(secondMessage);
+        verify(first, times(0)).addFlow(secondMessage);
+        
+        
+        /**
+         * First provider unregisters as implementation of FlowService
+         * for node one
+         * 
+         */
+        firstReg.unregisterPath(NodeContext.class, nodeOne.getValue());
+        
+        
+        /**
+         * Second provider registers as implementation for FlowService
+         * for node one
+         * 
+         */
+        secondReg.registerPath(NodeContext.class, nodeOne.getValue());
+        
+        /**
+         * Consumer sends third message to Node 1, should be processed
+         * by second instance.
+         * 
+         */
+        AddFlowInput thirdMessage = createSampleAddFlow(nodeOne,3);
+        consumerService.addFlow(thirdMessage);
+        
+        /**
+         * We verify that first provider was invoked 0 times,
+         * second provider 1 time.
+         */
+        verify(first,times(0)).addFlow(thirdMessage);
+        verify(second).addFlow(thirdMessage);
+        
+    }
+
+    private static NodeRef createNodeRef(String string) {
+        NodeKey key = new NodeKey(new NodeId(string));
+        InstanceIdentifier<Node> path = InstanceIdentifier.builder().node(Nodes.class).node(Node.class, key)
+                .toInstance();
+
+        return new NodeRef(path);
+    }
+
+    static AddFlowInput createSampleAddFlow(NodeRef node,int cookie) {
+        AddFlowInputBuilder ret = new AddFlowInputBuilder();
+        ret.setNode(node);
+        ret.setCookie(BigInteger.valueOf(cookie));
+        return ret.build();
+    }
+}