AMBARI-16437. Add conditional constraints for Kerberos identities to control 
when they are created (rlevas)


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

Branch: refs/heads/trunk
Commit: 2fcc947530eb521bbcb3bdc941a5a680a4145140
Parents: 069cc49
Author: Robert Levas <[email protected]>
Authored: Sat May 28 08:02:00 2016 -0400
Committer: Robert Levas <[email protected]>
Committed: Sat May 28 08:02:05 2016 -0400

----------------------------------------------------------------------
 .../ambari/server/collections/Predicate.java    |  82 +++++++++++
 .../server/collections/PredicateUtils.java      | 111 ++++++++++++++
 .../collections/functors/AndPredicate.java      |  96 +++++++++++++
 .../collections/functors/ContainsPredicate.java | 138 ++++++++++++++++++
 .../functors/ContextTransformer.java            | 142 ++++++++++++++++++
 .../DelegatedMultiplePredicateContainer.java    | 114 +++++++++++++++
 .../DelegatedSinglePredicateContainer.java      | 110 ++++++++++++++
 .../collections/functors/EqualsPredicate.java   | 115 +++++++++++++++
 .../collections/functors/NotPredicate.java      |  80 +++++++++++
 .../functors/OperationPredicate.java            | 112 +++++++++++++++
 .../collections/functors/OrPredicate.java       |  96 +++++++++++++
 .../functors/PredicateClassFactory.java         |  58 ++++++++
 .../server/controller/KerberosHelperImpl.java   |  64 ++++++---
 .../AbstractPrepareKerberosServerAction.java    |  10 +-
 .../AbstractKerberosDescriptorContainer.java    |  49 ++++---
 .../kerberos/KerberosIdentityDescriptor.java    |  80 ++++++++++-
 .../server/upgrade/UpgradeCatalog240.java       |   2 +-
 .../server/collections/PredicateUtilsTest.java  | 143 +++++++++++++++++++
 .../collections/functors/AndPredicateTest.java  | 108 ++++++++++++++
 .../functors/ContainsPredicateTest.java         |  91 ++++++++++++
 .../functors/ContextTransformerTest.java        |  75 ++++++++++
 .../functors/EqualsPredicateTest.java           |  89 ++++++++++++
 .../collections/functors/NotPredicateTest.java  |  89 ++++++++++++
 .../collections/functors/OrPredicateTest.java   | 107 ++++++++++++++
 .../server/controller/KerberosHelperTest.java   |  32 +++--
 .../state/kerberos/KerberosDescriptorTest.java  |   4 +-
 .../KerberosIdentityDescriptorTest.java         |  17 +++
 27 files changed, 2152 insertions(+), 62 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java
