You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by st...@apache.org on 2017/01/11 20:13:27 UTC

[1/2] ambari git commit: AMBARI-19426. Modify quick link resource provider to consider filters and return visibility. (Balazs Bence Sari via stoader)

Repository: ambari
Updated Branches:
  refs/heads/trunk 6483710a2 -> 2643dd653


http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
index 6f5dd07..57badb8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
@@ -37,7 +37,7 @@ public class QuickLinksProfileParserTest {
     assertEquals(
         Filter.linkAttributeFilter("sso", true),
         profile.getFilters().get(0));
-    assertEquals(2, profile.getServices().size());
+    assertEquals(3, profile.getServices().size());
 
     Service hdfs = profile.getServices().get(0);
     assertEquals("HDFS", hdfs.getName());
@@ -58,6 +58,12 @@ public class QuickLinksProfileParserTest {
     assertEquals(
         Filter.acceptAllFilter(true),
         historyServer.getFilters().get(0));
+
+    Service yarn = profile.getServices().get(2);
+    assertEquals(1, yarn.getFilters().size());
+    assertEquals(
+        Filter.linkNameFilter("resourcemanager_ui", true),
+        yarn.getFilters().get(0));
   }
 
   @Test(expected = JsonParseException.class)

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/resources/example_quicklinks_profile.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/example_quicklinks_profile.json b/ambari-server/src/test/resources/example_quicklinks_profile.json
index 2fa33a4..50ea5e8 100644
--- a/ambari-server/src/test/resources/example_quicklinks_profile.json
+++ b/ambari-server/src/test/resources/example_quicklinks_profile.json
@@ -43,6 +43,15 @@
           ]
         }
       ]
+    },
+    {
+      "name": "YARN",
+      "filters": [
+        {
+          "link_name": "resourcemanager_ui",
+          "visible": true
+        }
+      ]
     }
   ]
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/resources/inconsistent_quicklinks_profile_2.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/inconsistent_quicklinks_profile_2.json b/ambari-server/src/test/resources/inconsistent_quicklinks_profile_2.json
new file mode 100644
index 0000000..457e01d
--- /dev/null
+++ b/ambari-server/src/test/resources/inconsistent_quicklinks_profile_2.json
@@ -0,0 +1,13 @@
+{
+  "filters": [
+    {
+      "property_name": "sso",
+      "visible": true
+    },
+    {
+      "property_name": "sso",
+      "visible": false
+    }
+  ],
+  "services": []
+}
\ No newline at end of file


[2/2] ambari git commit: AMBARI-19426. Modify quick link resource provider to consider filters and return visibility. (Balazs Bence Sari via stoader)

Posted by st...@apache.org.
AMBARI-19426. Modify quick link resource provider to consider filters and return visibility. (Balazs Bence Sari via stoader)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/2643dd65
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/2643dd65
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/2643dd65

Branch: refs/heads/trunk
Commit: 2643dd65348272f69b91160990d6e484289572ba
Parents: 6483710
Author: Balazs Bence Sari <bs...@hortonworks.com>
Authored: Wed Jan 11 21:12:46 2017 +0100
Committer: Toader, Sebastian <st...@hortonworks.com>
Committed: Wed Jan 11 21:13:14 2017 +0100

----------------------------------------------------------------------
 .../controller/AmbariManagementController.java  |   7 +
 .../AmbariManagementControllerImpl.java         |  20 ++
 .../QuickLinkArtifactResourceProvider.java      |  22 ++
 .../ambari/server/state/quicklinks/Link.java    |  11 +
 .../DefaultQuickLinkVisibilityController.java   | 213 ++++++++++++++++++
 .../QuickLinkVisibilityController.java          |  37 ++++
 .../QuickLinkVisibilityControllerFactory.java   |  57 +++++
 .../quicklinksprofile/QuickLinksProfile.java    |  10 +-
 .../QuickLinksProfileEvaluationException.java   |  31 +++
 .../QuickLinksProfileEvaluator.java             | 202 -----------------
 .../QuickLinksProfileEvaluatorException.java    |  27 ---
 .../ShowAllLinksVisibilityController.java       |  38 ++++
 .../QuickLinkArtifactResourceProviderTest.java  | 198 +++++++++++++++++
 .../state/quicklinksprofile/EvaluatorTest.java  | 203 -----------------
 .../quicklinksprofile/FilterEvaluatorTest.java  | 218 +++++++++++++++++++
 ...uickLinkVisibilityControllerFactoryTest.java |  82 +++++++
 .../QuickLinkVisibilityControllerTest.java      | 181 +++++++++++++++
 .../QuickLinksProfileEvaluatorTest.java         | 168 --------------
 .../QuickLinksProfileParserTest.java            |   8 +-
 .../resources/example_quicklinks_profile.json   |   9 +
 .../inconsistent_quicklinks_profile_2.json      |  13 ++
 21 files changed, 1150 insertions(+), 605 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
index cc20324..91bfe09 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
@@ -59,6 +59,7 @@ import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.ServiceOsSpecific;
 import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController;
 import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
 
 /**
@@ -912,5 +913,11 @@ public interface AmbariManagementController {
    */
   MetricsCollectorHAManager getMetricsCollectorHAManager();
 
+  /**
+   * @return the visibility controller that decides which quicklinks should be visible
+   * based on the actual quick links profile. If no profile is set, all links will be shown.
+   */
+  QuickLinkVisibilityController getQuicklinkVisibilityController();
+
 }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 3963a19..5e8c803 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -112,6 +112,7 @@ import org.apache.ambari.server.orm.dao.ClusterVersionDAO;
 import org.apache.ambari.server.orm.dao.ExtensionDAO;
 import org.apache.ambari.server.orm.dao.ExtensionLinkDAO;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
+import org.apache.ambari.server.orm.dao.SettingDAO;
 import org.apache.ambari.server.orm.dao.StackDAO;
 import org.apache.ambari.server.orm.dao.WidgetDAO;
 import org.apache.ambari.server.orm.dao.WidgetLayoutDAO;
@@ -122,6 +123,7 @@ import org.apache.ambari.server.orm.entities.ExtensionLinkEntity;
 import org.apache.ambari.server.orm.entities.OperatingSystemEntity;
 import org.apache.ambari.server.orm.entities.RepositoryEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.orm.entities.SettingEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.orm.entities.WidgetEntity;
 import org.apache.ambari.server.orm.entities.WidgetLayoutEntity;
@@ -181,6 +183,8 @@ import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityControllerFactory;
 import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
 import org.apache.ambari.server.state.stack.RepositoryXml;
@@ -235,6 +239,12 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
   public static final String SKIP_INSTALL_FOR_COMPONENTS = "skipInstallForComponents";
   public static final String DONT_SKIP_INSTALL_FOR_COMPONENTS = "dontSkipInstallForComponents";
 
+  /**
+   * The name of the ambari setting that stores the quicklinks profile.
+   * See {@link org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile}
+   */
+  public static final String SETTING_QUICKLINKS_PROFILE = "QuickLinksProfile";
+
   private final Clusters clusters;
 
   private final ActionManager actionManager;
@@ -299,6 +309,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
   private AmbariEventPublisher ambariEventPublisher;
   @Inject
   private MetricsCollectorHAManager metricsCollectorHAManager;
+  @Inject
+  private SettingDAO settingDAO;
 
   private MaintenanceStateHelper maintenanceStateHelper;
 
@@ -5512,4 +5524,12 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     linkEntity.setExtension(extension);
     return linkEntity;
   }
