KNOX-1935 - CM discovery - Hue should not have both LB and non LB (#220)

diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerCluster.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerCluster.java
index 66e487b..e6a36cb 100644
--- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerCluster.java
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerCluster.java
@@ -17,9 +17,10 @@
 package org.apache.knox.gateway.topology.discovery.cm;
 
 import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
+import org.apache.knox.gateway.topology.discovery.cm.collector.ServiceURLCollectors;
 
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -31,7 +32,7 @@
 
   private String name;
 
-  private Set<ServiceModel> serviceModels = new HashSet<>();
+  private Map<String, List<ServiceModel>> serviceModels = new HashMap<>();
 
   ClouderaManagerCluster(String clusterName) {
     this.name = clusterName;
@@ -45,13 +46,14 @@
   @Override
   public List<String> getServiceURLs(String serviceName) {
     List<String> urls = new ArrayList<>();
-
-    for (ServiceModel sm : serviceModels) {
-      if (sm.getService().equals(serviceName)) {
-        urls.add(sm.getServiceUrl());
+    if (serviceModels.containsKey(serviceName)) {
+      Map<String, List<ServiceModel>> roleModels = new HashMap<>();
+      for (ServiceModel model : serviceModels.get(serviceName)) {
+        roleModels.computeIfAbsent(model.getRoleType(), l -> new ArrayList<>()).add(model);
       }
-    }
 
+      urls.addAll((ServiceURLCollectors.getCollector(serviceName)).collect(roleModels));
+    }
     return urls;
   }
 
@@ -66,7 +68,9 @@
   }
 
   void addServiceModels(Set<ServiceModel> serviceModels) {
-    this.serviceModels.addAll(serviceModels);
+    for (ServiceModel model : serviceModels) {
+      this.serviceModels.computeIfAbsent(model.getService(), l -> new ArrayList<>()).add(model);
+    }
   }
 
   static class ServiceConfiguration {
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModel.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModel.java
index 6e4f09e..a1bc537 100644
--- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModel.java
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceModel.java
@@ -24,36 +24,72 @@
 
   private final Type type;
   private final String service;
+  private final String serviceType;
+  private final String roleType;
   private final String serviceUrl;
 
-  public ServiceModel(Type   type,
-                      String service,
-                      String serviceUrl) {
-    this.type = type;
-    this.service = service;
-    this.serviceUrl = serviceUrl;
+  /**
+   * @param type        The model type
+   * @param service     The service name
+   * @param serviceType The service type
+   * @param roleType    The service role type
+   * @param serviceUrl  The service URL
+   */
+  public ServiceModel(final Type   type,
+                      final String service,
+                      final String serviceType,
+                      final String roleType,
+                      final String serviceUrl) {
+    this.type        = type;
+    this.service     = service;
+    this.serviceType = serviceType;
+    this.roleType    = roleType;
+    this.serviceUrl  = serviceUrl;
   }
 
+  /**
+   * @return The model type
+   */
   public Type getType() {
     return type;
   }
 
+  /**
+   * @return The name of the modeled service
+   */
   public String getService() {
     return service;
   }
 
+  /**
+   * @return The type of the modeled service
+   */
+  public String getServiceType() {
+    return serviceType;
+  }
+
+  /**
+   * @return The role type of the modeled service
+   */
+  public String getRoleType() {
+    return roleType;
+  }
+
+  /**
+   * @return The URL of the modeled service
+   */
   public String getServiceUrl() {
     return serviceUrl;
   }
 
   @Override
   public String toString() {
-    return getService() + '-' + getServiceUrl();
+    return getService() + '-' + getServiceType() + '-' + getRoleType() + '-' + getServiceUrl();
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(type, service, serviceUrl);
+    return Objects.hash(type, service, serviceType, roleType, serviceUrl);
   }
 
   @Override
@@ -67,6 +103,8 @@
     ServiceModel other = (ServiceModel) obj;
     return getType().equals(other.getType()) &&
            getService().equals(other.getService()) &&
+           getServiceType().equals(other.getServiceType()) &&
+           getRoleType().equals(other.getRoleType()) &&
            getServiceUrl().equals(other.getServiceUrl());
   }
 
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceURLCollector.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceURLCollector.java
new file mode 100644
index 0000000..1e09eae
--- /dev/null
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/ServiceURLCollector.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.apache.knox.gateway.topology.discovery.cm;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Collects service URLs from a set of ServiceModels for a service's roles.
+ */
+public interface ServiceURLCollector {
+
+  /**
+   *
+   * @param roleModels A map of service role names to ServiceModel instances
+   *
+   * @return The service URLs based on the ServiceModel instances.
+   */
+  List<String> collect(Map<String, List<ServiceModel>> roleModels);
+
+}
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/AbstractURLCollector.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/AbstractURLCollector.java
new file mode 100644
index 0000000..b1ee664
--- /dev/null
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/AbstractURLCollector.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.apache.knox.gateway.topology.discovery.cm.collector;
+
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import org.apache.knox.gateway.topology.discovery.cm.ServiceURLCollector;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base ServiceURLCollector implementation
+ */
+public abstract class AbstractURLCollector implements ServiceURLCollector {
+
+  @Override
+  public List<String> collect(Map<String, List<ServiceModel>> roleModels) {
+    List<String> urls;
+
+    if (roleModels.isEmpty()) {
+      urls = Collections.emptyList();
+    } else if (roleModels.size() == 1) {
+      urls = new ArrayList<>();
+      List<ServiceModel> models =  roleModels.values().iterator().next();
+      urls.addAll(getURLs(models));
+    } else {
+      urls = getURLs(roleModels);
+    }
+    return urls;
+  }
+
+  protected List<String> getURLs(Map<String, List<ServiceModel>> roleModels) {
+    List<String> urls = new ArrayList<>();
+
+    for (List<ServiceModel> models : roleModels.values()) {
+      urls.addAll(getURLs(models));
+    }
+
+    return urls;
+  }
+
+  protected List<String> getURLs(List<ServiceModel> roleModels) {
+    List<String> urls = new ArrayList<>();
+
+    for (ServiceModel model : roleModels) {
+      urls.add(model.getServiceUrl());
+    }
+
+    return urls;
+  }
+
+}
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/DefaultURLCollector.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/DefaultURLCollector.java
new file mode 100644
index 0000000..72eec58
--- /dev/null
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/DefaultURLCollector.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.apache.knox.gateway.topology.discovery.cm.collector;
+
+/**
+ * A ServiceURLCollector implementation sufficient for most services
+ */
+public class DefaultURLCollector extends AbstractURLCollector {
+
+}
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/HueURLCollector.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/HueURLCollector.java
new file mode 100644
index 0000000..29d329a
--- /dev/null
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/HueURLCollector.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.apache.knox.gateway.topology.discovery.cm.collector;
+
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import org.apache.knox.gateway.topology.discovery.cm.model.hue.HueLBServiceModelGenerator;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ServiceURLCollector for the Hue service
+ */
+public class HueURLCollector extends AbstractURLCollector {
+
+  @Override
+  protected List<String> getURLs(Map<String, List<ServiceModel>> roleModels) {
+    List<String> urls = new ArrayList<>();
+
+    if (roleModels.containsKey(HueLBServiceModelGenerator.ROLE_TYPE)) {
+      urls.addAll(getURLs(roleModels.get(HueLBServiceModelGenerator.ROLE_TYPE)));
+    } else {
+      for (List<ServiceModel> models : roleModels.values()) {
+        urls.addAll(getURLs(models));
+      }
+    }
+
+    return urls;
+  }
+
+}
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/ServiceURLCollectors.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/ServiceURLCollectors.java
new file mode 100644
index 0000000..406a9aa
--- /dev/null
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/collector/ServiceURLCollectors.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.apache.knox.gateway.topology.discovery.cm.collector;
+
+import org.apache.knox.gateway.topology.discovery.cm.ServiceURLCollector;
+import org.apache.knox.gateway.topology.discovery.cm.model.hue.HueServiceModelGenerator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Mapping of service names to ServiceURLCollector instances.
+ */
+public class ServiceURLCollectors {
+
+  private static final String DEFAULT = "DEFAULT";
+
+  private static final Map<String, ServiceURLCollector> collectors = new HashMap<>();
+  static {
+    collectors.put(DEFAULT, new DefaultURLCollector());
+    collectors.put(HueServiceModelGenerator.SERVICE, new HueURLCollector());
+  }
+
+  /**
+   * Get the ServiceURLCollector for the specified service name.
+   *
+   * @param serviceName The name of the service
+   *
+   * @return The ServiceURLCollector instance associated with the service name.
+   */
+  public static ServiceURLCollector getCollector(final String serviceName) {
+     ServiceURLCollector collector = collectors.get(serviceName);
+     if (collector == null) {
+        collector = collectors.get(DEFAULT);
+     }
+     return collector;
+  }
+
+}
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/AbstractServiceModelGenerator.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/AbstractServiceModelGenerator.java
index 95d1bfd..5d77fcd 100644
--- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/AbstractServiceModelGenerator.java
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/AbstractServiceModelGenerator.java
@@ -75,7 +75,7 @@
   }
 
   protected ServiceModel createServiceModel(final String url) {
-    return new ServiceModel(getModelType(), getService(), url);
+    return new ServiceModel(getModelType(), getService(), getServiceType(), getRoleType(), url);
   }
 
 }
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasServiceModelGenerator.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasServiceModelGenerator.java
index 415a9c8..5809943 100644
--- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasServiceModelGenerator.java
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/atlas/AtlasServiceModelGenerator.java
@@ -74,6 +74,8 @@
     }
     return new ServiceModel(getModelType(),
                             getService(),
+                            getServiceType(),
+                            getRoleType(),
                             String.format(Locale.getDefault(), "%s://%s:%s", scheme, hostname, port));
   }
 
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueLBServiceModelGenerator.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueLBServiceModelGenerator.java
index 19a4647..ade5403 100644
--- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueLBServiceModelGenerator.java
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueLBServiceModelGenerator.java
@@ -27,9 +27,9 @@
 
 public class HueLBServiceModelGenerator extends AbstractServiceModelGenerator {
 
-  private static final String SERVICE = "HUE";
-  private static final String SERVICE_TYPE = "HUE";
-  private static final String ROLE_TYPE = "HUE_LOAD_BALANCER";
+  public static final String SERVICE = "HUE";
+  public static final String SERVICE_TYPE = "HUE";
+  public static final String ROLE_TYPE = "HUE_LOAD_BALANCER";
 
   @Override
   public String getService() {
diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueServiceModelGenerator.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueServiceModelGenerator.java
index adf70fa..e6b59d8 100644
--- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueServiceModelGenerator.java
+++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/model/hue/HueServiceModelGenerator.java
@@ -27,9 +27,9 @@
 
 public class HueServiceModelGenerator extends AbstractServiceModelGenerator {
 
-  private static final String SERVICE = "HUE";
-  private static final String SERVICE_TYPE = "HUE";
-  private static final String ROLE_TYPE = "HUE_SERVER";
+  public static final String SERVICE = "HUE";
+  public static final String SERVICE_TYPE = "HUE";
+  public static final String ROLE_TYPE = "HUE_SERVER";
 
   @Override
   public String getService() {
diff --git a/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/collector/AbstractURLCollectorTest.java b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/collector/AbstractURLCollectorTest.java
new file mode 100644
index 0000000..9af8e34
--- /dev/null
+++ b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/collector/AbstractURLCollectorTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.apache.knox.gateway.topology.discovery.cm.collector;
+
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import org.easymock.EasyMock;
+
+public abstract class AbstractURLCollectorTest {
+
+
+  protected static ServiceModel createMockServiceModel(final String service,
+                                                       final String serviceType,
+                                                       final String roleType,
+                                                       final String url) {
+    ServiceModel sm  = EasyMock.createNiceMock(ServiceModel.class);
+    EasyMock.expect(sm.getService()).andReturn(service).anyTimes();
+    EasyMock.expect(sm.getServiceType()).andReturn(serviceType).anyTimes();
+    EasyMock.expect(sm.getRoleType()).andReturn(roleType).anyTimes();
+    EasyMock.expect(sm.getServiceUrl()).andReturn(url).anyTimes();
+    EasyMock.replay(sm);
+    return sm;
+  }
+
+
+
+}
diff --git a/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/collector/HueURLCollectorTest.java b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/collector/HueURLCollectorTest.java
new file mode 100644
index 0000000..7523ab6
--- /dev/null
+++ b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/collector/HueURLCollectorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.apache.knox.gateway.topology.discovery.cm.collector;
+
+import org.apache.knox.gateway.topology.discovery.cm.ServiceModel;
+import org.apache.knox.gateway.topology.discovery.cm.model.hue.HueLBServiceModelGenerator;
+import org.apache.knox.gateway.topology.discovery.cm.model.hue.HueServiceModelGenerator;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class HueURLCollectorTest extends AbstractURLCollectorTest {
+
+  @Test
+  public void testNoServiceModels() {
+    HueURLCollector collector = new HueURLCollector();
+    List<String> urls = collector.collect(Collections.emptyMap());
+    assertNotNull(urls);
+    assertEquals(0, urls.size());
+  }
+
+  @Test
+  public void testSingleServiceModel() {
+    Map<String, List<ServiceModel>> testRoleModels = new HashMap<>();
+
+    testRoleModels.put(HueServiceModelGenerator.SERVICE,
+                       Collections.singletonList(createMockServiceModel(HueServiceModelGenerator.SERVICE,
+                                                                        HueServiceModelGenerator.SERVICE_TYPE,
+                                                                        HueServiceModelGenerator.ROLE_TYPE,
+                                                                        "http://hostx:1234/test1")));
+
+    HueURLCollector collector = new HueURLCollector();
+    List<String> urls = collector.collect(testRoleModels);
+    assertEquals("Expected only the single URL.", 1, urls.size());
+    assertTrue(urls.contains("http://hostx:1234/test1"));
+  }
+
+  @Test
+  public void testMultipleServiceModels() {
+    Map<String, List<ServiceModel>> testRoleModels = new HashMap<>();
+
+    testRoleModels.put(HueServiceModelGenerator.SERVICE,
+                       Arrays.asList(createMockServiceModel(HueServiceModelGenerator.SERVICE,
+                                                            HueServiceModelGenerator.SERVICE_TYPE,
+                                                            HueServiceModelGenerator.ROLE_TYPE,
+                                                            "http://hostx:1234/test1"),
+                                     createMockServiceModel(HueServiceModelGenerator.SERVICE,
+                                                            HueServiceModelGenerator.SERVICE_TYPE,
+                                                            HueServiceModelGenerator.ROLE_TYPE,
+                                                            "http://hostx:1234/test2"),
+                                     createMockServiceModel(HueServiceModelGenerator.SERVICE,
+                                                            HueServiceModelGenerator.SERVICE_TYPE,
+                                                            HueServiceModelGenerator.ROLE_TYPE,
+                                                            "http://hostx:1234/test3")));
+
+    HueURLCollector collector = new HueURLCollector();
+    List<String> urls = collector.collect(testRoleModels);
+    assertEquals("Expected all the URLs.", 3, urls.size());
+    assertTrue(urls.contains("http://hostx:1234/test1"));
+    assertTrue(urls.contains("http://hostx:1234/test2"));
+    assertTrue(urls.contains("http://hostx:1234/test3"));
+  }
+
+  @Test
+  public void testMultipleServiceModelsWithHueLB() {
+
+    Map<String, List<ServiceModel>> testRoleModels = new HashMap<>();
+
+    testRoleModels.put(HueServiceModelGenerator.ROLE_TYPE,
+                       Arrays.asList(createMockServiceModel(HueServiceModelGenerator.SERVICE,
+                                                            HueServiceModelGenerator.SERVICE_TYPE,
+                                                            HueServiceModelGenerator.ROLE_TYPE,
+                                                            "http://hostx:1234/test1"),
+                                     createMockServiceModel(HueServiceModelGenerator.SERVICE,
+                                                            HueServiceModelGenerator.SERVICE_TYPE,
+                                                            HueServiceModelGenerator.ROLE_TYPE,
+                                                            "http://hostx:1234/test2")));
+
+
+    testRoleModels.put(HueLBServiceModelGenerator.ROLE_TYPE,
+                       Collections.singletonList(createMockServiceModel(HueLBServiceModelGenerator.SERVICE,
+                                                                        HueLBServiceModelGenerator.SERVICE_TYPE,
+                                                                        HueLBServiceModelGenerator.ROLE_TYPE,
+                                                                        "http://hostx:1234/lb")));
+
+    HueURLCollector collector = new HueURLCollector();
+    List<String> urls = collector.collect(testRoleModels);
+    assertEquals("Expected only the load-balancer URL.", 1, urls.size());
+    assertEquals("Expected the load-balancer URL.", "http://hostx:1234/lb", urls.get(0));
+  }
+
+}