You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2016/12/20 14:35:05 UTC

[03/15] ambari git commit: AMBARI-18978. Create Quick link profile data model and json parser. (Balazs Bence Sari via stoader)

AMBARI-18978. Create Quick link profile data model and json parser. (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/61dcdc3f
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/61dcdc3f
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/61dcdc3f

Branch: refs/heads/branch-dev-patch-upgrade
Commit: 61dcdc3f1db0f8091ab3314f6746dace68847178
Parents: fce9d6e
Author: Balazs Bence Sari <bs...@hortonworks.com>
Authored: Mon Dec 19 10:47:47 2016 +0100
Committer: Toader, Sebastian <st...@hortonworks.com>
Committed: Mon Dec 19 10:47:47 2016 +0100

----------------------------------------------------------------------
 .../quicklinksprofile/AcceptAllFilter.java      |  46 ++++++++
 .../state/quicklinksprofile/Component.java      |  57 ++++++++++
 .../server/state/quicklinksprofile/Filter.java  |  78 ++++++++++++++
 .../state/quicklinksprofile/LinkNameFilter.java |  61 +++++++++++
 .../state/quicklinksprofile/PropertyFilter.java |  60 +++++++++++
 .../quicklinksprofile/QuickLinksProfile.java    |  70 ++++++++++++
 .../QuickLinksProfileParser.java                | 108 +++++++++++++++++++
 .../server/state/quicklinksprofile/Service.java |  72 +++++++++++++
 .../QuickLinksConfigurationModuleTest.java      |   2 -
 .../QuickLinksProfileParserTest.java            |  70 ++++++++++++
 .../resources/example_quicklinks_profile.json   |  48 +++++++++
 .../inconsistent_quicklinks_profile.json        |  10 ++
 12 files changed, 680 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
new file mode 100644
index 0000000..5124241
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.state.quicklinksprofile;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+
+/**
+ * A filter that accepts all links. It is useful to specify a general rule while the more specific
+ * ({@link LinkNameFilter} and {@link PropertyFilter}) filters handle more special cases.
+ */
+public class AcceptAllFilter extends Filter {
+
+  @Override
+  public boolean accept(Link link) {
+    return true;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    AcceptAllFilter that = (AcceptAllFilter) o;
+    return isVisible() == that.isVisible();
+  }
+
+  @Override
+  public int hashCode() {
+    return java.util.Objects.hash(isVisible());
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
new file mode 100644
index 0000000..7ef0259
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.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.util.List;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * Class to represent component-level filter definitions
+ */
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Component {
+  @JsonProperty("name")
+  private String name;
+
+  @JsonProperty("filters")
+  private List<Filter> filters;
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * @return the quicklink filters for this component
+   */
+  public List<Filter> getFilters() {
+    return filters;
+  }
+
+  public void setFilters(List<Filter> filters) {
+    this.filters = filters;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
new file mode 100644
index 0000000..1711628
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
@@ -0,0 +1,78 @@
+/*
+ * 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 org.apache.ambari.server.state.quicklinks.Link;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+/**
+ * Base class to represent a quicklink filter. A quicklink filter has two important features:
+ * <ul>
+ *   <li>It can tell if it applies to a link (see {@link #accept(Link)} method).</li>
+ *   <li>It can specify the visibility of the links it applies to (see {@link #isVisible()} method).</li>
+ * </ul>
+ */
+public abstract class Filter {
+
+  @JsonProperty("visible")
+  private boolean visible;
+
+  /**
+   * @return a boolean indicating whether links accepted by this filter should be shown or hidden
+   */
+  public boolean isVisible() {
+    return visible;
+  }
+
+  public void setVisible(boolean visible) {
+    this.visible = visible;
+  }
+
+  /**
+   * @param link the link to examine
+   * @return if this filter applies to the link in the parameter
+   */
+  public abstract boolean accept(Link link);
+
+  // Factory methods
+
+  static AcceptAllFilter acceptAllFilter(boolean visible) {
+    AcceptAllFilter acceptAllFilter = new AcceptAllFilter();
+    acceptAllFilter.setVisible(visible);
+    return acceptAllFilter;
+  }
+
+  static LinkNameFilter linkNameFilter(String linkName, boolean visible) {
+    LinkNameFilter linkNameFilter = new LinkNameFilter();
+    linkNameFilter.setLinkName(linkName);
+    linkNameFilter.setVisible(visible);
+    return linkNameFilter;
+  }
+
+  static PropertyFilter propertyFilter(String propertyName, boolean visible) {
+    PropertyFilter propertyFilter = new PropertyFilter();
+    propertyFilter.setPropertyName(propertyName);
+    propertyFilter.setVisible(visible);
+    return propertyFilter;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java
new file mode 100644
index 0000000..b874295
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java
@@ -0,0 +1,61 @@
+/*
+ * 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.Objects;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * A filter that accepts quicklinks based on name match.
+ */
+public class LinkNameFilter extends Filter {
+
+  static final String LINK_NAME = "link_name";
+
+  @JsonProperty(LINK_NAME)
+  private String linkName;
+
+  public String getLinkName() {
+    return linkName;
+  }
+
+  public void setLinkName(String linkName) {
+    this.linkName = linkName;
+  }
+
+  @Override
+  public boolean accept(Link link) {
+    return Objects.equals(link.getName(), linkName);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    LinkNameFilter that = (LinkNameFilter) o;
+    return isVisible() == that.isVisible() && Objects.equals(linkName, that.linkName);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(isVisible(), linkName);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/PropertyFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/PropertyFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/PropertyFilter.java
new file mode 100644
index 0000000..7b5eba0
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/PropertyFilter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.Objects;
+
+import org.apache.ambari.server.state.quicklinks.Link;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * A quicklink filter based on property-match (the filter's property is contained by the links set of properties)
+ */
+public class PropertyFilter extends Filter {
+  static final String PROPERTY_NAME = "property_name";
+
+  @JsonProperty(PROPERTY_NAME)
+  private String propertyName;
+
+  public String getPropertyName() {
+    return propertyName;
+  }
+
+  public void setPropertyName(String propertyName) {
+    this.propertyName = propertyName;
+  }
+
+  @Override
+  public boolean accept(Link link) {
+    return link.getProperties().contains(propertyName);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    PropertyFilter that = (PropertyFilter) o;
+    return isVisible() == that.isVisible() && Objects.equals(propertyName, that.propertyName);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(isVisible(), propertyName);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/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
new file mode 100644
index 0000000..1a1488b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
@@ -0,0 +1,70 @@
+/*
+ * 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.List;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+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:
+ * <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.
+ */
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class QuickLinksProfile {
+
+  @JsonProperty("filters")
+  private List<Filter> filters;
+
+  @JsonProperty("services")
+  private List<Service> services;
+
+  /**
+   * @return service-specific quicklink filter definitions
+   */
+  public List<Service> getServices() {
+    return services;
+  }
+
+  public void setServices(List<Service> services) {
+    this.services = services;
+  }
+
+  /**
+   * @return the global quicklink filters
+   */
+  public List<Filter> getFilters() {
+    return filters;
+  }
+
+  public void setFilters(List<Filter> filters) {
+    this.filters = filters;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
new file mode 100644
index 0000000..c1f3c86
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.net.URL;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.Version;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.deser.std.StdDeserializer;
+import org.codehaus.jackson.map.module.SimpleModule;
+import org.codehaus.jackson.node.ObjectNode;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Resources;
+
+/**
+ * Loads and parses JSON quicklink profiles.
+ */
+public class QuickLinksProfileParser {
+  private final ObjectMapper mapper = new ObjectMapper();
+
+  public QuickLinksProfileParser() {
+    SimpleModule module =
+        new SimpleModule("Quick Links Parser", new Version(1, 0, 0, null));
+    module.addDeserializer(Filter.class, new QuickLinksFilterDeserializer());
+    mapper.registerModule(module);
+  }
+
+
+  public QuickLinksProfile parse(byte[] input) throws IOException {
+    return mapper.readValue(input, QuickLinksProfile.class);
+  }
+
+  public QuickLinksProfile parse(URL url) throws IOException {
+    return parse(Resources.toByteArray(url));
+  }
+}
+
+/**
+ * Custom deserializer is needed to handle filter polymorphism.
+ */
+class QuickLinksFilterDeserializer extends StdDeserializer<Filter> {
+  private static final String PARSE_ERROR_MESSAGE =
+      "A filter is not allowed to declare both property_name and link_name at the same time.";
+
+  QuickLinksFilterDeserializer() {
+    super(Filter.class);
+  }
+
+  /**
+   * Filter polymorphism is handled here. If a filter object in the JSON document has:
+   * <ul>
+   *   <li>a {@code property_name} field, it will parsed as {@link PropertyFilter}</li>
+   *   <li>a {@code link_name} field, it will be parsed as {@link LinkNameFilter}</li>
+   *   <li>both {@code property_name} and {@code link_name}, it will throw a {@link JsonParseException}</li>
+   *   <li>neither of the above fields, it will be parsed as {@link AcceptAllFilter}</li>
+   * </ul>
+   *
+   * @throws JsonParseException if ambiguous filter definitions are found, or any JSON syntax error.
+   */
+  @Override
+  public Filter deserialize (JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
+    ObjectMapper mapper = (ObjectMapper) parser.getCodec();
+    ObjectNode root = (ObjectNode) mapper.readTree(parser);
+    Class<? extends Filter> filterClass = null;
+    for (String fieldName: ImmutableList.copyOf(root.getFieldNames())) {
+      switch(fieldName) {
+        case PropertyFilter.PROPERTY_NAME:
+          if (null != filterClass) {
+            throw new JsonParseException(PARSE_ERROR_MESSAGE, parser.getCurrentLocation());
+          }
+          filterClass = PropertyFilter.class;
+          break;
+        case LinkNameFilter.LINK_NAME:
+          if (null != filterClass) {
+            throw new JsonParseException(PARSE_ERROR_MESSAGE, parser.getCurrentLocation());
+          }
+          filterClass = LinkNameFilter.class;
+          break;
+      }
+    }
+    if (null == filterClass) {
+      filterClass = AcceptAllFilter.class;
+    }
+    return mapper.readValue(root, filterClass);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
new file mode 100644
index 0000000..600872f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
@@ -0,0 +1,72 @@
+/*
+ * 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.List;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * Class to represent component-level filter definitions
+ */
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Service {
+  @JsonProperty("name")
+  private String name;
+
+  @JsonProperty("components")
+  private List<Component> components;
+
+  @JsonProperty("filters")
+  private List<Filter> filters;
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * @return component-specific quicklink filter definitions for components of this service
+   */
+  public List<Component> getComponents() {
+    return components;
+  }
+
+  public void setComponents(List<Component> components) {
+    this.components = components;
+  }
+
+  /**
+   * @return service-specific filters for this service
+   */
+  public List<Filter> getFilters() {
+    return filters;
+  }
+
+  public void setFilters(List<Filter> filters) {
+    this.filters = filters;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/test/java/org/apache/ambari/server/stack/QuickLinksConfigurationModuleTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/stack/QuickLinksConfigurationModuleTest.java b/ambari-server/src/test/java/org/apache/ambari/server/stack/QuickLinksConfigurationModuleTest.java
index 6f9827d..190e61b 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/stack/QuickLinksConfigurationModuleTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/stack/QuickLinksConfigurationModuleTest.java
@@ -146,10 +146,8 @@ public class QuickLinksConfigurationModuleTest {
     assertEquals("Links are not properly overridden for foo_logs",
         new ArrayList<>(),
         linksByName.get("foo_logs").getProperties());
-
   }
 
-
   private QuickLinks[] resolveQuickLinks(String parentJson, String childJson) throws AmbariException{
     File parentQuiclinksFile = new File(this.getClass().getClassLoader().getResource(parentJson).getFile());
     File childQuickLinksFile = new File(this.getClass().getClassLoader().getResource(childJson).getFile());

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/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
new file mode 100644
index 0000000..0644027
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.assertEquals;
+
+import com.google.common.io.Resources;
+import org.codehaus.jackson.JsonParseException;
+import org.junit.Test;
+
+
+public class QuickLinksProfileParserTest {
+
+
+  @Test
+  public void testParseProfile() throws Exception {
+    String profileName = "example_quicklinks_profile.json";
+    QuickLinksProfileParser parser = new QuickLinksProfileParser();
+    QuickLinksProfile profile = parser.parse(Resources.getResource(profileName));
+    assertEquals(1, profile.getFilters().size());
+    assertEquals(
+        Filter.propertyFilter("sso", true),
+        profile.getFilters().get(0));
+    assertEquals(2, profile.getServices().size());
+
+    Service hdfs = profile.getServices().get(0);
+    assertEquals("HDFS", hdfs.getName());
+    assertEquals(1, hdfs.getFilters().size());
+    assertEquals(1, hdfs.getComponents().size());
+    assertEquals(
+        Filter.propertyFilter("authenticated", true),
+        hdfs.getFilters().get(0));
+
+    Component nameNode = hdfs.getComponents().get(0);
+    assertEquals(2, nameNode.getFilters().size());
+    assertEquals(
+        Filter.linkNameFilter("namenode_ui", false),
+        nameNode.getFilters().get(0));
+
+    Component historyServer = profile.getServices().get(1).getComponents().get(0);
+    assertEquals(1, historyServer.getFilters().size());
+    assertEquals(
+        Filter.acceptAllFilter(true),
+        historyServer.getFilters().get(0));
+  }
+
+  @Test(expected = JsonParseException.class)
+  public void testParseInconsistentProfile() throws Exception {
+    String profileName = "inconsistent_quicklinks_profile.json";
+    QuickLinksProfileParser parser = new QuickLinksProfileParser();
+    QuickLinksProfile profile = parser.parse(Resources.getResource(profileName));
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/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
new file mode 100644
index 0000000..028d011
--- /dev/null
+++ b/ambari-server/src/test/resources/example_quicklinks_profile.json
@@ -0,0 +1,48 @@
+{
+  "filters": [
+    {
+      "property_name": "sso",
+      "visible": true
+    }
+  ],
+  "services": [
+    {
+      "name": "HDFS",
+      "filters": [
+        {
+          "property_name": "authenticated",
+          "visible": true
+        }
+      ],
+      "components": [
+        {
+          "name": "NAMENODE",
+          "filters": [
+            {
+              "link_name": "namenode_ui",
+              "visible": false
+            },
+            {
+              "link_name": "namenode_jmx",
+              "visible": false
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name": "MAPREDUCE2",
+      "filters": [],
+      "components": [
+        {
+          "name": "HISTORYSERVER",
+          "filters": [
+            {
+              "visible": true
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/61dcdc3f/ambari-server/src/test/resources/inconsistent_quicklinks_profile.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/inconsistent_quicklinks_profile.json b/ambari-server/src/test/resources/inconsistent_quicklinks_profile.json
new file mode 100644
index 0000000..e5bc310
--- /dev/null
+++ b/ambari-server/src/test/resources/inconsistent_quicklinks_profile.json
@@ -0,0 +1,10 @@
+{
+  "filters": [
+    {
+      "property_name": "sso",
+      "link_name": "namenode_ui",
+      "visible": true
+    }
+  ],
+  "services": []
+}
\ No newline at end of file