+
+  @Override
+  public QuickLinkVisibilityController getQuicklinkVisibilityController() {
+    SettingEntity entity = settingDAO.findByName(SETTING_QUICKLINKS_PROFILE);
+    String quickLinkProfileJson = null != entity ? entity.getContent() : null;
+    return QuickLinkVisibilityControllerFactory.get(quickLinkProfileJson);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java
index 5603765..714ae53 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java
@@ -41,6 +41,9 @@ import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.state.QuickLinksConfigurationInfo;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackInfo;
+import org.apache.ambari.server.state.quicklinks.Link;
+import org.apache.ambari.server.state.quicklinks.QuickLinks;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController;
 
 public class QuickLinkArtifactResourceProvider extends AbstractControllerResourceProvider {
 
@@ -174,6 +177,8 @@ public class QuickLinkArtifactResourceProvider extends AbstractControllerResourc
           }
         }
 
+        setVisibility(serviceInfo.getName(), serviceQuickLinks);
+
         List<Resource> serviceResources = new ArrayList<Resource>();
         for (QuickLinksConfigurationInfo quickLinksConfigurationInfo : serviceQuickLinks) {
           Resource resource = new ResourceImpl(Resource.Type.QuickLink);
@@ -194,6 +199,23 @@ public class QuickLinkArtifactResourceProvider extends AbstractControllerResourc
     return resources;
   }
 
