Enhanced ProviderId to support notion of primary vs. ancillary and modified AbstractProviderRegistry to enforce one primary provider per URI scheme.
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java b/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
index a4095ea..d59bfd2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/AbstractProviderRegistry.java
@@ -35,10 +35,22 @@
     public synchronized S register(P provider) {
         checkNotNull(provider, "Provider cannot be null");
         checkState(!services.containsKey(provider.id()), "Provider %s already registered", provider.id());
+
+        // If the provider is a primary one, check for a conflict.
+        ProviderId pid = provider.id();
+        checkState(pid.isAncillary() || !providersByScheme.containsKey(pid.scheme()),
+                   "A primary provider with id %s is already registered",
+                   providersByScheme.get(pid.scheme()));
+
         S service = createProviderService(provider);
         services.put(provider.id(), service);
         providers.put(provider.id(), provider);
-        // FIXME populate scheme look-up
+
+        // Register the provider by URI scheme only if it is not ancillary.
+        if (!pid.isAncillary()) {
+            providersByScheme.put(pid.scheme(), provider);
+        }
+
         return service;
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
index 725748a..7d314e8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
@@ -11,6 +11,7 @@
 
     private final String scheme;
     private final String id;
+    private final boolean ancillary;
 
     /**
      * Creates a new provider identifier from the specified string.
@@ -21,8 +22,22 @@
      * @param id     string identifier
      */
     public ProviderId(String scheme, String id) {
+        this(scheme, id, false);
+    }
+
+    /**
+     * Creates a new provider identifier from the specified string.
+     * The providers are expected to follow the reverse DNS convention, e.g.
+     * {@code org.onlab.onos.provider.of.device}
+     *
+     * @param scheme device URI scheme to which this provider is bound, e.g. "of", "snmp"
+     * @param id     string identifier
+     * @param ancillary ancillary provider indicator
+     */
+    public ProviderId(String scheme, String id, boolean ancillary) {
         this.scheme = scheme;
         this.id = id;
+        this.ancillary = ancillary;
     }
 
     /**
@@ -35,6 +50,15 @@
     }
 
     /**
+     * Indicates whether the provider id belongs to an ancillary provider.
+     *
+     * @return true for ancillary; false for primary provider
+     */
+    public boolean isAncillary() {
+        return ancillary;
+    }
+
+    /**
      * Returns the device URI scheme specific id portion.
      *
      * @return id
@@ -56,14 +80,16 @@
         if (obj instanceof ProviderId) {
             final ProviderId other = (ProviderId) obj;
             return Objects.equals(this.scheme, other.scheme) &&
-                    Objects.equals(this.id, other.id);
+                    Objects.equals(this.id, other.id) &&
+                    this.ancillary == other.ancillary;
         }
         return false;
     }
 
     @Override
     public String toString() {
-        return toStringHelper(this).add("scheme", scheme).add("id", id).toString();
+        return toStringHelper(this).add("scheme", scheme).add("id", id)
+                .add("ancillary", ancillary).toString();
     }
 
 }
diff --git a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
index 37bee71..3ecd90d 100644
--- a/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/provider/AbstractProviderRegistryTest.java
@@ -35,7 +35,7 @@
         assertThat("provider not found", registry.getProviders().contains(fooId));
         assertEquals("incorrect provider", psFoo.provider(), pFoo);
 
-        ProviderId barId = new ProviderId("of", "bar");
+        ProviderId barId = new ProviderId("snmp", "bar");
         TestProvider pBar = new TestProvider(barId);
         TestProviderService psBar = registry.register(pBar);
         assertEquals("incorrect provider count", 2, registry.getProviders().size());
@@ -49,6 +49,16 @@
         assertThat("provider not found", registry.getProviders().contains(barId));
     }
 
+    @Test
+    public void ancillaryProviders() {
+        TestProviderRegistry registry = new TestProviderRegistry();
+        TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
+        TestProvider pBar = new TestProvider(new ProviderId("of", "bar", true));
+        registry.register(pFoo);
+        registry.register(pBar);
+        assertEquals("incorrect provider count", 2, registry.getProviders().size());
+    }
+
     @Test(expected = IllegalStateException.class)
     public void duplicateRegistration() {
         TestProviderRegistry registry = new TestProviderRegistry();
@@ -57,6 +67,15 @@
         registry.register(pFoo);
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void duplicateSchemeRegistration() {
+        TestProviderRegistry registry = new TestProviderRegistry();
+        TestProvider pFoo = new TestProvider(new ProviderId("of", "foo"));
+        TestProvider pBar = new TestProvider(new ProviderId("of", "bar"));
+        registry.register(pFoo);
+        registry.register(pBar);
+    }
+
     @Test
     public void voidUnregistration() {
         TestProviderRegistry registry = new TestProviderRegistry();