[ONOS-5806] Revise virtual provider and provider service

Changes
1. VirtualProvider contains ProviderId
2. VirtualProviderRegistryService Interface is added
3. VirtualProviderManager which is the implementation
   of the intetreface is added

Change-Id: I015f0b8d2a6d7ae3bb571f6e1fa0090be3b4f192
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProvider.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProvider.java
index fa5332d..1010417 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProvider.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProvider.java
@@ -16,66 +16,22 @@
 
 package org.onosproject.incubator.net.virtual.provider;
 
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
+import org.onosproject.net.provider.ProviderId;
 
 public abstract class AbstractVirtualProvider implements VirtualProvider {
-    private final String scheme;
-    private final String id;
+    private final ProviderId providerId;
 
     /**
-     * Creates a provider with the supplied identifier.
+     * Creates a virtual provider with the supplied identifier.
      *
-     * @param scheme provider scheme
-     * @param id provider id
+     * @param id a virtual provider id
      */
-    protected AbstractVirtualProvider(String id, String scheme) {
-        this.scheme = scheme;
-        this.id = id;
-    }
-
-    /**
-     * Returns the device URI scheme to which this provider is bound.
-     *
-     * @return device URI scheme
-     */
-    @Override
-    public String scheme() {
-        return this.scheme;
-    }
-
-    /**
-     * Returns the device URI scheme specific id portion.
-     *
-     * @return id
-     */
-    @Override
-    public String id() {
-        return this.id;
+    protected AbstractVirtualProvider(ProviderId id) {
+        this.providerId = id;
     }
 
     @Override
-    public int hashCode() {
-        return Objects.hash(scheme, id);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj instanceof AbstractVirtualProvider) {
-            final AbstractVirtualProvider other = (AbstractVirtualProvider) obj;
-            return Objects.equals(this.scheme, other.scheme) &&
-                    Objects.equals(this.id, other.id);
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(this).add("scheme", scheme).add("id", id)
-                .toString();
+    public ProviderId id() {
+        return providerId;
     }
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProviderService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProviderService.java
index b257d2e..e12f1ef 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProviderService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/AbstractVirtualProviderService.java
@@ -18,13 +18,24 @@
 
 import static com.google.common.base.Preconditions.checkState;
 
+/**
+ * Base implementation of a virtual provider service,
+ * which tracks the provider to which it is issued and can be invalidated.
+ *
+ * @param <P> type of the information provider
+ */
 public abstract class AbstractVirtualProviderService<P extends VirtualProvider>
         implements VirtualProviderService {
 
     private boolean isValid = true;
-    private final P provider;
+    private P provider = null;
 
-    protected AbstractVirtualProviderService(P provider) {
+    /**
+     * Creates a virtual provider service on behalf of the specified provider.
+     *
+     * @param provider provider to which this service is being issued
+     */
+    protected void setProvider(P provider) {
         this.provider = provider;
     }
 
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProvider.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProvider.java
index e94215d..c9e58e2 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProvider.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProvider.java
@@ -16,6 +16,8 @@
 
 package org.onosproject.incubator.net.virtual.provider;
 
+import org.onosproject.net.provider.ProviderId;
+
 /**
  * Abstraction of a provider of information about virtual network environment.
  * The role of virtual providers is to translate virtual objects into physical
@@ -24,16 +26,9 @@
 public interface VirtualProvider {
 
     /**
-     * Returns the device URI scheme to which this provider is bound.
+     * Returns the provider identifier.
      *
-     * @return device URI scheme
+     * @return provider identification
      */
-    String scheme();
-
-    /**
-     * Returns the device URI scheme specific id portion.
-     *
-     * @return id
-     */
-    String id();
+    ProviderId id();
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderRegistryService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderRegistryService.java
new file mode 100644
index 0000000..05625e9
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/provider/VirtualProviderRegistryService.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual.provider;
+
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.Set;
+
+/**
+ * Registry for tracking information providers with the core.
+ */
+public interface VirtualProviderRegistryService {
+
+    /**
+     * Registers the supplied virtual provider.
+     *
+     * @param virtualProvider a virtual provider to be registered
+     * @throws java.lang.IllegalArgumentException if the provider is registered already
+     */
+    void registerProvider(VirtualProvider virtualProvider);
+
+    /**
+     * Unregisters the supplied virtual provider.
+     * As a result the previously issued virtual provider service will be invalidated
+     * and any subsequent invocations of its methods may throw
+     * {@link java.lang.IllegalStateException}.
+     * <p>
+     * Unregistering a virtual provider that has not been previously registered results
+     * in a no-op.
+     * </p>
+     *
+     * @param virtualProvider a virtual provider to be unregistered
+     */
+    void unregisterProvider(VirtualProvider virtualProvider);
+
+    /**
+     * Registers the supplied virtual provider.
+     *
+     * @param networkId a virtual network identifier
+     * @param virtualProviderService a virtual provider service to be registered
+     */
+    void registerProviderService(NetworkId networkId,
+                                 VirtualProviderService virtualProviderService);
+
+    /**
+     * Unregisters the supplied virtual provider service.
+     *
+     * @param networkId a virtual network identifier
+     * @param virtualProviderService a virtual provider service to be unregistered
+     */
+    void unregisterProviderService(NetworkId networkId,
+                                   VirtualProviderService virtualProviderService);
+
+    /**
+     * Returns a set of currently registered virtual provider identities.
+     *
+     * @return set of virtual provider identifiers
+     */
+    Set<ProviderId> getProviders();
+
+    /**
+     * Returns a set of currently registered virtual provider identities
+     * corresponding to the requested providerService.
+     *
+     * @param virtualProviderService a virtual provider service
+     * @return set of virtual provider identifiers
+     */
+    Set<ProviderId> getProvidersByService(VirtualProviderService virtualProviderService);
+
+    /**
+     * Returns the virtual provider registered with the specified provider ID or null
+     * if none is found for the given provider family and default fall-back is
+     * not supported.
+     *
+     * @param providerId provider identifier
+     * @return provider
+     */
+    VirtualProvider getProvider(ProviderId providerId);
+
+    /**
+     * Returns the virtual provider for the specified device ID based on URI scheme.
+     *
+     * @param deviceId virtual device identifier
+     * @return provider bound to the URI scheme
+     */
+    VirtualProvider getProvider(DeviceId deviceId);
+
+    /**
+     * Returns the virtual provider registered with the specified scheme.
+     *
+     * @param scheme provider scheme
+     * @return provider
+     */
+    VirtualProvider getProvider(String scheme);
+
+    /**
+     * Returns the virtual provider service corresponding to the virtual network and provider.
+     *
+     * @param networkId a virtual network identifier
+     * @param virtualProvider a virtual provider
+     * @return a virtual provider service
+     */
+    VirtualProviderService getProviderService(NetworkId networkId,
+                                              VirtualProvider virtualProvider);
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManager.java
new file mode 100644
index 0000000..59c6630
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManager.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual.impl.provider;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.provider.VirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
+import org.onosproject.incubator.net.virtual.provider.VirtualProviderService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * Implementation of the virtual provider registry and providerService registry service.
+ */
+@Component(immediate = true)
+@Service
+public class VirtualProviderManager
+        implements VirtualProviderRegistryService {
+
+    private final Map<ProviderId, VirtualProvider> providers = new HashMap<>();
+    private final Map<ProviderId, VirtualProviderService> servicesWithProvider = new HashMap<>();
+    private final Map<String, VirtualProvider> providersByScheme = new HashMap<>();
+    private final Map<NetworkId, Set<VirtualProviderService>> servicesByNetwork = new HashMap<>();
+
+    @Override
+    public synchronized void registerProvider(VirtualProvider virtualProvider) {
+        checkNotNull(virtualProvider, "Provider cannot be null");
+        checkState(!providers.containsKey(virtualProvider.id()),
+                   "Provider %s already registered", virtualProvider.id());
+
+        // If the provider is a primary one, check for a conflict.
+        ProviderId pid = virtualProvider.id();
+        checkState(pid.isAncillary() || !providersByScheme.containsKey(pid.scheme()),
+                   "A primary provider with id %s is already registered",
+                   providersByScheme.get(pid.scheme()));
+
+        providers.put(virtualProvider.id(), virtualProvider);
+
+        // Register the provider by URI scheme only if it is not ancillary.
+        if (!pid.isAncillary()) {
+            providersByScheme.put(pid.scheme(), virtualProvider);
+        }
+    }
+
+    @Override
+    public synchronized void unregisterProvider(VirtualProvider virtualProvider) {
+        checkNotNull(virtualProvider, "Provider cannot be null");
+
+        //TODO: invalidate provider services witch subscribe the provider
+        providers.remove(virtualProvider.id());
+
+        if (!virtualProvider.id().isAncillary()) {
+            providersByScheme.remove(virtualProvider.id().scheme());
+        }
+    }
+
+    @Override
+    public synchronized void
+    registerProviderService(NetworkId networkId,
+                            VirtualProviderService virtualProviderService) {
+        Set<VirtualProviderService> services = servicesByNetwork.get(networkId);
+
+        if (services == null) {
+            services = new HashSet<>();
+            servicesByNetwork.put(networkId, services);
+        }
+        services.add(virtualProviderService);
+    }
+
+    @Override
+    public synchronized void
+    unregisterProviderService(NetworkId networkId,
+                              VirtualProviderService virtualProviderService) {
+        Set<VirtualProviderService> services = servicesByNetwork.get(networkId);
+
+        if (services != null) {
+            services.remove(virtualProviderService);
+        }
+    }
+
+    @Override
+    public synchronized Set<ProviderId> getProviders() {
+        return ImmutableSet.copyOf(providers.keySet());
+    }
+
+    @Override
+    public Set<ProviderId> getProvidersByService(VirtualProviderService virtualProviderService) {
+        Class clazz = getProviderClass(virtualProviderService);
+
+        return ImmutableSet.copyOf(providers.values().stream()
+                                           .filter(p -> p.getClass()
+                                                   .isAssignableFrom(clazz))
+                                           .map(p -> p.id()).collect(Collectors.toSet()));
+    }
+
+    @Override
+    public synchronized VirtualProvider getProvider(ProviderId providerId) {
+        return providers.get(providerId);
+    }
+
+    @Override
+    public synchronized VirtualProvider getProvider(DeviceId deviceId) {
+        return providersByScheme.get(deviceId.uri().getScheme());
+    }
+
+    @Override
+    public synchronized VirtualProvider getProvider(String scheme) {
+        return providersByScheme.get(scheme);
+    }
+
+    @Override
+    public synchronized VirtualProviderService
+    getProviderService(NetworkId networkId, VirtualProvider virtualProvider) {
+        Set<VirtualProviderService> services = servicesByNetwork.get(networkId);
+
+        if (services == null) {
+            return null;
+        }
+
+        return services.stream()
+                .filter(s -> virtualProvider.getClass()
+                        .isAssignableFrom(getProviderClass(s)))
+                .findFirst().get();
+    }
+
+    /**
+     * Returns the class type of parameter type.
+     * More specifically, it returns the class type of provider service's provider type.
+     *
+     * @param service a virtual provider service
+     * @return the class type of provider service of the service
+     */
+    private Class getProviderClass(VirtualProviderService service) {
+       String className = service.getClass().getGenericSuperclass().getTypeName();
+       String pramType = className.split("<")[1].split(">")[0];
+
+        try {
+            return Class.forName(pramType);
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+}
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java
new file mode 100644
index 0000000..f952633
--- /dev/null
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/VirtualProviderManagerTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual.impl.provider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
+import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProviderService;
+import org.onosproject.net.provider.ProviderId;
+
+import static org.junit.Assert.assertEquals;
+
+public class VirtualProviderManagerTest {
+
+    private static final String TEST_SCHEME1 = "test1";
+    private static final String TEST_SCHEME2 = "test2";
+    private static final String TEST_ID1 = "org.onosproject.virtual.testprovider1";
+    private static final String TEST_ID2 = "org.onosproject.virtual.testprovider1";
+    private static final NetworkId NETWORK_ID1 = NetworkId.networkId(1);
+    private static final NetworkId NETWORK_ID2 = NetworkId.networkId(2);
+
+    VirtualProviderManager virtualProviderManager;
+
+    @Before
+    public void setUp() throws Exception {
+        virtualProviderManager = new VirtualProviderManager();
+    }
+
+    /**
+     * Tests registerProvider() and unregisterProvider().
+     */
+    @Test
+    public void registerProviderTest() {
+        TestProvider1 provider1 = new TestProvider1();
+        virtualProviderManager.registerProvider(provider1);
+
+        assertEquals("The number of registered provider did not match.", 1,
+                     virtualProviderManager.getProviders().size());
+
+        assertEquals("The registered provider did not match", provider1,
+                     virtualProviderManager.getProvider(TEST_SCHEME1));
+
+        virtualProviderManager.unregisterProvider(provider1);
+
+        TestProvider2 provider2 = new TestProvider2();
+        virtualProviderManager.registerProvider(provider2);
+
+        assertEquals("The number of registered provider did not match.", 1,
+                     virtualProviderManager.getProviders().size());
+
+        virtualProviderManager.unregisterProvider(provider2);
+
+        assertEquals("The number of registered provider did not match.", 0,
+                     virtualProviderManager.getProviders().size());
+    }
+
+    /**
+     * Tests registerProviderService() and getProviderService().
+     */
+    @Test
+    public void registerProviderServiceTest() {
+        TestProvider1 provider1 = new TestProvider1();
+        virtualProviderManager.registerProvider(provider1);
+
+        TestProviderService1 providerService1 = new TestProviderService1();
+        virtualProviderManager.registerProviderService(NETWORK_ID1, providerService1);
+
+        assertEquals(providerService1,
+                     virtualProviderManager.getProviderService(NETWORK_ID1, provider1));
+    }
+
+    private class TestProvider1 extends AbstractVirtualProvider {
+        protected TestProvider1() {
+            super(new ProviderId(TEST_SCHEME1, TEST_ID1));
+        }
+    }
+
+    private class TestProvider2 extends AbstractVirtualProvider {
+        protected TestProvider2() {
+            super(new ProviderId(TEST_SCHEME2, TEST_ID2));
+        }
+    }
+
+    private class TestProviderService1 extends AbstractVirtualProviderService<TestProvider1> {
+    }
+
+    private class TestProviderService2 extends AbstractVirtualProviderService<TestProvider2> {
+    }
+}