+  /**
+   * Sets the visibility flag of the links based on the actual quicklinks profile (if exists).
+   * @param serviceName the name of the service
+   * @param serviceQuickLinks the links
+   */
+  private void setVisibility(String serviceName, List<QuickLinksConfigurationInfo> serviceQuickLinks) {
+    QuickLinkVisibilityController visibilityController = getManagementController().getQuicklinkVisibilityController();
+
+    for(QuickLinksConfigurationInfo configurationInfo: serviceQuickLinks) {
+      for (QuickLinks links: configurationInfo.getQuickLinksConfigurationMap().values()) {
+        for(Link link: links.getQuickLinksConfiguration().getLinks()) {
+          link.setVisible(visibilityController.isVisible(serviceName, link));
+        }
+      }
+    }
+  }
+
   @Override
   protected Set<String> getPKPropertyIds() {
     return null;

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java
index f589f5d..1d2e712 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java
@@ -53,6 +53,9 @@ public class Link{
   @JsonProperty("attributes")
   private List<String> attributes;
 
+  @JsonProperty("visible")
+  private boolean visible = true;
+
   public String getName() {
     return name;
   }
@@ -109,6 +112,14 @@ public class Link{
     this.protocol = protocol;
   }
 
+  public boolean isVisible() {
+    return visible;
+  }
+
+  public void setVisible(boolean visible) {
+    this.visible = visible;
+  }
+
   @Nullable
   public List<String> getAttributes() {
     return attributes;

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java
new file mode 100644
index 0000000..730f01b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java
@@ -0,0 +1,213 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+
+import com.google.common.base.Optional;
+
+/**
+ * This class can evaluate whether a quicklink has to be shown or hidden based on the received {@link QuickLinksProfile}.
+ */
+public class DefaultQuickLinkVisibilityController implements QuickLinkVisibilityController {
+  private final FilterEvaluator globalRules;
+  private final Map<String, FilterEvaluator> serviceRules = new HashMap<>();
+  private final Map<ServiceComponent, FilterEvaluator> componentRules = new HashMap<>();
+
+  public DefaultQuickLinkVisibilityController(QuickLinksProfile profile) throws QuickLinksProfileEvaluationException {
+    int filterCount = size(profile.getFilters());
+    globalRules = new FilterEvaluator(profile.getFilters());
+    for (Service service: nullToEmptyList(profile.getServices())) {
+      filterCount += size(service.getFilters());
+      serviceRules.put(service.getName(), new FilterEvaluator(service.getFilters()));
+      for (Component component: nullToEmptyList(service.getComponents())) {
+        filterCount += size(component.getFilters());
+        componentRules.put(ServiceComponent.of(service.getName(), component.getName()),
+            new FilterEvaluator(component.getFilters()));
+      }
+    }
+    if (filterCount == 0) {
+      throw new QuickLinksProfileEvaluationException("At least one filter must be defined.");
+    }
+  }
+
+  /**
+   * @param service the name of the service
+   * @param quickLink the quicklink
+   * @return a boolean indicating whether the link in the parameter should be visible
+   */
+  public boolean isVisible(@Nonnull String service, @Nonnull Link quickLink) {
+    // First, component rules are evaluated if exist and applicable
+    Optional<Boolean> componentResult = evaluateComponentRules(service, quickLink);
+    if (componentResult.isPresent()) {
+      return componentResult.get();
+    }
+
+    // Secondly, service level rules are applied
+    Optional<Boolean> serviceResult = evaluateServiceRules(service, quickLink);
+    if (serviceResult.isPresent()) {
+      return serviceResult.get();
+    }
+
+    // Global rules are evaluated lastly. If no rules apply to the link, it will be hidden.
+    return globalRules.isVisible(quickLink).or(false);
+  }
+
+  private int size(@Nullable Collection<?> collection) {
+    return null == collection ? 0 : collection.size();
+  }
+
+  private Optional<Boolean> evaluateComponentRules(@Nonnull String service, @Nonnull Link quickLink) {
+    if (null == quickLink.getComponentName()) {
+      return Optional.absent();
+    }
+    else {
+      FilterEvaluator componentEvaluator = componentRules.get(ServiceComponent.of(service, quickLink.getComponentName()));
+      return componentEvaluator != null ? componentEvaluator.isVisible(quickLink) : Optional.<Boolean>absent();
+    }
+  }
+
+  private Optional<Boolean> evaluateServiceRules(@Nonnull String service, @Nonnull Link quickLink) {
+    return serviceRules.containsKey(service) ?
+        serviceRules.get(service).isVisible(quickLink) : Optional.<Boolean>absent();
+  }
+
+  static <T> List<T> nullToEmptyList(@Nullable List<T> items) {
+    return items != null ? items : Collections.<T>emptyList();
+  }
+}
+
+/**
+ * Groups quicklink filters that are on the same level (e.g. a global evaluator or an evaluator for the "HDFS" service,
+ * etc.). The evaluator pick the most applicable filter for a given quick link. If no applicable filter is found, it
+ * returns {@link Optional#absent()}.
+ * <p>
+ *   Filter evaluation order is the following:
+ *   <ol>
+ *     <li>First, link name filters are evaluated. These match links by name.</li>
+ *     <li>If there is no matching link name filter, link attribute filters are evaluated next. "Hide" type filters
+ *     take precedence to "show" type filters.</li>
+ *     <li>Finally, the match-all filter is evaluated, provided it exists.</li>
+ *   </ol>
+ * </p>
+ */
+class FilterEvaluator {
+  private final Map<String, Boolean> linkNameFilters = new HashMap<>();
+  private final Set<String> showAttributes = new HashSet<>();
+  private final Set<String> hideAttributes = new HashSet<>();
+  private Optional<Boolean> acceptAllFilter = Optional.absent();
+
+  FilterEvaluator(List<Filter> filters) throws QuickLinksProfileEvaluationException {
+    for (Filter filter: DefaultQuickLinkVisibilityController.nullToEmptyList(filters)) {
+      if (filter instanceof LinkNameFilter) {
+        String linkName = ((LinkNameFilter)filter).getLinkName();
+        if (linkNameFilters.containsKey(linkName) && linkNameFilters.get(linkName) != filter.isVisible()) {
+          throw new QuickLinksProfileEvaluationException("Contradicting filters for link name [" + linkName + "]");
+        }
+        linkNameFilters.put(linkName, filter.isVisible());
+      }
+      else if (filter instanceof LinkAttributeFilter) {
+        String linkAttribute = ((LinkAttributeFilter)filter).getLinkAttribute();
+        if (filter.isVisible()) {
+          showAttributes.add(linkAttribute);
+        }
+        else {
+          hideAttributes.add(linkAttribute);
+        }
+        if (showAttributes.contains(linkAttribute) && hideAttributes.contains(linkAttribute)) {
+          throw new QuickLinksProfileEvaluationException("Contradicting filters for link attribute [" + linkAttribute + "]");
+        }
+      }
+      // If none of the above, it is an accept-all filter. We expect only one of this type for an Evaluator
+      else {
+        if (acceptAllFilter.isPresent() && !acceptAllFilter.get().equals(filter.isVisible())) {
+          throw new QuickLinksProfileEvaluationException("Contradicting accept-all filters.");
+        }
+        acceptAllFilter = Optional.of(filter.isVisible());
+      }
+    }
+  }
+
+  /**
+   * @param quickLink the link to evaluate
+   * @return Three way evaluation result, which can be one of these:
+   *    show: Optional.of(true), hide: Optional.of(false), don't know: absent optional
+   */
+  Optional<Boolean> isVisible(Link quickLink) {
+    // process first priority filters based on link name
+    if (linkNameFilters.containsKey(quickLink.getName())) {
+      return Optional.of(linkNameFilters.get(quickLink.getName()));
+    }
+
+    // process second priority filters based on link attributes
+    // 'hide' rules take precedence over 'show' rules
+    for (String attribute: DefaultQuickLinkVisibilityController.nullToEmptyList(quickLink.getAttributes())) {
+      if (hideAttributes.contains(attribute)) return Optional.of(false);
+    }
+    for (String attribute: DefaultQuickLinkVisibilityController.nullToEmptyList(quickLink.getAttributes())) {
+      if (showAttributes.contains(attribute)) return Optional.of(true);
+    }
+
+    // accept all filter (if exists) is the last priority
+    return acceptAllFilter;
+  }
+}
+
+/**
+ * Simple value class encapsulating a link name an component name.
+ */
+class ServiceComponent {
+  private final String service;
+  private final String component;
+
+  ServiceComponent(String service, String component) {
+    this.service = service;
+    this.component = component;
+  }
+
+  static ServiceComponent of(String service, String component) {
+    return new ServiceComponent(service, component);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ServiceComponent that = (ServiceComponent) o;
+    return Objects.equals(service, that.service) &&
+        Objects.equals(component, that.component);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(service, component);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java
new file mode 100644
index 0000000..caa2e2e
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java
@@ -0,0 +1,37 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+
+import javax.annotation.Nonnull;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+
+
+public interface QuickLinkVisibilityController {
+
+  /**
+   * @param service The name of the service the quicklink belongs to
+   * @param quickLink the link
+   * @return a boolean indicating if the link should be visible
+   */
+  boolean isVisible(@Nonnull String service, @Nonnull Link quickLink);
+
+}
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java
new file mode 100644
index 0000000..5cafb48
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+import java.io.IOException;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This factory returns the quicklinks visibility controller. If the received string is a parseable and valid quicklinks
+ * profile, a {@link DefaultQuickLinkVisibilityController} will be returned based on the profile. If the string is
+ * {@code null} or invalid {@link ShowAllLinksVisibilityController} will be returned that displays all links which
+ * corresponds to the legacy behavior.
+ */
+public class QuickLinkVisibilityControllerFactory {
+  private static final Logger LOG = LoggerFactory.getLogger(QuickLinkVisibilityControllerFactory.class);
+
+  /**
+   * @param quickLinkProfileJson the quick links profile or {@code null} if no profile is set
+   * @return a {@link DefaultQuickLinkVisibilityController} if the profile exists and is valid, {@link ShowAllLinksVisibilityController}
+   *   otherwise.
+   */
+  public static QuickLinkVisibilityController get(@Nullable String quickLinkProfileJson) {
+    if (null == quickLinkProfileJson) {
+      LOG.info("No quick link profile is set, will display all quicklinks.");
+      return new ShowAllLinksVisibilityController();
+    }
+    try {
+      QuickLinksProfile profile = new QuickLinksProfileParser().parse(quickLinkProfileJson.getBytes());
+      return new DefaultQuickLinkVisibilityController(profile);
+    }
+    catch (IOException | QuickLinksProfileEvaluationException ex) {
+      LOG.error("Unable to parse quick link profile json: " + quickLinkProfileJson, ex);
+      return new ShowAllLinksVisibilityController();
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
index e86af38..c9ac6b4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
@@ -25,15 +25,17 @@ import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
 /**
- * A quicklinks profile is essentially a set of filters defined on three levels:
+ * A quicklinks profile is essentially a set of quick link filters defined on three levels:
  * <ul>
  *   <li>global level</li>
  *   <li>service level</li>
  *   <li>component level (within a service)</li>
  * </ul>
- *
- * For each link, filters are evaluated bottom up: component level filters take priority to service level filters
- * and service level filters take priority to global filters.
+ * <p>For each link, filters are evaluated bottom up: component level filters take priority to service level filters
+ * and service level filters take priority to global filters.</p>
+ * <p>When a quick link profile is set in Ambari, then each quick link's visibility flag is updated according to the profile
+ * before being returned by {@link org.apache.ambari.server.controller.internal.QuickLinkArtifactResourceProvider}.</p>
+ * <p>When no profile is set, all quick link's visibility flat will be set to {@code true} by the provider</p>
  */
 @JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
 @JsonIgnoreProperties(ignoreUnknown = true)

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
new file mode 100644
index 0000000..26819e1
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+/**
+ * Exception that is thrown if a {@link QuickLinkVisibilityController} cannot be build due to errors in the
+ * {@link QuickLinksProfile}.
+ */
+public class QuickLinksProfileEvaluationException extends Exception {
+
+  public QuickLinksProfileEvaluationException(String message) {
+    super(message);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java
deleted file mode 100644
index 31335b6..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.ambari.server.state.quicklinksprofile;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.apache.ambari.server.state.quicklinks.Link;
-
-import com.google.common.base.Optional;
-
-/**
- * This class can evaluate whether a quicklink has to be shown or hidden based on the received {@link QuickLinksProfile}.
- */
-public class QuickLinksProfileEvaluator {
-  private final Evaluator globalRules;
-  private final Map<String, Evaluator> serviceRules = new HashMap<>();
-  private final Map<ServiceComponent, Evaluator> componentRules = new HashMap<>();
-
-  public QuickLinksProfileEvaluator(QuickLinksProfile profile) throws QuickLinksProfileEvaluatorException {
-    globalRules = new Evaluator(profile.getFilters());
-    for (Service service: nullToEmptyList(profile.getServices())) {
-      serviceRules.put(service.getName(), new Evaluator(service.getFilters()));
-      for (Component component: nullToEmptyList(service.getComponents())) {
-        componentRules.put(ServiceComponent.of(service.getName(), component.getName()),
-            new Evaluator(component.getFilters()));
-      }
-    }
-  }
-
-  /**
-   * @param service the name of the service
-   * @param quickLink the quicklink
-   * @return a boolean indicating whether the link in the parameter should be visible
-   */
-  public boolean isVisible(@Nonnull String service, @Nonnull Link quickLink) {
-    // First, component rules are evaluated if exist and applicable
-    Optional<Boolean> componentResult = evaluateComponentRules(service, quickLink);
-    if (componentResult.isPresent()) {
-      return componentResult.get();
-    }
-
-    // Secondly, service level rules are applied
-    Optional<Boolean> serviceResult = evaluateServiceRules(service, quickLink);
-    if (serviceResult.isPresent()) {
-      return serviceResult.get();
-    }
-
-    // Global rules are evaluated lastly. If no rules apply to the link, it will be hidden.
-    return globalRules.isVisible(quickLink).or(false);
-  }
-
-  private Optional<Boolean> evaluateComponentRules(@Nonnull String service, @Nonnull Link quickLink) {
-    if (null == quickLink.getComponentName()) {
-      return Optional.absent();
-    }
-    else {
-      Evaluator componentEvaluator = componentRules.get(ServiceComponent.of(service, quickLink.getComponentName()));
-      return componentEvaluator != null ? componentEvaluator.isVisible(quickLink) : Optional.<Boolean>absent();
-    }
-  }
-
-  private Optional<Boolean> evaluateServiceRules(@Nonnull String service, @Nonnull Link quickLink) {
-    return serviceRules.containsKey(service) ?
-        serviceRules.get(service).isVisible(quickLink) : Optional.<Boolean>absent();
-  }
-
-  static <T> List<T> nullToEmptyList(@Nullable List<T> items) {
-    return items != null ? items : Collections.<T>emptyList();
-  }
-}
-
-/**
- * Groups quicklink filters that are on the same level (e.g. a global evaluator or an evaluator for the "HDFS" service,
- * etc.). The evaluator pick the most applicable filter for a given quick link. If no applicable filter is found, it
- * returns {@link Optional#absent()}.
- * <p>
- *   Filter evaluation order is the following:
- *   <ol>
- *     <li>First, link name filters are evaluated. These match links by name.</li>
- *     <li>If there is no matching link name filter, link attribute filters are evaluated next. "Hide" type filters
- *     take precedence to "show" type filters.</li>
- *     <li>Finally, the match-all filter is evaluated, provided it exists.</li>
- *   </ol>
- * </p>
- */
-class Evaluator {
-  private final Map<String, Boolean> linkNameFilters = new HashMap<>();
-  private final Set<String> showAttributes = new HashSet<>();
-  private final Set<String> hideAttributes = new HashSet<>();
-  private Optional<Boolean> acceptAllFilter = Optional.absent();
-
-  Evaluator(List<Filter> filters) throws QuickLinksProfileEvaluatorException {
-    for (Filter filter: QuickLinksProfileEvaluator.nullToEmptyList(filters)) {
-      if (filter instanceof LinkNameFilter) {
-        String linkName = ((LinkNameFilter)filter).getLinkName();
-        if (linkNameFilters.containsKey(linkName) && linkNameFilters.get(linkName) != filter.isVisible()) {
-          throw new QuickLinksProfileEvaluatorException("Contradicting filters for link name [" + linkName + "]");
-        }
-        linkNameFilters.put(linkName, filter.isVisible());
-      }
-      else if (filter instanceof LinkAttributeFilter) {
-        String linkAttribute = ((LinkAttributeFilter)filter).getLinkAttribute();
-        if (filter.isVisible()) {
-          showAttributes.add(linkAttribute);
-        }
-        else {
-          hideAttributes.add(linkAttribute);
-        }
-        if (showAttributes.contains(linkAttribute) && hideAttributes.contains(linkAttribute)) {
-          throw new QuickLinksProfileEvaluatorException("Contradicting filters for link attribute [" + linkAttribute + "]");
-        }
-      }
-      // If none of the above, it is an accept-all filter. We expect only one for an Evaluator
-      else {
-        if (acceptAllFilter.isPresent() && !acceptAllFilter.get().equals(filter.isVisible())) {
-          throw new QuickLinksProfileEvaluatorException("Contradicting accept-all filters.");
-        }
-        acceptAllFilter = Optional.of(filter.isVisible());
-      }
-    }
-  }
-
-  /**
-   * @param quickLink the link to evaluate
-   * @return Three way evaluation result, which can be one of these:
-   *    show: Optional.of(true), hide: Optional.of(false), don't know: absent optional
-   */
-  Optional<Boolean> isVisible(Link quickLink) {
-    // process first priority filters based on link name
-    if (linkNameFilters.containsKey(quickLink.getName())) {
-      return Optional.of(linkNameFilters.get(quickLink.getName()));
-    }
-
-    // process second priority filters based on link attributes
-    // 'hide' rules take precedence over 'show' rules
-    for (String attribute: QuickLinksProfileEvaluator.nullToEmptyList(quickLink.getAttributes())) {
-      if (hideAttributes.contains(attribute)) return Optional.of(false);
-    }
-    for (String attribute: QuickLinksProfileEvaluator.nullToEmptyList(quickLink.getAttributes())) {
-      if (showAttributes.contains(attribute)) return Optional.of(true);
-    }
-
-    // accept all filter (if exists) is the last priority
-    return acceptAllFilter;
-  }
-}
-
-/**
- * Simple value class encapsulating a link name an component name.
- */
-class ServiceComponent {
-  private final String service;
-  private final String component;
-
-  ServiceComponent(String service, String component) {
-    this.service = service;
-    this.component = component;
-  }
-
-  static ServiceComponent of(String service, String component) {
-    return new ServiceComponent(service, component);
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-    ServiceComponent that = (ServiceComponent) o;
-    return Objects.equals(service, that.service) &&
-        Objects.equals(component, that.component);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(service, component);
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java
deleted file mode 100644
index c24281a..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.ambari.server.state.quicklinksprofile;
-
-public class QuickLinksProfileEvaluatorException extends Exception {
-
-  public QuickLinksProfileEvaluatorException(String message) {
-    super(message);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java
new file mode 100644
index 0000000..286a10c
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+
+import javax.annotation.Nonnull;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+
+/**
+ * A visibility controller that shows all quicklinks. This is used when no quicklinks profile is set or the profile is
+ * invalid. This mimics legacy behavior when all quicklinks were shown.
+ *
+ */
+public class ShowAllLinksVisibilityController implements QuickLinkVisibilityController {
+
+  @Override
+  public boolean isVisible(@Nonnull String service, @Nonnull Link quickLink) {
+    return true;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
new file mode 100644
index 0000000..8c723c9
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
@@ -0,0 +1,198 @@
+/**
+ * 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.ambari.server.controller.internal;
+
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PredicateBuilder;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.stack.QuickLinksConfigurationModule;
+import org.apache.ambari.server.state.QuickLinksConfigurationInfo;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.StackInfo;
+import org.apache.ambari.server.state.quicklinks.Link;
+import org.apache.ambari.server.state.quicklinks.QuickLinks;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityControllerFactory;
+import org.easymock.IAnswer;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import com.google.common.io.Resources;
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+public class QuickLinkArtifactResourceProviderTest {
+
+  private Injector injector;
+  String quicklinkProfile;
+
+  @Before
+  public void before() {
+    injector = Guice.createInjector(new MockModule());
+  }
+
+  private QuickLinkArtifactResourceProvider createProvider() {
+    return new QuickLinkArtifactResourceProvider(injector.getInstance(AmbariManagementController.class));
+  }
+
+  /**
+   * Test to prove the returned links' visibility reflect the actual profile
+   */
+  @Test
+  public void getResourcesRespectsVisibility() throws Exception {
+    quicklinkProfile = Resources.toString(Resources.getResource("example_quicklinks_profile.json"), Charsets.UTF_8);
+
+    QuickLinkArtifactResourceProvider provider = createProvider();
+    Predicate predicate = new PredicateBuilder().property(
+        QuickLinkArtifactResourceProvider.STACK_NAME_PROPERTY_ID).equals("HDP").
+        and().
+        property(QuickLinkArtifactResourceProvider.STACK_VERSION_PROPERTY_ID).equals("2.0.6").
+        and().
+        property(QuickLinkArtifactResourceProvider.STACK_SERVICE_NAME_PROPERTY_ID).equals("YARN").
+        toPredicate();
+    Set<Resource> resources =
+        provider.getResources(PropertyHelper.getReadRequest(Sets.<String>newHashSet()), predicate);
+    Map<String, Link> linkMap = getLinks(resources);
+
+    for (Map.Entry<String, Link> entry: linkMap.entrySet()) {
+      assertTrue("Only resourcemanager_ui should be visible.",
+          entry.getValue().isVisible() == entry.getKey().equals("resourcemanager_ui"));
+    }
+  }
+
+  /**
+   * Test to prove the all links are visible if no profile is set
+   */
+  @Test
+  public void whenNoProfileIsSetAllLinksAreVisible() throws Exception {
+    quicklinkProfile = null;
+
+    QuickLinkArtifactResourceProvider provider = createProvider();
+    Predicate predicate = new PredicateBuilder().property(
+        QuickLinkArtifactResourceProvider.STACK_NAME_PROPERTY_ID).equals("HDP").
+        and().
+        property(QuickLinkArtifactResourceProvider.STACK_VERSION_PROPERTY_ID).equals("2.0.6").
+        and().
+        property(QuickLinkArtifactResourceProvider.STACK_SERVICE_NAME_PROPERTY_ID).equals("YARN").
+        toPredicate();
+    Set<Resource> resources =
+        provider.getResources(PropertyHelper.getReadRequest(Sets.<String>newHashSet()), predicate);
+    Map<String, Link> linkMap = getLinks(resources);
+
+    for (Link link: linkMap.values()) {
+      assertTrue("All links should be visible.", link.isVisible());
+    }
+  }
+
+  /**
+   * Test to prove the all links are visible if invalid profile is set
+   */
+  @Test
+  public void whenInvalidProfileIsSetAllLinksAreVisible() throws Exception {
+    quicklinkProfile = "{}";
+
+    QuickLinkArtifactResourceProvider provider = createProvider();
+    Predicate predicate = new PredicateBuilder().property(
+        QuickLinkArtifactResourceProvider.STACK_NAME_PROPERTY_ID).equals("HDP").
+        and().
+        property(QuickLinkArtifactResourceProvider.STACK_VERSION_PROPERTY_ID).equals("2.0.6").
+        and().
+        property(QuickLinkArtifactResourceProvider.STACK_SERVICE_NAME_PROPERTY_ID).equals("YARN").
+        toPredicate();
+    Set<Resource> resources =
+        provider.getResources(PropertyHelper.getReadRequest(Sets.<String>newHashSet()), predicate);
+    Map<String, Link> linkMap = getLinks(resources);
+
+    for (Link link: linkMap.values()) {
+      assertTrue("All links should be visible.", link.isVisible());
+    }
+  }
+
+  private Map<String, Link> getLinks(Set<Resource> resources) {
+    QuickLinks quickLinks = (QuickLinks)
+        resources.iterator().next().getPropertiesMap().get("QuickLinkInfo/quicklink_data").values().iterator().next();
+    Map<String, Link> linksMap = new HashMap<>();
+    for (Link link: quickLinks.getQuickLinksConfiguration().getLinks()) {
+      linksMap.put(link.getName(), link);
+    }
+    return linksMap;
+  }
+
+  private class MockModule implements Module {
+    @Override
+    public void configure(Binder binder) {
+      AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
+      StackInfo stack = createMock(StackInfo.class);
+      ServiceInfo service = createMock(ServiceInfo.class);
+
+      QuickLinksConfigurationInfo qlConfigInfo = new QuickLinksConfigurationInfo();
+      qlConfigInfo.setDeleted(false);
+      qlConfigInfo.setFileName("parent_quicklinks.json");
+      qlConfigInfo.setIsDefault(true);
+      File qlFile = new File(Resources.getResource("parent_quicklinks.json").getFile()) ;
+      QuickLinksConfigurationModule module = new QuickLinksConfigurationModule(qlFile, qlConfigInfo);
+      module.getModuleInfo();
+
+      AmbariManagementController amc = createMock(AmbariManagementController.class);
+      expect(amc.getAmbariMetaInfo()).andReturn(metaInfo).anyTimes();
+      expect(amc.getQuicklinkVisibilityController()).andAnswer(
+          new IAnswer<QuickLinkVisibilityController>() {
+            @Override
+            public QuickLinkVisibilityController answer() throws Throwable {
+              return QuickLinkVisibilityControllerFactory.get(quicklinkProfile);
+            }
+          }
+      ).anyTimes();
+
+      try {
+        expect(metaInfo.getStack(anyString(), anyString())).andReturn(stack).anyTimes();
+      }
+      catch (AmbariException ex) {
+        throw new RuntimeException(ex);
+      }
+      expect(stack.getServices()).andReturn(ImmutableList.of(service)).anyTimes();
+      expect(stack.getService("YARN")).andReturn(service).anyTimes();
+      expect(service.getQuickLinksConfigurationsMap()).andReturn(ImmutableMap.of("YARN", qlConfigInfo));
+      expect(service.getName()).andReturn("YARN").anyTimes();
+      binder.bind(AmbariManagementController.class).toInstance(amc);
+      replay(amc, metaInfo, stack, service);
+
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java
deleted file mode 100644
index f54842d..0000000
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * 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.ambari.server.state.quicklinksprofile;
-
-import static org.apache.ambari.server.state.quicklinksprofile.Filter.acceptAllFilter;
-import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkAttributeFilter;
-import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkNameFilter;
-import static org.junit.Assert.assertEquals;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.ambari.server.state.quicklinks.Link;
-import org.junit.Test;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-public class EvaluatorTest {
-
-  static final String NAMENODE = "NAMENODE";
-  static final String NAMENODE_UI = "namenode_ui";
-  static final String AUTHENTICATED = "authenticated";
-  static final String NAMENODE_JMX = "namenode_jmx";
-  static final String SSO = "sso";
-
-  private Link namenodeUi;
-  private Link nameNodeJmx;
-
-  public EvaluatorTest() {
-    namenodeUi = new Link();
-    namenodeUi.setComponentName(NAMENODE);
-    namenodeUi.setName(NAMENODE_UI);
-    namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED));
-
-    nameNodeJmx = new Link();
-    nameNodeJmx.setComponentName(NAMENODE);
-    nameNodeJmx.setName(NAMENODE_JMX);
-  }
-
-  /**
-   * Evaluators should work when initialized with {@code null} or an empty list of filters.
-   */
-  @Test
-  public void testWithEmptyFilters() throws Exception {
-    Evaluator evaluator = new Evaluator(new ArrayList<Filter>());
-    assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi));
-
-    Evaluator evaluator2 = new Evaluator(null);
-    assertEquals(Optional.absent(), evaluator2.isVisible(namenodeUi));
-  }
-
-  /**
-   * Evaluator should return {@link Optional#absent()} when the link doesn't match any filters
-   */
-  @Test
-  public void testNoMatchingFilter() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-        linkNameFilter(NAMENODE_JMX, true),
-        linkAttributeFilter(SSO, false));
-    Evaluator evaluator = new Evaluator(filters);
-    assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi));
-  }
-
-  /**
-   * Link name filters should be evaluated first
-   */
-  @Test
-  public void testLinkNameFiltersEvaluatedFirst() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-      acceptAllFilter(false),
-      linkNameFilter(NAMENODE_UI, true),
-      linkNameFilter(NAMENODE_JMX, false),
-      linkAttributeFilter(AUTHENTICATED, false),
-      linkAttributeFilter(SSO, false));
-    Evaluator evaluator = new Evaluator(filters);
-    assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi));
-  }
-
-  /**
-   * Link attribute filters should be evaluated only if the link does not match any link name filters.
-   */
-  @Test
-  public void testLinkAttributeFiltersEvaluatedSecondly() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-        acceptAllFilter(false),
-        linkNameFilter(NAMENODE_JMX, false),
-        linkAttributeFilter(AUTHENTICATED, true),
-        linkAttributeFilter(SSO, true));
-    Evaluator evaluator = new Evaluator(filters);
-    assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi));
-  }
-
-  /**
-   * Link attribute filters work with links with null attributes. (No NPE is thrown)
-   */
-  @Test
-  public void testLinkAttributeFiltersWorkWithNullAttributes() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-        acceptAllFilter(true),
-        linkAttributeFilter(AUTHENTICATED, false),
-        linkAttributeFilter(SSO, false));
-    Evaluator evaluator = new Evaluator(filters);
-    assertEquals(Optional.of(true), evaluator.isVisible(nameNodeJmx));
-  }
-
-
-  /**
-   * If the link matches both a show and hide type link attribute filter, then it will be evaluated as hidden.
-   */
-  @Test
-  public void testHideFilterTakesPrecedence() throws Exception {
-    List<Filter> filters = Lists.<Filter>newArrayList(
-        linkAttributeFilter(AUTHENTICATED, false),
-        linkAttributeFilter(SSO, true));
-    Evaluator evaluator = new Evaluator(filters);
-    namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED, SSO));
-    assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi));
-  }
-
-  /**
-   * Accept-all filters are only evaluated if the link does not match any link name or link attribute filters.
-   */
-  @Test
-  public void acceptAllFilterEvaluatedLast() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-        acceptAllFilter(false),
-        linkNameFilter(NAMENODE_JMX, true),
-        linkAttributeFilter(SSO, true));
-    Evaluator evaluator = new Evaluator(filters);
-    assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi));
-  }
-
-  /**
-   * Contradicting link name filters should result in {@link QuickLinksProfileEvaluatorException}.
-   */
-  @Test(expected = QuickLinksProfileEvaluatorException.class)
-  public void contradictingLinkNameFiltersRejected() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-        linkNameFilter(NAMENODE_JMX, true),
-        linkNameFilter(NAMENODE_JMX, false),
-        linkAttributeFilter(SSO, true));
-    new Evaluator(filters);
-  }
-
-  /**
-   * Contradicting link attribute filters should result in {@link QuickLinksProfileEvaluatorException}.
-   */
-  @Test(expected = QuickLinksProfileEvaluatorException.class)
-  public void contradictingLinkAttributeFiltersRejected() throws Exception {
-    List<Filter> filters = Lists.<Filter>newArrayList(
-        linkAttributeFilter(SSO, true),
-        linkAttributeFilter(SSO, false));
-    new Evaluator(filters);
-  }
-
-  /**
-   * Contradicting accept-all filters should result in {@link QuickLinksProfileEvaluatorException}.
-   */
-  @Test(expected = QuickLinksProfileEvaluatorException.class)
-  public void contradictingAcceptAllFiltersRejected() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-        linkNameFilter(NAMENODE_JMX, true),
-        linkAttributeFilter(SSO, true),
-        acceptAllFilter(true),
-        acceptAllFilter(false));
-    new Evaluator(filters);
-  }
-
-  /**
-   * Duplicate filter declarations are ok if their visibility rule is the same
-   */
-  @Test
-  public void duplicateFiltersAreOkIfDoNotContradict() throws Exception {
-    List<Filter> filters = Lists.newArrayList(
-        acceptAllFilter(true),
-        acceptAllFilter(true),
-        linkNameFilter(NAMENODE_JMX, false),
-        linkNameFilter(NAMENODE_JMX, false),
-        linkAttributeFilter(SSO, false),
-        linkAttributeFilter(SSO, false));
-    Evaluator evaluator = new Evaluator(filters);
-    assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi));
-  }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java
new file mode 100644
index 0000000..8646413
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+import static org.apache.ambari.server.state.quicklinksprofile.Filter.acceptAllFilter;
+import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkAttributeFilter;
+import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkNameFilter;
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+import org.junit.Test;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class FilterEvaluatorTest {
+
+  static final String NAMENODE = "NAMENODE";
+  static final String NAMENODE_UI = "namenode_ui";
+  static final String AUTHENTICATED = "authenticated";
+  static final String NAMENODE_JMX = "namenode_jmx";
+  static final String SSO = "sso";
+
+  private Link namenodeUi;
+  private Link nameNodeJmx;
+
+  public FilterEvaluatorTest() {
+    namenodeUi = new Link();
+    namenodeUi.setComponentName(NAMENODE);
+    namenodeUi.setName(NAMENODE_UI);
+    namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED));
+
+    // this is a "legacy" link with no attributes defined
+    nameNodeJmx = new Link();
+    nameNodeJmx.setComponentName(NAMENODE);
+    nameNodeJmx.setName(NAMENODE_JMX);
+  }
+
+  /**
+   * Evaluators should work when initialized with {@code null} or an empty list of filters.
+   */
+  @Test
+  public void testWithEmptyFilters() throws Exception {
+    FilterEvaluator evaluator = new FilterEvaluator(new ArrayList<Filter>());
+    assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi));
+
+    FilterEvaluator evaluator2 = new FilterEvaluator(null);
+    assertEquals(Optional.absent(), evaluator2.isVisible(namenodeUi));
+  }
+
+  /**
+   * FilterEvaluator should return {@link Optional#absent()} when the link doesn't match any filters
+   */
+  @Test
+  public void testNoMatchingFilter() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        linkNameFilter(NAMENODE_JMX, true),
+        linkAttributeFilter(SSO, false));
+    FilterEvaluator evaluator = new FilterEvaluator(filters);
+    assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi));
+  }
+
+  /**
+   * Link name filters should be evaluated first
+   */
+  @Test
+  public void testLinkNameFiltersEvaluatedFirst() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        acceptAllFilter(false),
+        linkNameFilter(NAMENODE_UI, true),
+        linkNameFilter(NAMENODE_JMX, false),
+        linkAttributeFilter(AUTHENTICATED, false),
+        linkAttributeFilter(SSO, false));
+    FilterEvaluator evaluator = new FilterEvaluator(filters);
+    assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi));
+  }
+
+  /**
+   * Link attribute filters should be evaluated only if the link does not match any link name filters.
+   */
+  @Test
+  public void testLinkAttributeFiltersEvaluatedSecondly() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        acceptAllFilter(false),
+        linkNameFilter(NAMENODE_JMX, false),
+        linkAttributeFilter(AUTHENTICATED, true),
+        linkAttributeFilter(SSO, true));
+    FilterEvaluator evaluator = new FilterEvaluator(filters);
+    assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi));
+  }
+
+
+  /**
+   * Link attribute filters work with links with null attributes. (No NPE is thrown)
+   */
+  @Test
+  public void testLinkAttributeFiltersWorkWithNullAttributes() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        acceptAllFilter(true),
+        linkAttributeFilter(AUTHENTICATED, false),
+        linkAttributeFilter(SSO, false));
+    FilterEvaluator evaluator = new FilterEvaluator(filters);
+    assertEquals(Optional.of(true), evaluator.isVisible(nameNodeJmx));
+  }
+
+
+  /**
+   * If the link matches both a show and hide type link attribute filter, then it will be evaluated as hidden.
+   */
+  @Test
+  public void testHideFilterTakesPrecedence() throws Exception {
+    List<Filter> filters = Lists.<Filter>newArrayList(
+        linkAttributeFilter(AUTHENTICATED, false),
+        linkAttributeFilter(SSO, true));
+    FilterEvaluator evaluator = new FilterEvaluator(filters);
+    namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED, SSO));
+    assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi));
+  }
+
+  /**
+   * Accept-all filters are only evaluated if the link does not match any link name or link attribute filters.
+   */
+  @Test
+  public void acceptAllFilterEvaluatedLast() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        acceptAllFilter(false),
+        linkNameFilter(NAMENODE_JMX, true),
+        linkAttributeFilter(SSO, true));
+    FilterEvaluator evaluator = new FilterEvaluator(filters);
+    assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi));
+  }
+
+  /**
+   * Contradicting link name filters should result in {@link QuickLinksProfileEvaluationException}.
+   */
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void contradictingLinkNameFiltersRejected() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        linkNameFilter(NAMENODE_JMX, true),
+        linkNameFilter(NAMENODE_JMX, false),
+        linkAttributeFilter(SSO, true));
+    new FilterEvaluator(filters);
+  }
+
+  /**
+   * Contradicting property filters should result in {@link QuickLinksProfileEvaluationException}.
+   * @throws Exception
+   */
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void contradictingPropertyFiltersRejected() throws Exception {
+    List<Filter> filters = Lists.<Filter>newArrayList(
+        linkAttributeFilter(SSO, true),
+        linkAttributeFilter(SSO, false));
+    new FilterEvaluator(filters);
+  }
+
+
+  /**
+   * Contradicting link attribute filters should result in {@link QuickLinksProfileEvaluationException}.
+   */
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void contradictingLinkAttributeFiltersRejected() throws Exception {
+    List<Filter> filters = Lists.<Filter>newArrayList(
+        linkAttributeFilter(SSO, true),
+        linkAttributeFilter(SSO, false));
+    new FilterEvaluator(filters);
+  }
+
+  /**
+   * Contradicting accept-all filters should result in {@link QuickLinksProfileEvaluationException}.
+   */
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void contradictingAcceptAllFiltersRejected() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        linkNameFilter(NAMENODE_JMX, true),
+        linkAttributeFilter(SSO, true),
+        acceptAllFilter(true),
+        acceptAllFilter(false));
+    new FilterEvaluator(filters);
+  }
+
+  /**
+   * Duplicate filter declarations are ok if their visibility rule is the same
+   */
+  @Test
+  public void duplicateFiltersAreOkIfDoNotContradict() throws Exception {
+    List<Filter> filters = Lists.newArrayList(
+        acceptAllFilter(true),
+        acceptAllFilter(true),
+        linkNameFilter(NAMENODE_JMX, false),
+        linkNameFilter(NAMENODE_JMX, false),
+        linkAttributeFilter(SSO, false),
+        linkAttributeFilter(SSO, false));
+    FilterEvaluator evaluator = new FilterEvaluator(filters);
+    assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi));
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java
new file mode 100644
index 0000000..c278791
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+
+
+public class QuickLinkVisibilityControllerFactoryTest {
+
+  /**
+   * If receives a {@code null} quick profile string, the factory should return an instance of {@link ShowAllLinksVisibilityController}
+   * to mimic legacy behavior.
+   */
+  @Test
+  public void nullStringPassed() throws Exception {
+    assertTrue(
+        "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned",
+        QuickLinkVisibilityControllerFactory.get(null) instanceof ShowAllLinksVisibilityController);
+  }
+
+  /**
+   * If receives an invalid json as quick profile string, the factory should return an instance of {@link ShowAllLinksVisibilityController}
+   * to mimic legacy behavior.
+   */
+  @Test
+  public void invalidJsonPassed() throws Exception {
+    assertTrue(
+        "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned",
+        QuickLinkVisibilityControllerFactory.get("Hello world!") instanceof ShowAllLinksVisibilityController);
+  }
+
+  /**
+   * If receives an invalid quick profile string (e.g. valid json but no filters defined or contradicting filters),
+   * the factory should return an instance of {@link ShowAllLinksVisibilityController} to mimic legacy behavior.
+   */
+  @Test
+  public void invalidProfilePassed() throws Exception {
+    String json = Resources.toString(Resources.getResource("inconsistent_quicklinks_profile.json"), Charsets.UTF_8);
+    assertTrue(
+        "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned",
+        QuickLinkVisibilityControllerFactory.get(json) instanceof ShowAllLinksVisibilityController);
+
+    json = Resources.toString(Resources.getResource("inconsistent_quicklinks_profile_2.json"), Charsets.UTF_8);
+    assertTrue(
+        "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned",
+        QuickLinkVisibilityControllerFactory.get(json) instanceof ShowAllLinksVisibilityController);
+  }
+
+  /**
+   * If receives a valid profile json string, an visibility controller should be returned that behaves according to the
+   * received profile.
+   */
+  @Test
+  public void validProfilePassed() throws Exception {
+    String json = Resources.toString(Resources.getResource("example_quicklinks_profile.json"), Charsets.UTF_8);
+    assertTrue(
+        "An instance of " + DefaultQuickLinkVisibilityController.class.getSimpleName() + " should have been returned",
+        QuickLinkVisibilityControllerFactory.get(json) instanceof DefaultQuickLinkVisibilityController);
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java
new file mode 100644
index 0000000..9e44e45
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.ambari.server.state.quicklinksprofile;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class QuickLinkVisibilityControllerTest {
+
+  static final String AUTHENTICATED = "authenticated";
+  static final String SSO = "sso";
+  static final String NAMENODE = "NAMENODE";
+  static final String HDFS = "HDFS";
+  static final String NAMENODE_UI = "namenode_ui";
+
+
+  private Link namenodeUi;
+
+  public QuickLinkVisibilityControllerTest() {
+    namenodeUi = new Link();
+    namenodeUi.setComponentName(NAMENODE);
+    namenodeUi.setName(NAMENODE_UI);
+    namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED));
+  }
+
+  /**
+   * Test to prove that {@link DefaultQuickLinkVisibilityController} can accept quicklink profiles with null values.
+   */
+  @Test
+  public void testNullsAreAccepted() throws Exception {
+    QuickLinksProfile profile = QuickLinksProfile.create(ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), null);
+    DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+    evaluator.isVisible(HDFS, namenodeUi); //should not throw NPE
+
+    Service service = Service.create(HDFS, ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), null);
+    profile = QuickLinksProfile.create(null, ImmutableList.of(service));
+    evaluator = new DefaultQuickLinkVisibilityController(profile);
+    evaluator.isVisible(HDFS, namenodeUi); //should not throw NPE
+
+  }
+
+  /**
+   * Quicklinks profile must contain at least one filter (can be on any level: global/component/service), otherwise
+   * an exception is thrown.
+   */
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void testProfileMustContainAtLeastOneFilter() throws Exception {
+    Component component = Component.create("NAMENODE", null);
+    Service service = Service.create(HDFS, null, ImmutableList.of(component));
+    QuickLinksProfile profile = QuickLinksProfile.create(null, ImmutableList.of(service));
+    QuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+  }
+
+  /**
+   * Test to prove that {@link Link}'s with unset {@code componentName} fields are handled properly.
+   */
+  @Test
+  public void testLinkWithNoComponentField() throws Exception {
+    Component component = Component.create(NAMENODE,
+        ImmutableList.<Filter>of(Filter.linkNameFilter(NAMENODE_UI, true)));
+
+    Service service = Service.create(HDFS, ImmutableList.<Filter>of(), ImmutableList.of(component));
+
+    QuickLinksProfile profile = QuickLinksProfile.create(ImmutableList.<Filter>of(), ImmutableList.of(service));
+    DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+    namenodeUi.setComponentName(null);
+    assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi));
+  }
+
+  /**
+   * Test to prove that component level filters are evaluated first.
+   */
+  @Test
+  public void testComponentLevelFiltersEvaluatedFirst() throws Exception {
+    Component component = Component.create(
+        NAMENODE,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true)));
+
+    Service service = Service.create(
+        HDFS,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, false)),
+        ImmutableList.of(component));
+
+    QuickLinksProfile profile = QuickLinksProfile.create(
+        ImmutableList.<Filter>of(Filter.acceptAllFilter(false)),
+        ImmutableList.of(service));
+
+    DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+    assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi));
+  }
+
+  /**
+   * Test to prove that service level filters are evaluated secondly.
+   */
+  @Test
+  public void testServiceLevelFiltersEvaluatedSecondly() throws Exception {
+    Component component = Component.create(NAMENODE,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)));
+
+    Service service = Service.create(HDFS,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true)),
+        ImmutableList.of(component));
+
+    QuickLinksProfile profile = QuickLinksProfile.create(
+        ImmutableList.<Filter>of(Filter.acceptAllFilter(false)),
+        ImmutableList.of(service));
+
+    DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+    assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi));
+  }
+
+  /**
+   * Test to prove that global filters are evaluated last.
+   */
+  @Test
+  public void testGlobalFiltersEvaluatedLast() throws Exception {
+    Component component = Component.create(NAMENODE,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)));
+
+    Service service = Service.create(HDFS,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)),
+        ImmutableList.of(component));
+
+    QuickLinksProfile profile = QuickLinksProfile.create(
+        ImmutableList.<Filter>of(Filter.acceptAllFilter(true)),
+        ImmutableList.of(service));
+
+    DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+    assertTrue("Global filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi));
+  }
+
+  /**
+   * Test to prove that the link is hidden if no filters apply.
+   */
+  @Test
+  public void testNoMatchingRule() throws Exception {
+    Component component1 = Component.create(NAMENODE,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)));
+
+    Component component2 = Component.create("DATANODE",
+        ImmutableList.<Filter>of(Filter.acceptAllFilter(true)));
+
+    Service service1 = Service.create(HDFS,
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)),
+        ImmutableList.of(component1, component2));
+
+    Service service2 = Service.create("YARN",
+        ImmutableList.<Filter>of(Filter.acceptAllFilter(true)),
+        ImmutableList.<Component>of());
+
+    QuickLinksProfile profile = QuickLinksProfile.create(
+        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)),
+        ImmutableList.of(service1, service2));
+
+    DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+    assertFalse("No filters should have been applied, so default false should have been returned.",
+        evaluator.isVisible(HDFS, namenodeUi));
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java
deleted file mode 100644
index 6a31ca0..0000000
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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.ambari.server.state.quicklinksprofile;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.apache.ambari.server.state.quicklinks.Link;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-
-public class QuickLinksProfileEvaluatorTest {
-
-  static final String AUTHENTICATED = "authenticated";
-  static final String SSO = "sso";
-  static final String NAMENODE = "NAMENODE";
-  static final String HDFS = "HDFS";
-  static final String NAMENODE_UI = "namenode_ui";
-
-
-  private Link namenodeUi;
-
-  public QuickLinksProfileEvaluatorTest() {
-    namenodeUi = new Link();
-    namenodeUi.setComponentName(NAMENODE);
-    namenodeUi.setName(NAMENODE_UI);
-    namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED));
-  }
-
-  /**
-   * Test to prove that {@link QuickLinksProfileEvaluator} can accept quicklink profiles with null values.
-   */
-  @Test
-  public void testNullsAreAccepted() throws Exception {
-    QuickLinksProfile profile = new QuickLinksProfile();
-    QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile);
-    assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi));
-
-    Service service = Service.create(HDFS, null, null);
-    profile = QuickLinksProfile.create(null, ImmutableList.of(service));
-    evaluator = new QuickLinksProfileEvaluator(profile);
-    assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi));
-  }
-
-  /**
-   * Test to prove that {@link Link}'s with unset {@code componentName} fields are handled properly.
-   */
-  @Test
-  public void testLinkWithNoComponentField() throws Exception {
-    Component component = Component.create(NAMENODE,
-        ImmutableList.<Filter>of(Filter.linkNameFilter(NAMENODE_UI, true)));
-
-    Service service = Service.create(HDFS, ImmutableList.<Filter>of(), ImmutableList.of(component));
-
-    QuickLinksProfile profile = QuickLinksProfile.create(ImmutableList.<Filter>of(), ImmutableList.of(service));
-    QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile);
-    namenodeUi.setComponentName(null);
-    assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi));
-  }
-
-  /**
-   * Test to prove that component level filters are evaluated first.
-   */
-  @Test
-  public void testComponentLevelFiltersEvaluatedFirst() throws Exception {
-    Component component = Component.create(
-        NAMENODE,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true)));
-
-    Service service = Service.create(
-        HDFS,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, false)),
-        ImmutableList.of(component));
-
-    QuickLinksProfile profile = QuickLinksProfile.create(
-        ImmutableList.<Filter>of(Filter.acceptAllFilter(false)),
-        ImmutableList.of(service));
-
-    QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile);
-    assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi));
-  }
-
-  /**
-   * Test to prove that service level filters are evaluated secondly.
-   */
-  @Test
-  public void testServiceLevelFiltersEvaluatedSecondly() throws Exception {
-    Component component = Component.create(NAMENODE,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)));
-
-    Service service = Service.create(HDFS,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true)),
-        ImmutableList.of(component));
-
-    QuickLinksProfile profile = QuickLinksProfile.create(
-        ImmutableList.<Filter>of(Filter.acceptAllFilter(false)),
-        ImmutableList.of(service));
-
-    QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile);
-    assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi));
-  }
-
-  /**
-   * Test to prove that global filters are evaluated last.
-   */
-  @Test
-  public void testGlobalFiltersEvaluatedLast() throws Exception {
-    Component component = Component.create(NAMENODE,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)));
-
-    Service service = Service.create(HDFS,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)),
-        ImmutableList.of(component));
-
-    QuickLinksProfile profile = QuickLinksProfile.create(
-        ImmutableList.<Filter>of(Filter.acceptAllFilter(true)),
-        ImmutableList.of(service));
-
-    QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile);
-    assertTrue("Global filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi));
-  }
-
-  /**
-   * Test to prove that the link is hidden if no filters apply.
-   */
-  @Test
-  public void testNoMatchingRule() throws Exception {
-    Component component1 = Component.create(NAMENODE,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)));
-
-    Component component2 = Component.create("DATANODE",
-        ImmutableList.<Filter>of(Filter.acceptAllFilter(true)));
-
-    Service service1 = Service.create(HDFS,
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)),
-        ImmutableList.of(component1, component2));
-
-    Service service2 = Service.create("YARN",
-        ImmutableList.<Filter>of(Filter.acceptAllFilter(true)),
-        ImmutableList.<Component>of());
-
-    QuickLinksProfile profile = QuickLinksProfile.create(
-        ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)),
-        ImmutableList.of(service1, service2));
-
-    QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile);
-    assertFalse("No filters should have been applied, so default false should have been returned.",
-        evaluator.isVisible(HDFS, namenodeUi));
-  }
-
-}
\ No newline at end of file