new file mode 100644
index 0000000..09bc7d5
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.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.collections;
+
+import com.google.gson.Gson;
+
+import java.util.Map;
+
+/**
+ * {@link Predicate} wraps {@link org.apache.commons.collections.Predicate} to
+ * provide additional functionality like serializing to and from a Map and 
JSON formatted data.
+ */
+public abstract class Predicate implements 
org.apache.commons.collections.Predicate {
+
+  /**
+   * The name of this predicate. For example "and", "or", etc...
+   */
+  private final String name;
+
+  protected Predicate(String name) {
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Serialize this {@link Predicate} to a {@link Map}
+   *
+   * @return a {@link Map}
+   */
+  public abstract Map<String, Object> toMap();
+
+  /**
+   * Serialize this {@link Predicate} to a JSON-formatted {@link String}
+   *
+   * @return a JSON-formatted {@link String}
+   */
+  public String toJSON() {
+    Map<String, Object> map = toMap();
+    return (map == null) ? null : new Gson().toJson(map);
+  }
+
+
+  @Override
+  public int hashCode() {
+    return 37 * ((name == null) ? 0 : name.hashCode());
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    } else if (obj == null) {
+      return false;
+    } else if ((obj instanceof Predicate) && (hashCode() == obj.hashCode())) {
+      Predicate p = (Predicate) obj;
+      return (name == null) ? (p.name == null) : name.equals(p.name);
+    } else {
+      return false;
+    }
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java
new file mode 100644
index 0000000..2a75d81
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java
@@ -0,0 +1,111 @@
+/*
+ * 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.collections;
+
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import org.apache.ambari.server.collections.functors.PredicateClassFactory;
+import org.apache.commons.lang.StringUtils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * PredicateUtils is a utility class providing methods to help perform tasks 
on {@link Predicate}s.
+ */
+public class PredicateUtils {
+  private static final Type PARSED_TYPE = new TypeToken<Map<String, Object>>() 
{
+  }.getType();
+
+  /**
+   * Safely serializes the specified {@link Predicate} to a {@link Map}
+   *
+   * @param predicate the {@link Predicate} to process
+   * @return a {@link Map} or null of the supplied predicate is null
+   */
+  public static Map<String, Object> toMap(Predicate predicate) {
+    return (predicate == null) ? null : predicate.toMap();
+  }
+
+  /**
+   * Builds a {@link Predicate} from a {@link Map}
+   *
+   * @param map a map containing the details of the predicate to create.
+   * @return a {@link Predicate}
+   */
+  public static Predicate fromMap(Map<String, Object> map) {
+    Predicate predicate = null;
+
+    if ((map != null) && !map.isEmpty()) {
+      if (map.size() == 1) {
+        Map.Entry<String, Object> entry = map.entrySet().iterator().next();
+        String name = entry.getKey();
+
+        Class<? extends Predicate> predicateClass = 
PredicateClassFactory.getPredicateClass(name);
+
+        if (predicateClass == null) {
+          throw new IllegalArgumentException(String.format("Unexpected 
predicate name - %s", name));
+        } else {
+          try {
+            // Dynamically locate and invoke the static toMap method for the 
named Predicate
+            // implementation using reflection
+            Method method = predicateClass.getMethod("fromMap", Map.class);
+            if (method == null) {
+              throw new UnsupportedOperationException(String.format("Cannot 
translate data to a %s - %s", predicateClass.getName(), "Failed to find toMap 
method"));
+            } else {
+              predicate = (Predicate) method.invoke(null, 
Collections.singletonMap(name, entry.getValue()));
+            }
+          } catch (NoSuchMethodException | InvocationTargetException | 
IllegalAccessException e) {
+            throw new UnsupportedOperationException(String.format("Cannot 
translate data to a %s - %s", predicateClass.getName(), 
e.getLocalizedMessage()), e);
+          }
+        }
+      } else {
+        throw new IllegalArgumentException(String.format("Too many map entries 
have been encountered - %d", map.size()));
+      }
+    }
+
+    return predicate;
+  }
+
+
+  /**
+   * Safely serializes the specified {@link Predicate} to a JSON-formatted 
{@link String}
+   *
+   * @param predicate the {@link Predicate} to process
+   * @return a {@link String} or null of the supplied predicate is null
+   */
+  public static String toJSON(Predicate predicate) {
+    return (predicate == null) ? null : predicate.toJSON();
+  }
+
+  /**
+   * Builds a {@link Predicate} from a JSON-formatted {@link String}
+   *
+   * @param json a string containing the details of the predicate to create.
+   * @return a {@link Predicate}
+   * @see #fromMap(Map)
+   */
+  public static Predicate fromJSON(String json) {
+    Map<String, Object> map = new Gson().fromJson(json, PARSED_TYPE);
+    return (StringUtils.isEmpty(json) ? null : fromMap(map));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java
new file mode 100644
index 0000000..62a953a
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java
@@ -0,0 +1,96 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.PredicateUtils;
+import org.apache.commons.collections.Predicate;
+import org.apache.commons.collections.functors.PredicateDecorator;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * {@link AndPredicate} wraps {@link 
org.apache.commons.collections.functors.AndPredicate} to
+ * provide additional functionality like serializing to and from a Map and 
JSON formatted data.
+ * <p>
+ * See {@link DelegatedMultiplePredicateContainer}
+ */
+public class AndPredicate extends DelegatedMultiplePredicateContainer {
+  /**
+   * The name of this {@link org.apache.ambari.server.collections.Predicate} 
implementation
+   */
+  public static final String NAME = "and";
+
+  /**
+   * Creates a new {@link AndPredicate} using the given {@link Map} of data.
+   * <p>
+   * It is expected that the map contains a single {@link java.util.Map.Entry} 
where the key name
+   * is "and" and the value is a {@link Collection} of {@link Map}s 
representing the contained
+   * predicates.
+   *
+   * @return a new {@link AndPredicate}
+   */
+  public static AndPredicate fromMap(Map<String, Object> map) {
+    Object data = (map == null) ? null : map.get(NAME);
+
+    if (data == null) {
+      throw new IllegalArgumentException("Missing data for '" + NAME + "' 
operation");
+    } else if (data instanceof Collection) {
+      Collection<?> collection = (Collection) data;
+      if (collection.size() == 2) {
+        Iterator<?> iterator = collection.iterator();
+        Object d1 = iterator.next();
+        Object d2 = iterator.next();
+
+        if ((d1 instanceof Map) && (d2 instanceof Map)) {
+          return new AndPredicate(PredicateUtils.fromMap((Map) d1), 
PredicateUtils.fromMap((Map) d2));
+        } else {
+          throw new IllegalArgumentException(String.format("Unexpected data 
types for predicates: %s and %s", d1.getClass().getName(), 
d2.getClass().getName()));
+        }
+      } else {
+        throw new IllegalArgumentException(String.format("Missing data for '" 
+ NAME + "' operation - 2 predicates are needed, %d found", collection.size()));
+      }
+    } else {
+      throw new IllegalArgumentException(String.format("Unexpected data type 
for '" + NAME + "' operation - %s", data.getClass().getName()));
+    }
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param predicate1 the first predicate to process
+   * @param predicate2 the second predicate to process (if the first one 
yields <code>true</code>
+   */
+  public AndPredicate(Predicate predicate1, Predicate predicate2) {
+    super(NAME,
+        (PredicateDecorator) 
org.apache.commons.collections.functors.AndPredicate.getInstance(predicate1, 
predicate2));
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() + this.getClass().getName().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return (obj == this) ||
+        ((obj != null) && (super.equals(obj) && 
this.getClass().isInstance(obj) && (hashCode() == obj.hashCode())));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java
new file mode 100644
index 0000000..503405e
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java
@@ -0,0 +1,138 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.Predicate;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * {@link ContainsPredicate} is a predicate implementation testing whether a 
string value exists
+ * within some set of strings.
+ * <p>
+ * It it expected that the data supplied to the {@link #evaluate(Object)} 
method is as {@link Set}
+ * of {@link String}s retrieved from a context (a {@link Map} of keys to 
values, where each value
+ * can be an explicit value or a {@link Map} of values creating a tree 
structure.
+ */
+public class ContainsPredicate extends OperationPredicate {
+
+  /**
+   * The name of this {@link Predicate} implementation
+   */
+  public static final String NAME = "contains";
+
+  /**
+   * The value to test for
+   */
+  private final String value;
+
+  /**
+   * Creates a new {@link ContainsPredicate} using the given {@link Map} of 
data.
+   * <p>
+   * It is expected that the map contains a single {@link java.util.Map.Entry} 
where the key name
+   * is "contains" and the value is a {@link Collection} of 2 {@link String}s. 
The first value
+   * should be the key to use when retrieving data from the context, which 
should resolve to a
+   * {@link Set} of {@link String}s (or null). The second value should be the 
value to check for
+   * existing in the relevant set.
+   *
+   * @return a new {@link ContainsPredicate}
+   */
+  public static ContainsPredicate fromMap(Map<String, Object> map) {
+
+    Object data = (map == null) ? null : map.get(NAME);
+
+    if (data == null) {
+      throw new IllegalArgumentException("Missing data for '" + NAME + "' 
operation");
+    } else if (data instanceof Collection) {
+      Collection<?> collection = (Collection) data;
+      if (collection.size() == 2) {
+        Iterator<?> iterator = collection.iterator();
+        Object d1 = iterator.next();
+        Object d2 = iterator.next();
+
+        if ((d1 instanceof String) && (d2 instanceof String)) {
+          return new ContainsPredicate(new ContextTransformer((String) d1), 
(String) d2);
+        } else {
+          throw new IllegalArgumentException(String.format("Unexpected data 
types: %s and %s", d1.getClass().getName(), d2.getClass().getName()));
+        }
+      } else {
+        throw new IllegalArgumentException(String.format("Missing data for '" 
+ NAME + "' operation - 2 predicates are needed, %d found", collection.size()));
+      }
+    } else {
+      throw new IllegalArgumentException(String.format("Unexpected data type 
for '" + NAME + "' operation - %s", data.getClass().getName()));
+    }
+  }
+
+  /**
+   * Constructor
+   *
+   * @param transformer the {@link ContextTransformer} configured with the 
context key to find
+   * @param value       the value to test
+   */
+  public ContainsPredicate(ContextTransformer transformer, String value) {
+    super(NAME, transformer);
+    this.value = value;
+  }
+
+  /**
+   * Gets the test value
+   *
+   * @return a string
+   */
+  public String getValue() {
+    return value;
+  }
+
+  @Override
+  protected boolean evaluateTransformedData(Object data) {
+    return (this.value != null) && (data instanceof Set) && ((Set<?>) 
data).contains(this.value);
+  }
+
+  @Override
+  public Map<String, Object> toMap() {
+    return Collections.<String, Object>singletonMap(NAME,
+        new ArrayList<String>(Arrays.asList(getContextKey(), value)));
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() +
+        (37 * ((this.value == null) ? 0 : this.value.hashCode()));
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    } else if (obj == null) {
+      return false;
+    } else if (super.equals(obj) && (obj instanceof ContainsPredicate) && 
(hashCode() == obj.hashCode())) {
+      ContainsPredicate p = (ContainsPredicate) obj;
+      return (value == null) ? (p.value == null) : value.equals(p.value);
+    } else {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java
new file mode 100644
index 0000000..b20963a
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java
@@ -0,0 +1,142 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.commons.collections.Transformer;
+
+import java.util.Map;
+
+/**
+ * {@link ContextTransformer} is a {@link Transformer} implementation that 
traverses a {@link Map}
+ * of {@link Map}s to find the value related to the specified key.
+ * <p>
+ * This implementation first checks the context map for the explicit key. If 
the key exists, it
+ * returns the value associated with it. If the key does not exist and appears 
to represent a path
+ * via "/"-delimited keys (configurations/service-site/property_name), the 
path is traversed
+ * recursively looking for the requested data.
+ * <p>
+ * Example context:
+ * <pre>
+ * |- services : [set of services]
+ * |
+ * |- configurations
+ * |  |- service-site
+ * |  |  |- property : [value]
+ * |
+ * |- key/looks/like/path : [value2]
+ * </pre>
+ * <ul>
+ * <li>If the key was <code>services</code>, <code>[set of services]</code> 
would be returned</li>
+ * <li>If the key was <code>configurations/service-site/property</code>, 
<code>value</code> would be returned</li>
+ * <li>If the key was <code>configurations/service-site</code>, <code>[map of 
service-site properties]</code> would be returned</li>
+ * <li>If the key was <code>key/looks/like/path</code>, <code>value2</code> 
would be returned</li>
+ * </ul>
+ */
+public class ContextTransformer implements Transformer {
+
+  /**
+   * The key to search for
+   */
+  private final String key;
+
+  /**
+   * Constructor.
+   *
+   * @param key the key to search for
+   */
+  public ContextTransformer(String key) {
+    this.key = key;
+  }
+
+  /**
+   * Returns the key this transformer is using to search for data
+   *
+   * @return a string
+   */
+  public String getKey() {
+    return key;
+  }
+
+  @Override
+  public Object transform(Object o) {
+    return transform(key, o);
+  }
+
+  /**
+   * Traverses the input object (expected to be a {@link Map}) to find the 
data associated with
+   * the specified key.
+   * <p>
+   * Note: This method is recursive in the even the key represents a path
+   *
+   * @param key the key to search for
+   * @param o   the object containing data to process
+   * @return the found data or null
+   */
+  private Object transform(String key, Object o) {
+    Object transformedData = null;
+
+    if (key != null) {
+      if (o instanceof Map) {
+        Map<?, ?> data = (Map) o;
+
+        if (data.containsKey(key)) {
+          transformedData = data.get(key);
+        } else {
+          // See if the key implies a tree that needs to be traversed...
+          // For example:  configurations/service-conf/property_name
+          //  A map of maps is expected such that the top level map has a map 
identified by the key
+          //  of "configuration".  The "configuration" map has a map 
identified by the key of
+          // "service-conf". The "service-conf" has a value identified by the 
key of "property_name".
+          String[] parts = key.split("\\/", 2);
+
+          // If only a single item is returned, than the key does not indicate 
a tree.
+          if (parts.length == 2) {
+            // If the first item is empty, a leading "/" was encountered... 
retry with the pruned key
+            if (parts[0].isEmpty()) {
+              transformedData = transform(parts[1], o);
+            } else {
+              transformedData = transform(parts[1], data.get(parts[0]));
+            }
+          }
+        }
+      }
+    }
+
+    return transformedData;
+  }
+
+  @Override
+  public int hashCode() {
+    return (37 * ((key == null) ? 0 : key.hashCode()));
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    } else if (obj == null) {
+      return false;
+    } else if ((obj instanceof ContextTransformer) && (hashCode() == 
obj.hashCode())) {
+      ContextTransformer t = (ContextTransformer) obj;
+      return (key == null) ? (t.key == null) : key.equals(t.key);
+    } else {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java
new file mode 100644
index 0000000..b9add7a
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.collections.functors;
+
+import org.apache.ambari.server.collections.Predicate;
+import org.apache.commons.collections.functors.PredicateDecorator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * DelegatedMultiplePredicateContainer is an abstract class providing 
functionality related to
+ * managing a delegate used to hold multiple {@link Predicate}s. For example 
<code>and</code> and
+ * <code>or</code> operators.
+ */
+abstract class DelegatedMultiplePredicateContainer extends Predicate 
implements PredicateDecorator {
+
+  /**
+   * The delegate {@link PredicateDecorator} used to handle the internal logic 
for the container
+   */
+  private final PredicateDecorator delegate;
+
+  /**
+   * Constructor.
+   *
+   * @param name     the name of this predicate
+   * @param delegate the delegate used to handle the internal logic for the 
container
+   */
+  DelegatedMultiplePredicateContainer(String name, PredicateDecorator 
delegate) {
+    super(name);
+    this.delegate = delegate;
+  }
+
+  @Override
+  public Map<String, Object> toMap() {
+    return Collections.<String, Object>singletonMap(getName(), 
containedPredicatesToMaps());
+  }
+
+  @Override
+  public boolean evaluate(Object o) {
+    return delegate.evaluate(o);
+  }
+
+  @Override
+  public org.apache.commons.collections.Predicate[] getPredicates() {
+    return delegate.getPredicates();
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() + ((delegate == null) ? 0 : delegate.hashCode());
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    } else if (obj == null) {
+      return false;
+    } else if (super.equals(obj) && (obj instanceof 
DelegatedMultiplePredicateContainer) && (hashCode() == obj.hashCode())) {
+      DelegatedMultiplePredicateContainer p = 
(DelegatedMultiplePredicateContainer) obj;
+      return (delegate == null) ? (p.delegate == null) : 
delegate.equals(p.delegate);
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * Processes the contained predicates into a {@link List} of {@link Map}s.
+   * <p>
+   * This is used to serialize this {@link 
DelegatedMultiplePredicateContainer} into a {@link Map}
+   *
+   * @return a list of maps representing the contained predicates
+   */
+  private List<Map<String, Object>> containedPredicatesToMaps() {
+
+    List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
+
+    if (delegate != null) {
+      org.apache.commons.collections.Predicate[] predicates = 
delegate.getPredicates();
+
+      if (predicates != null) {
+        for (org.apache.commons.collections.Predicate p : predicates) {
+          if (p instanceof Predicate) {
+            list.add(((Predicate) p).toMap());
+          } else {
+            throw new UnsupportedOperationException(String.format("Cannot 
convert a %s to a Map", p.getClass().getName()));
+          }
+        }
+      }
+    }
+
+    return list;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java
new file mode 100644
index 0000000..0e800ea
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java
@@ -0,0 +1,110 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.Predicate;
+import org.apache.commons.collections.functors.PredicateDecorator;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * DelegatedSinglePredicateContainer is an abstract class providing 
functionality to managing a
+ * delegate used to hold a since {@link Predicate}s. For example 
<code>not</code>.
+ */
+abstract class DelegatedSinglePredicateContainer extends Predicate implements 
PredicateDecorator {
+
+  /**
+   * The delegate {@link PredicateDecorator} used to handle the internal logic 
for the container
+   */
+  private final PredicateDecorator delegate;
+
+  /**
+   * Constructor.
+   *
+   * @param name     the name of this predicate
+   * @param delegate the delegate used to handle the internal logic for the 
container
+   */
+  DelegatedSinglePredicateContainer(String name, PredicateDecorator delegate) {
+    super(name);
+    this.delegate = delegate;
+  }
+
+  @Override
+  public Map<String, Object> toMap() {
+    return Collections.<String, Object>singletonMap(getName(), 
containedPredicateToMap());
+  }
+
+  @Override
+  public boolean evaluate(Object o) {
+    return delegate.evaluate(o);
+  }
+
+  @Override
+  public org.apache.commons.collections.Predicate[] getPredicates() {
+    return delegate.getPredicates();
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() + ((delegate == null) ? 0 : delegate.hashCode());
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    } else if (obj == null) {
+      return false;
+    } else if (super.equals(obj) && (obj instanceof 
DelegatedSinglePredicateContainer) && (hashCode() == obj.hashCode())) {
+      DelegatedSinglePredicateContainer p = 
(DelegatedSinglePredicateContainer) obj;
+      return (delegate == null) ? (p.delegate == null) : 
delegate.equals(p.delegate);
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * Processes the contained predicate into a Map.
+   * <p>
+   * This is used to serialize this {@link DelegatedSinglePredicateContainer} 
into a {@link Map}
+   *
+   * @return a map representing the contained predicate
+   */
+  private Map<String, Object> containedPredicateToMap() {
+
+    Map<String, Object> map = null;
+
+    if (delegate != null) {
+      org.apache.commons.collections.Predicate[] predicates = 
delegate.getPredicates();
+
+      if ((predicates != null) && (predicates.length > 0)) {
+        // Only process the 1st predicate.
+        org.apache.commons.collections.Predicate p = predicates[0];
+        if (p instanceof Predicate) {
+          map = ((Predicate) p).toMap();
+        } else {
+          throw new UnsupportedOperationException(String.format("Cannot 
convert a %s to a Map", p.getClass().getName()));
+        }
+      }
+    }
+
+    return map;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java
new file mode 100644
index 0000000..a1a8716
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java
@@ -0,0 +1,115 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.Predicate;
+import org.apache.commons.collections.functors.EqualPredicate;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * {@link EqualsPredicate} wraps {@link 
org.apache.commons.collections.functors.EqualPredicate} to
+ * provide additional functionality like serializing to and from a Map and 
JSON formatted data as well
+ * as obtaining data using a {@link ContextTransformer}
+ */
+public class EqualsPredicate extends OperationPredicate {
+
+  /**
+   * The name of this {@link Predicate} implementation
+   */
+  public static final String NAME = "equals";
+
+  /**
+   * The {@link org.apache.commons.collections.functors.EqualPredicate} to 
delegate operations to
+   */
+  private final org.apache.commons.collections.functors.EqualPredicate 
delegate;
+
+  /**
+   * Creates a new {@link EqualsPredicate} using the given {@link Map} of data.
+   * <p>
+   * It is expected that the map contains a single {@link java.util.Map.Entry} 
where the key name
+   * is "equals" and the value is a {@link Collection} of 2 {@link String}s. 
The first value
+   * should be the key to use when retrieving data from the context, which 
should resolve to a
+   * {@link String} (or null). The second value should be the value to use 
when checking for equality.
+   *
+   * @return a new {@link EqualsPredicate}
+   */
+  public static EqualsPredicate fromMap(Map<String, Object> map) {
+    Object data = (map == null) ? null : map.get(NAME);
+
+    if (data == null) {
+      throw new IllegalArgumentException("Missing data for '" + NAME + "' 
operation");
+    } else if (data instanceof Collection) {
+      Collection<?> collection = (Collection) data;
+      if (collection.size() == 2) {
+        Iterator<?> iterator = collection.iterator();
+        Object d1 = iterator.next();
+        Object d2 = iterator.next();
+
+        if ((d1 instanceof String) && (d2 instanceof String)) {
+          return new EqualsPredicate(new ContextTransformer((String) d1), 
(String) d2);
+        } else {
+          throw new IllegalArgumentException(String.format("Unexpected data 
types: %s and %s", d1.getClass().getName(), d2.getClass().getName()));
+        }
+      } else {
+        throw new IllegalArgumentException(String.format("Missing data for '" 
+ NAME + "' operation - 2 predicates are needed, %d found", collection.size()));
+      }
+    } else {
+      throw new IllegalArgumentException(String.format("Unexpected data type 
for '" + NAME + "' operation - %s", data.getClass().getName()));
+    }
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param transformer the {@link ContextTransformer}
+   * @param value       the value to test
+   */
+  public EqualsPredicate(ContextTransformer transformer, String value) {
+    super(NAME, transformer);
+    delegate = new EqualPredicate(value);
+  }
+
+  /**
+   * Gets the test value
+   *
+   * @return a string
+   */
+  public String getValue() {
+    Object o = (delegate == null) ? null : delegate.getValue();
+    return (o == null) ? null : o.toString();
+  }
+
+  @Override
+  public Map<String, Object> toMap() {
+    return Collections.<String, Object>singletonMap(NAME,
+        new ArrayList<String>(Arrays.asList(getContextKey(), 
delegate.getValue().toString())));
+  }
+
+  @Override
+  public boolean evaluateTransformedData(Object data) {
+    return delegate.evaluate(data);
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java
new file mode 100644
index 0000000..64dc592
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java
@@ -0,0 +1,80 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.PredicateUtils;
+import org.apache.commons.collections.Predicate;
+import org.apache.commons.collections.functors.PredicateDecorator;
+
+import java.util.Map;
+
+/**
+ * {@link NotPredicate} wraps {@link 
org.apache.commons.collections.functors.NotPredicate} to
+ * provide additional functionality like serializing to and from a Map and 
JSON formatted data.
+ * <p>
+ * See {@link DelegatedSinglePredicateContainer}
+ */
+public class NotPredicate extends DelegatedSinglePredicateContainer {
+
+  /**
+   * The name of this {@link org.apache.ambari.server.collections.Predicate} 
implementation
+   */
+  public static final String NAME = "not";
+
+  /**
+   * Creates a new {@link NotPredicate} using the given {@link Map} of data.
+   * <p>
+   * It is expected that the map contains a single {@link java.util.Map.Entry} 
where the key name
+   * is "not" and the value is a {@link Map} representing the contained 
predicate.
+   *
+   * @return a new {@link NotPredicate}
+   */
+  public static NotPredicate fromMap(Map<String, Object> map) {
+    Object data = (map == null) ? null : map.get(NAME);
+
+    if (data == null) {
+      throw new IllegalArgumentException("Missing data for '" + NAME + "' 
operation");
+    } else if (data instanceof Map) {
+      return new NotPredicate(PredicateUtils.fromMap((Map) data));
+    } else {
+      throw new IllegalArgumentException("Missing data for '" + NAME + "' 
operation");
+    }
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param predicate the predicate to negate
+   */
+  public NotPredicate(Predicate predicate) {
+    super(NAME,
+        (PredicateDecorator) 
org.apache.commons.collections.functors.NotPredicate.getInstance(predicate));
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() + this.getClass().getName().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return (obj == this) ||
+        ((obj != null) && (super.equals(obj) && 
this.getClass().isInstance(obj) && (hashCode() == obj.hashCode())));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java
new file mode 100644
index 0000000..c57f7c2
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java
@@ -0,0 +1,112 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.Predicate;
+import org.apache.commons.collections.Transformer;
+
+/**
+ * OperationPredicate is an abstract class providing functionality of 
transforming the input context
+ * before executing the implementation-specific
+ * {@link org.apache.commons.collections.Predicate#evaluate(Object)} method.
+ */
+abstract class OperationPredicate extends Predicate {
+  /**
+   * The {@link Transformer} to use to transform the data before evaluation
+   */
+  private ContextTransformer transformer = null;
+
+  /**
+   * Constructor
+   *
+   * @param name        the name of this {@link Predicate}
+   * @param transformer the {@link Transformer} to use to transform the data 
before evaluation
+   */
+  OperationPredicate(String name, ContextTransformer transformer) {
+    super(name);
+    this.transformer = transformer;
+  }
+
+  /**
+   * Gets the {@link Transformer}
+   *
+   * @return the assigned {@link ContextTransformer}
+   */
+  public ContextTransformer getTransformer() {
+    return transformer;
+  }
+
+  /**
+   * Sets the {@link Transformer}
+   *
+   * @param transformer a {@link ContextTransformer}
+   */
+  public void setTransformer(ContextTransformer transformer) {
+    this.transformer = transformer;
+  }
+
+
+  /**
+   * Gets the context key assigned to the {@link ContextTransformer}.
+   * <p>
+   * This key is used to identify which value from the context a passed to the
+   * {@link org.apache.commons.collections.Predicate#evaluate(Object)} method
+   *
+   * @return a key name
+   */
+  public String getContextKey() {
+    return (this.transformer == null) ? null : this.transformer.getKey();
+  }
+
+  @Override
+  public boolean evaluate(Object o) {
+    Object data = (transformer == null) ? o : transformer.transform(o);
+    return evaluateTransformedData(data);
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() + (37 * ((transformer == null) ? 0 : 
transformer.hashCode()));
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    } else if (obj == null) {
+      return false;
+    } else if (super.equals(obj) && (obj instanceof OperationPredicate) && 
(hashCode() == obj.hashCode())) {
+      OperationPredicate p = (OperationPredicate) obj;
+      return (transformer == null) ? (p.transformer == null) : 
transformer.equals(p.transformer);
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * Abstract method used internally to process the {@link #evaluate(Object)} 
logic after the
+   * input data is transformed.
+   *
+   * @param data the transformed data to use
+   * @return the result of the evaluation (<code>true</code>, 
<code>false</code>)
+   * @see org.apache.commons.collections.Predicate#evaluate(Object)
+   */
+  protected abstract boolean evaluateTransformedData(Object data);
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java
new file mode 100644
index 0000000..b207e40
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java
@@ -0,0 +1,96 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.PredicateUtils;
+import org.apache.commons.collections.Predicate;
+import org.apache.commons.collections.functors.PredicateDecorator;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * {@link OrPredicate} wraps {@link 
org.apache.commons.collections.functors.OrPredicate} to
+ * provide additional functionality like serializing to and from a Map and 
JSON formatted data.
+ * <p>
+ * See {@link DelegatedMultiplePredicateContainer}
+ */
+public class OrPredicate extends DelegatedMultiplePredicateContainer {
+  /**
+   * The name of this {@link org.apache.ambari.server.collections.Predicate} 
implementation
+   */
+  public static final String NAME = "or";
+
+  /**
+   * Creates a new {@link OrPredicate} using the given {@link Map} of data.
+   * <p>
+   * It is expected that the map contains a single {@link java.util.Map.Entry} 
where the key name
+   * is "or" and the value is a {@link Collection} of {@link Map}s 
representing the contained
+   * predicates.
+   *
+   * @return a new {@link OrPredicate}
+   */
+  public static OrPredicate fromMap(Map<String, Object> map) {
+    Object data = (map == null) ? null : map.get(NAME);
+
+    if (data == null) {
+      throw new IllegalArgumentException("Missing data for '" + NAME + "' 
operation");
+    } else if (data instanceof Collection) {
+      Collection<?> collection = (Collection) data;
+      if (collection.size() == 2) {
+        Iterator<?> iterator = collection.iterator();
+        Object d1 = iterator.next();
+        Object d2 = iterator.next();
+
+        if ((d1 instanceof Map) && (d2 instanceof Map)) {
+          return new OrPredicate(PredicateUtils.fromMap((Map) d1), 
PredicateUtils.fromMap((Map) d2));
+        } else {
+          throw new IllegalArgumentException(String.format("Unexpected data 
types for predicates: %s and %s", d1.getClass().getName(), 
d2.getClass().getName()));
+        }
+      } else {
+        throw new IllegalArgumentException(String.format("Missing data for '" 
+ NAME + "' operation - 2 predicates are needed, %d found", collection.size()));
+      }
+    } else {
+      throw new IllegalArgumentException(String.format("Unexpected data type 
for '" + NAME + "' operation - %s", data.getClass().getName()));
+    }
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param predicate1 the first predicate to process
+   * @param predicate2 the second predicate to process (if the first one 
yields <code>false</code>
+   */
+  public OrPredicate(Predicate predicate1, Predicate predicate2) {
+    super(NAME,
+        (PredicateDecorator) 
org.apache.commons.collections.functors.OrPredicate.getInstance(predicate1, 
predicate2));
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode() + this.getClass().getName().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return (obj == this) ||
+        ((obj != null) && (super.equals(obj) && 
this.getClass().isInstance(obj) && (hashCode() == obj.hashCode())));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java
new file mode 100644
index 0000000..f1f85c0
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.collections.functors;
+
+import org.apache.ambari.server.collections.Predicate;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@link PredicateClassFactory} is a factory class used to derive a {@link 
Predicate} implementation
+ * class from its name.
+ */
+public class PredicateClassFactory {
+  /**
+   * A static map of names to {@link Class}s.
+   */
+  private static final Map<String, Class<? extends Predicate>> NAME_TO_CLASS;
+
+  static {
+    Map<String, Class<? extends Predicate>> map = new HashMap<String, Class<? 
extends Predicate>>();
+
+    map.put(AndPredicate.NAME, AndPredicate.class);
+    map.put(OrPredicate.NAME, OrPredicate.class);
+    map.put(NotPredicate.NAME, NotPredicate.class);
+    map.put(ContainsPredicate.NAME, ContainsPredicate.class);
+    map.put(EqualsPredicate.NAME, EqualsPredicate.class);
+
+    NAME_TO_CLASS = Collections.unmodifiableMap(map);
+  }
+
+  /**
+   * Return a {@link Predicate} implementation class give its name
+   *
+   * @param name the name of a {@link Predicate} implementation
+   * @return a {@link Predicate} implementation class give its name or 
<code>null</code> is not found
+   */
+  public static Class<? extends Predicate> getPredicateClass(String name) {
+    return (name == null) ? null : NAME_TO_CLASS.get(name);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index c67c55d..1c46a93 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -353,6 +353,11 @@ public class KerberosHelperImpl implements KerberosHelper {
 
     Map<String, Set<String>> propertiesToIgnore = new HashMap<String, 
Set<String>>();
 
+    // Create the context to use for filtering Kerberos Identities based on 
the state of the cluster
+    Map<String, Object> filterContext = new HashMap<String, Object>();
+    filterContext.put("configurations", configurations);
+    filterContext.put("services", services);
+
     for (String serviceName : services) {
       // Set properties...
       KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(serviceName);
@@ -363,7 +368,7 @@ public class KerberosHelperImpl implements KerberosHelper {
           if (componentDescriptor != null) {
             Map<String, Map<String, String>> identityConfigurations;
 
-            identityConfigurations = 
getIdentityConfigurations(serviceDescriptor.getIdentities(true));
+            identityConfigurations = 
getIdentityConfigurations(serviceDescriptor.getIdentities(true, filterContext));
             if (identityConfigurations != null) {
               for (Map.Entry<String, Map<String, String>> entry : 
identityConfigurations.entrySet()) {
                 String configType = entry.getKey();
@@ -382,7 +387,7 @@ public class KerberosHelperImpl implements KerberosHelper {
               }
             }
 
-            identityConfigurations = 
getIdentityConfigurations(componentDescriptor.getIdentities(true));
+            identityConfigurations = 
getIdentityConfigurations(componentDescriptor.getIdentities(true, 
filterContext));
             if (identityConfigurations != null) {
               for (Map.Entry<String, Map<String, String>> entry : 
identityConfigurations.entrySet()) {
                 String configType = entry.getKey();
@@ -699,6 +704,11 @@ public class KerberosHelperImpl implements KerberosHelper {
         throw new AmbariException(message, e);
       }
 
+      // Create the context to use for filtering Kerberos Identities based on 
the state of the cluster
+      Map<String, Object> filterContext = new HashMap<String, Object>();
+      filterContext.put("configurations", configurations);
+      filterContext.put("services", services);
+
       for (String serviceName : services) {
         // Set properties...
         KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(serviceName);
@@ -710,7 +720,7 @@ public class KerberosHelperImpl implements KerberosHelper {
               List<KerberosIdentityDescriptor> identityDescriptors;
 
               // Handle the service-level Kerberos identities
-              identityDescriptors = serviceDescriptor.getIdentities(true);
+              identityDescriptors = serviceDescriptor.getIdentities(true, 
filterContext);
               if (identityDescriptors != null) {
                 for (KerberosIdentityDescriptor identityDescriptor : 
identityDescriptors) {
                   createUserIdentity(identityDescriptor, 
kerberosConfiguration, kerberosOperationHandler, configurations);
@@ -718,7 +728,7 @@ public class KerberosHelperImpl implements KerberosHelper {
               }
 
               // Handle the component-level Kerberos identities
-              identityDescriptors = componentDescriptor.getIdentities(true);
+              identityDescriptors = componentDescriptor.getIdentities(true, 
filterContext);
               if (identityDescriptors != null) {
                 for (KerberosIdentityDescriptor identityDescriptor : 
identityDescriptors) {
                   createUserIdentity(identityDescriptor, 
kerberosConfiguration, kerberosOperationHandler, configurations);
@@ -831,9 +841,14 @@ public class KerberosHelperImpl implements KerberosHelper {
       // Additional realms that need to be handled according to the Kerberos 
Descriptor
       String additionalRealms = 
kerberosDescriptor.getProperty("additional_realms");
 
+      // Create the context to use for filtering Kerberos Identities based on 
the state of the cluster
+      Map<String, Object> filterContext = new HashMap<String, Object>();
+      filterContext.put("configurations", existingConfigurations);
+      filterContext.put("services", cluster.getServices().keySet());
+
       // Determine which properties need to be set
       AuthToLocalBuilder authToLocalBuilder = new AuthToLocalBuilder(realm, 
additionalRealms, caseInsensitiveUser);
-      addIdentities(authToLocalBuilder, kerberosDescriptor.getIdentities(), 
null, existingConfigurations);
+      addIdentities(authToLocalBuilder, kerberosDescriptor.getIdentities(true, 
filterContext), null, existingConfigurations);
 
       authToLocalProperties = kerberosDescriptor.getAuthToLocalProperties();
       if (authToLocalProperties != null) {
@@ -847,7 +862,7 @@ public class KerberosHelperImpl implements KerberosHelper {
         for (KerberosServiceDescriptor service : services.values()) {
           if (installedServices.containsKey(service.getName())) {
             Service svc = installedServices.get(service.getName());
-            addIdentities(authToLocalBuilder, service.getIdentities(true), 
null, existingConfigurations);
+            addIdentities(authToLocalBuilder, service.getIdentities(true, 
filterContext), null, existingConfigurations);
 
             authToLocalProperties = service.getAuthToLocalProperties();
             if (authToLocalProperties != null) {
@@ -897,7 +912,7 @@ public class KerberosHelperImpl implements KerberosHelper {
 
                 if (addSvcCompIdentities) {
                   LOG.info("Adding identity for " + component.getName() + " to 
auth to local mapping");
-                  addIdentities(authToLocalBuilder, 
component.getIdentities(true), null, existingConfigurations);
+                  addIdentities(authToLocalBuilder, 
component.getIdentities(true, filterContext), null, existingConfigurations);
 
                   authToLocalProperties = component.getAuthToLocalProperties();
                   if (authToLocalProperties != null) {
@@ -905,8 +920,6 @@ public class KerberosHelperImpl implements KerberosHelper {
 
                   }
                 }
-
-
               }
             }
           }
@@ -1281,22 +1294,30 @@ public class KerberosHelperImpl implements 
KerberosHelper {
         if (kerberosDescriptor != null) {
           Map<String, String> kerberosDescriptorProperties = 
kerberosDescriptor.getProperties();
 
+          Set<String> existingServices = cluster.getServices().keySet();
+
           for (String hostname : hosts) {
+            // Calculate the current host-specific configurations. These will 
be used to replace
+            // variables within the Kerberos descriptor data
+            Map<String, Map<String, String>> configurations = 
calculateConfigurations(cluster,
+                hostname.equals(ambariServerHostname) ? null : hostname,
+                kerberosDescriptorProperties);
+
+            // Create the context to use for filtering Kerberos Identities 
based on the state of the cluster
+            Map<String, Object> filterContext = new HashMap<String, Object>();
+            filterContext.put("configurations", configurations);
+            filterContext.put("services", existingServices);
+
+
             Map<String, KerberosIdentityDescriptor> hostActiveIdentities = new 
HashMap<String, KerberosIdentityDescriptor>();
             List<KerberosIdentityDescriptor> identities = 
getActiveIdentities(cluster, hostname,
-                serviceName, componentName, kerberosDescriptor);
+                serviceName, componentName, kerberosDescriptor, filterContext);
 
             if (hostname.equals(ambariServerHostname)) {
               addAmbariServerIdentity(kerberosEnvConfig.getProperties(), 
kerberosDescriptor, identities);
             }
 
             if (!identities.isEmpty()) {
-              // Calculate the current host-specific configurations. These 
will be used to replace
-              // variables within the Kerberos descriptor data
-              Map<String, Map<String, String>> configurations = 
calculateConfigurations(cluster, hostname.equals
-                      (ambariServerHostname) ? null : hostname,
-                  kerberosDescriptorProperties);
-
               for (KerberosIdentityDescriptor identity : identities) {
                 KerberosPrincipalDescriptor principalDescriptor = 
identity.getPrincipalDescriptor();
                 String principal = null;
@@ -1352,7 +1373,8 @@ public class KerberosHelperImpl implements KerberosHelper 
{
                     hostActiveIdentities.put(uniqueKey, new 
KerberosIdentityDescriptor(
                         identity.getName(),
                         resolvedPrincipalDescriptor,
-                        resolvedKeytabDescriptor));
+                        resolvedKeytabDescriptor,
+                        identity.getWhen()));
                   }
                 }
               }
@@ -2305,13 +2327,15 @@ public class KerberosHelperImpl implements 
KerberosHelper {
    *                           components
    * @param kerberosDescriptor the relevant Kerberos Descriptor     @return a 
list of KerberosIdentityDescriptors representing the active identities for the
    * requested service component
+   * @param filterContext      the context to use for filtering identities 
based on the state of the cluster
    * @throws AmbariException if an error occurs processing the cluster's 
active identities
    */
   private List<KerberosIdentityDescriptor> getActiveIdentities(Cluster cluster,
                                                                String hostname,
                                                                String 
serviceName,
                                                                String 
componentName,
-                                                               
KerberosDescriptor kerberosDescriptor)
+                                                               
KerberosDescriptor kerberosDescriptor,
+                                                               Map<String, 
Object> filterContext)
       throws AmbariException {
 
     List<KerberosIdentityDescriptor> identities = new 
ArrayList<KerberosIdentityDescriptor>();
@@ -2329,14 +2353,14 @@ public class KerberosHelperImpl implements 
KerberosHelper {
           KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(schServiceName);
 
           if (serviceDescriptor != null) {
-            List<KerberosIdentityDescriptor> serviceIdentities = 
serviceDescriptor.getIdentities(true);
+            List<KerberosIdentityDescriptor> serviceIdentities = 
serviceDescriptor.getIdentities(true, filterContext);
             if (serviceIdentities != null) {
               identities.addAll(serviceIdentities);
             }
 
             KerberosComponentDescriptor componentDescriptor = 
serviceDescriptor.getComponent(schComponentName);
             if (componentDescriptor != null) {
-              List<KerberosIdentityDescriptor> componentIdentities = 
componentDescriptor.getIdentities(true);
+              List<KerberosIdentityDescriptor> componentIdentities = 
componentDescriptor.getIdentities(true, filterContext);
               if (componentIdentities != null) {
                 identities.addAll(componentIdentities);
               }

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
index 0dbd357..b6b0713 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
@@ -36,7 +36,6 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Type;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -89,6 +88,11 @@ public abstract class AbstractPrepareKerberosServerAction 
extends KerberosServer
       // variables within the Kerberos descriptor data
       Map<String, Map<String, String>> configurations = 
kerberosHelper.calculateConfigurations(cluster, null, 
kerberosDescriptorProperties);
 
+      // Create the context to use for filtering Kerberos Identities based on 
the state of the cluster
+      Map<String, Object> filterContext = new HashMap<String, Object>();
+      filterContext.put("configurations", configurations);
+      filterContext.put("services", cluster.getServices().keySet());
+
       actionLog.writeStdOut(String.format("Writing Kerberos identity data 
metadata file to %s", identityDataFile.getAbsolutePath()));
       try {
         kerberosIdentityDataFileWriter = 
kerberosIdentityDataFileWriterFactory.createKerberosIdentityDataFileWriter(identityDataFile);
@@ -118,7 +122,7 @@ public abstract class AbstractPrepareKerberosServerAction 
extends KerberosServer
             KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(serviceName);
 
             if (serviceDescriptor != null) {
-              List<KerberosIdentityDescriptor> serviceIdentities = 
serviceDescriptor.getIdentities(true);
+              List<KerberosIdentityDescriptor> serviceIdentities = 
serviceDescriptor.getIdentities(true, filterContext);
 
               // Add service-level principals (and keytabs)
               kerberosHelper.addIdentities(kerberosIdentityDataFileWriter, 
serviceIdentities,
@@ -128,7 +132,7 @@ public abstract class AbstractPrepareKerberosServerAction 
extends KerberosServer
               KerberosComponentDescriptor componentDescriptor = 
serviceDescriptor.getComponent(componentName);
 
               if (componentDescriptor != null) {
-                List<KerberosIdentityDescriptor> componentIdentities = 
componentDescriptor.getIdentities(true);
+                List<KerberosIdentityDescriptor> componentIdentities = 
componentDescriptor.getIdentities(true, filterContext);
 
                 // Calculate the set of configurations to update and replace 
any variables
                 // using the previously calculated Map of configurations for 
the host.

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
index bb2ed1c..64d9292 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java
@@ -162,14 +162,14 @@ public abstract class AbstractKerberosDescriptorContainer 
extends AbstractKerber
    * <p/>
    * The returned KerberosIdentityDescriptors are not merged with data from 
referenced
    * KerberosConfigurationDescriptors. This is the same calling
-   * {@link AbstractKerberosDescriptorContainer#getIdentities(boolean)} and 
setting the argument to
-   * 'false'
+   * {@link AbstractKerberosDescriptorContainer#getIdentities(boolean, Map)} 
and setting the
+   * argument to 'false'
    *
    * @return the relevant List of KerberosIdentityDescriptors
    */
   public List<KerberosIdentityDescriptor> getIdentities() {
     try {
-      return getIdentities(false);
+      return getIdentities(false, null);
     } catch (AmbariException e) {
       // AmbariException will not be thrown unless an error occurs while 
trying to dereference
       // identities.  This method does not attempt to dereference identities.
@@ -200,19 +200,20 @@ public abstract class AbstractKerberosDescriptorContainer 
extends AbstractKerber
    *                          (false)
    * @return a List of the requested KerberosIdentityDescriptors
    */
-  public List<KerberosIdentityDescriptor> getIdentities(boolean 
resolveReferences) throws AmbariException {
-    if (resolveReferences) {
-      if (identities == null) {
-        return Collections.emptyList();
-      } else {
-        List<KerberosIdentityDescriptor> list = new 
ArrayList<KerberosIdentityDescriptor>();
-
-        // For each KerberosIdentityDescriptor, copy it and then attempt to 
find the referenced
-        // KerberosIdentityDescriptor.
-        // * If a reference is found, copy that, update it with the initial 
KerberosIdentityDescriptor
-        //   and then add it to the list.
-        // * If a reference is not found, simply add the initial 
KerberosIdentityDescriptor to the list
-        for (KerberosIdentityDescriptor identity : identities) {
+  public List<KerberosIdentityDescriptor> getIdentities(boolean 
resolveReferences, Map<String,Object> contextForFilter) throws AmbariException {
+    if (identities == null) {
+      return Collections.emptyList();
+    } else {
+      List<KerberosIdentityDescriptor> list = new 
ArrayList<KerberosIdentityDescriptor>();
+
+      for (KerberosIdentityDescriptor identity : identities) {
+        KerberosIdentityDescriptor identityToAdd;
+
+        if (resolveReferences) {
+          // Copy this KerberosIdentityDescriptor and then attempt to find the 
referenced one.
+          // * If a reference is found, copy that, update it with the initial 
KerberosIdentityDescriptor
+          //   and then add it to the list.
+          // * If a reference is not found, simply add the initial 
KerberosIdentityDescriptor to the list
           KerberosIdentityDescriptor referencedIdentity;
           try {
             referencedIdentity = 
getReferencedIdentityDescriptor(identity.getName());
@@ -226,16 +227,22 @@ public abstract class AbstractKerberosDescriptorContainer 
extends AbstractKerber
           if (referencedIdentity != null) {
             KerberosIdentityDescriptor detachedIdentity = new 
KerberosIdentityDescriptor(referencedIdentity.toMap());
             detachedIdentity.update(identity);
-            list.add(detachedIdentity);
+
+            identityToAdd = detachedIdentity;
           } else {
-            list.add(identity);
+            identityToAdd = identity;
           }
+        } else {
+          identityToAdd = identity;
         }
 
-        return list;
+        // Make sure this Kerberos Identity is not to be filtered out based on 
its "when" clause
+        if ((identityToAdd != null) && ((contextForFilter == null) || 
identityToAdd.shouldInclude(contextForFilter))) {
+          list.add(identityToAdd);
+        }
       }
-    } else {
-      return identities;
+
+      return list;
     }
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
index d31dd21..2631d35 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
@@ -17,6 +17,9 @@
  */
 package org.apache.ambari.server.state.kerberos;
 
+import org.apache.ambari.server.collections.Predicate;
+import org.apache.ambari.server.collections.PredicateUtils;
+
 import java.util.Map;
 
 /**
@@ -86,16 +89,25 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
   private String password = null;
 
   /**
+   * An expression used to determine when this {@link 
KerberosIdentityDescriptor} is relevant for the
+   * cluster. If the process expression is not <code>null</code> and evaluates 
to <code>false</code>
+   * then this {@link KerberosIdentityDescriptor} will be ignored when 
processing identities.
+   */
+  private Predicate when = null;
+
+  /**
    * Creates a new KerberosIdentityDescriptor
    *
    * @param name the name of this identity descriptor
    * @param principal a KerberosPrincipalDescriptor
    * @param keytab a KerberosKeytabDescriptor
+   * @param when a predicate
    */
-  public KerberosIdentityDescriptor(String name, KerberosPrincipalDescriptor 
principal, KerberosKeytabDescriptor keytab) {
+  public KerberosIdentityDescriptor(String name, KerberosPrincipalDescriptor 
principal, KerberosKeytabDescriptor keytab, Predicate when) {
     setName(name);
     setPrincipalDescriptor(principal);
     setKeytabDescriptor(keytab);
+    setWhen(when);
   }
 
   /**
@@ -126,6 +138,11 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
       if (item instanceof Map) {
         setKeytabDescriptor(new KerberosKeytabDescriptor((Map<?, ?>) item));
       }
+
+      item = data.get("when");
+      if (item instanceof Map) {
+        setWhen(PredicateUtils.fromMap((Map<String, Object>) item));
+      }
     }
   }
 
@@ -193,6 +210,48 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
     this.password = password;
   }
 
+
+  /**
+   * Gets the expression (or {@link Predicate}) to use to determine when to 
include this Kerberos
+   * identity while processing Kerberos identities.
+   * <p>
+   * <code>null</code> indicates there is nothing to evaluate and this 
Kerberos identity is to always
+   * be included when processing Kerberos identities.
+   *
+   * @return a predicate
+   */
+  public Predicate getWhen() {
+    return when;
+  }
+
+  /**
+   * Sets the expression (or {@link Predicate}) to use to determine when to 
include this Kerberos
+   * identity while processing Kerberos identities.
+   * <p>
+   * <code>null</code> indicates there is nothing to evaluate and this 
Kerberos identity is to always
+   * be included when processing Kerberos identities.
+   *
+   * @param when a predicate
+   */
+  public void setWhen(Predicate when) {
+    this.when = when;
+  }
+
+  /**
+   * Processes the expression indicating when this {@link 
KerberosIdentityDescriptor} is to be included
+   * in the set of Kerberos identities to process.
+   * <p>
+   * <code>True</code> will be returned if the expression is <code>null</code> 
or if it evaluates
+   * as such.
+   *
+   * @param context A Map of context values, including at least the list of 
services and available configurations
+   * @return true if this {@link KerberosIdentityDescriptor} is to be included 
when processing the
+   * Kerberos identities; otherwise false.
+   */
+  public boolean shouldInclude(Map<String, Object> context) {
+    return (this.when == null) || this.when.evaluate(context);
+  }
+
   /**
    * Updates this KerberosIdentityDescriptor with data from another 
KerberosIdentityDescriptor
    * <p/>
@@ -219,6 +278,11 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
       } else {
         existingKeytabDescriptor.update(updates.getKeytabDescriptor());
       }
+
+      Predicate updatedWhen = updates.getWhen();
+      if(updatedWhen != null) {
+        setWhen(updatedWhen);
+      }
     }
   }
 
@@ -246,6 +310,10 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
       dataMap.put("password", password);
     }
 
+    if(when != null) {
+      dataMap.put("when", PredicateUtils.toMap(when));
+    }
+
     return dataMap;
   }
 
@@ -257,7 +325,10 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
             : getPrincipalDescriptor().hashCode()) +
         ((getKeytabDescriptor() == null)
             ? 0
-            : getKeytabDescriptor().hashCode());
+            : getKeytabDescriptor().hashCode()) +
+        ((getWhen() == null)
+            ? 0
+            : getWhen().hashCode());
   }
 
   @Override
@@ -283,6 +354,11 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
               (getPassword() == null)
                   ? (descriptor.getPassword() == null)
                   : getPassword().equals(descriptor.getPassword())
+          ) &&
+          (
+              (getWhen() == null)
+                  ? (descriptor.getWhen() == null)
+                  : getWhen().equals(descriptor.getWhen())
           );
     } else {
       return false;

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
index 9a58b8d..0d483fc 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java
@@ -2095,7 +2095,7 @@ public class UpgradeCatalog240 extends 
AbstractUpgradeCatalog {
                 componentDescriptor.removeIdentity("hbase_queryserver_hbase");
 
                 // Add the new identity
-                componentDescriptor.putIdentity(new 
KerberosIdentityDescriptor("/spnego", newPrincipalDescriptor, 
newKeytabDescriptor));
+                componentDescriptor.putIdentity(new 
KerberosIdentityDescriptor("/spnego", newPrincipalDescriptor, 
newKeytabDescriptor, null));
 
                 artifactEntity.setArtifactData(kerberosDescriptor.toMap());
                 artifactDAO.merge(artifactEntity);

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java
new file mode 100644
index 0000000..b97212d
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.collections;
+
+import junit.framework.Assert;
+import org.apache.ambari.server.collections.functors.AndPredicate;
+import org.apache.ambari.server.collections.functors.ContainsPredicate;
+import org.apache.ambari.server.collections.functors.ContextTransformer;
+import org.apache.ambari.server.collections.functors.EqualsPredicate;
+import org.apache.ambari.server.collections.functors.NotPredicate;
+import org.apache.ambari.server.collections.functors.OrPredicate;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+public class PredicateUtilsTest {
+  @Test
+  public void toMap() throws Exception {
+    // Test null predicate safely returns null
+    Assert.assertNull(PredicateUtils.toMap(null));
+
+    Assert.assertEquals(createMap(), PredicateUtils.toMap(createPredicate()));
+  }
+
+  @Test
+  public void fromMap() throws Exception {
+    verifyPredicate(PredicateUtils.fromMap(createMap()));
+  }
+
+  @Test
+  public void toJSON() throws Exception {
+    // Test null predicate safely returns null
+    Assert.assertNull(PredicateUtils.toJSON(null));
+
+    Assert.assertEquals(createJSON(), 
PredicateUtils.toJSON(createPredicate()));
+  }
+
+  @Test
+  public void fromJSON() throws Exception {
+    verifyPredicate(PredicateUtils.fromJSON(createJSON()));
+  }
+
+  private Predicate createPredicate() {
+    ContextTransformer transformer1 = new ContextTransformer("services");
+    ContextTransformer transformer2 = new 
ContextTransformer("configurations/service-env/property1");
+    ContextTransformer transformer3 = new 
ContextTransformer("configurations/cluster-env/property1");
+    ContainsPredicate predicate1 = new ContainsPredicate(transformer1, "HDFS");
+    EqualsPredicate predicate2 = new EqualsPredicate(transformer2, "true");
+    EqualsPredicate predicate3 = new EqualsPredicate(transformer3, "false");
+
+    AndPredicate andPredicate = new AndPredicate(predicate1, predicate2);
+    OrPredicate orPredicate = new OrPredicate(predicate3, andPredicate);
+
+    return new NotPredicate(orPredicate);
+  }
+
+  private Map<String, Object> createMap() {
+    Map<String, Object> andMap =
+        Collections.<String, Object>singletonMap(
+            AndPredicate.NAME, Arrays.asList(
+                Collections.<String, 
Object>singletonMap(ContainsPredicate.NAME, Arrays.asList("services", "HDFS")),
+                Collections.<String, Object>singletonMap(EqualsPredicate.NAME, 
Arrays.asList("configurations/service-env/property1", "true"))
+            )
+        );
+
+    Map<String, Object> orMap =
+        Collections.<String, Object>singletonMap(OrPredicate.NAME,
+            Arrays.asList(
+                Collections.<String, Object>singletonMap(EqualsPredicate.NAME, 
Arrays.asList("configurations/cluster-env/property1", "false")),
+                andMap
+            )
+        );
+
+    return Collections.<String, Object>singletonMap(NotPredicate.NAME, orMap);
+  }
+
+  private String createJSON() {
+    String andJSON = 
"{\"and\":[{\"contains\":[\"services\",\"HDFS\"]},{\"equals\":[\"configurations/service-env/property1\",\"true\"]}]}";
+    String orJSON = 
"{\"or\":[{\"equals\":[\"configurations/cluster-env/property1\",\"false\"]}," + 
andJSON + "]}";
+    return "{\"not\":" + orJSON + "}";
+  }
+
+  private void verifyPredicate(Predicate predicate) {
+    Assert.assertNotNull(predicate);
+    Assert.assertEquals(NotPredicate.NAME, predicate.getName());
+    Assert.assertTrue(predicate instanceof NotPredicate);
+
+    org.apache.commons.collections.Predicate[] predicates;
+
+    predicates = ((NotPredicate) predicate).getPredicates();
+    Assert.assertEquals(1, predicates.length);
+
+    Assert.assertNotNull(predicates[0]);
+    Assert.assertTrue(predicates[0] instanceof OrPredicate);
+    Assert.assertEquals(OrPredicate.NAME, ((OrPredicate) 
predicates[0]).getName());
+
+    predicates = ((OrPredicate) predicates[0]).getPredicates();
+    Assert.assertEquals(2, predicates.length);
+
+    Assert.assertNotNull(predicates[0]);
+    Assert.assertTrue(predicates[0] instanceof EqualsPredicate);
+    Assert.assertEquals(EqualsPredicate.NAME, ((EqualsPredicate) 
predicates[0]).getName());
+    Assert.assertEquals("configurations/cluster-env/property1", 
((EqualsPredicate) predicates[0]).getContextKey());
+    Assert.assertEquals("false", ((EqualsPredicate) predicates[0]).getValue());
+
+    Assert.assertNotNull(predicates[1]);
+    Assert.assertTrue(predicates[1] instanceof AndPredicate);
+    Assert.assertEquals(AndPredicate.NAME, ((AndPredicate) 
predicates[1]).getName());
+
+    predicates = ((AndPredicate) predicates[1]).getPredicates();
+    Assert.assertEquals(2, predicates.length);
+
+    Assert.assertNotNull(predicates[0]);
+    Assert.assertTrue(predicates[0] instanceof ContainsPredicate);
+    Assert.assertEquals(ContainsPredicate.NAME, ((ContainsPredicate) 
predicates[0]).getName());
+    Assert.assertEquals("services", ((ContainsPredicate) 
predicates[0]).getContextKey());
+    Assert.assertEquals("HDFS", ((ContainsPredicate) 
predicates[0]).getValue());
+
+    Assert.assertNotNull(predicates[1]);
+    Assert.assertTrue(predicates[1] instanceof EqualsPredicate);
+    Assert.assertEquals(EqualsPredicate.NAME, ((EqualsPredicate) 
predicates[1]).getName());
+    Assert.assertEquals("configurations/service-env/property1", 
((EqualsPredicate) predicates[1]).getContextKey());
+    Assert.assertEquals("true", ((EqualsPredicate) predicates[1]).getValue());
+  }
+}
\ No newline at end of file

Reply via email to