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/198f5880
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/198f5880
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/198f5880

Branch: refs/heads/branch-2.4
Commit: 198f588080b002956d5235343a1aeaa9f6ad3a94
Parents: 384a4be
Author: Robert Levas <[email protected]>
Authored: Sat May 28 08:02:00 2016 -0400
Committer: Jonathan Hurley <[email protected]>
Committed: Sat May 28 14:59:04 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/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 ++++++++++++++
 19 files changed, 1956 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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/198f5880/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

http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.java
new file mode 100644
index 0000000..977fe9d
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.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.collections.functors;
+
+import org.apache.ambari.server.collections.Predicate;
+import org.easymock.EasyMockSupport;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.expect;
+
+public class AndPredicateTest extends EasyMockSupport {
+
+  @Test
+  public void testEvaluate() {
+    Predicate mockPredicate1 = createStrictMock(Predicate.class);
+    expect(mockPredicate1.evaluate("context")).andReturn(true).times(1);
+    expect(mockPredicate1.evaluate("context")).andReturn(false).times(1);
+    expect(mockPredicate1.evaluate("context")).andReturn(true).times(1);
+
+    Predicate mockPredicate2 = createStrictMock(Predicate.class);
+    expect(mockPredicate2.evaluate("context")).andReturn(true).times(1);
+    expect(mockPredicate2.evaluate("context")).andReturn(true).times(1);
+
+    replayAll();
+
+    AndPredicate predicate = new AndPredicate(mockPredicate1, mockPredicate2);
+
+    // Try with true and true
+    predicate.evaluate("context");
+
+    // Try with false and ???? (short circuited)
+    predicate.evaluate("context");
+
+    // Try with true and false
+    predicate.evaluate("context");
+
+    verifyAll();
+
+    Assert.assertArrayEquals(new Predicate[]{mockPredicate1, mockPredicate2}, 
predicate.getPredicates());
+  }
+
+  @Test
+  public void testToMap() {
+    Predicate mockPredicate1 = createStrictMock(Predicate.class);
+    expect(mockPredicate1.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "foo")).times(1);
+
+    Predicate mockPredicate2 = createStrictMock(Predicate.class);
+    expect(mockPredicate2.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "baz")).times(1);
+
+    replayAll();
+
+    AndPredicate predicate = new AndPredicate(mockPredicate1, mockPredicate2);
+    Map<String, Object> actualMap = predicate.toMap();
+
+    verifyAll();
+
+    Map<String, Object> expectedMap = new HashMap<String, Object>();
+    expectedMap.put("and", new ArrayList<Map<String, Object>>(
+        Arrays.asList(Collections.<String, Object>singletonMap("nop", "foo"),
+            Collections.<String, Object>singletonMap("nop", "baz"))
+    ));
+
+    Assert.assertEquals(expectedMap, actualMap);
+  }
+
+  @Test
+  public void testToJSON() {
+    Predicate mockPredicate1 = createStrictMock(Predicate.class);
+    expect(mockPredicate1.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "foo")).times(1);
+
+    Predicate mockPredicate2 = createStrictMock(Predicate.class);
+    expect(mockPredicate2.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "baz")).times(1);
+
+    replayAll();
+
+    AndPredicate predicate = new AndPredicate(mockPredicate1, mockPredicate2);
+    String actualJSON = predicate.toJSON();
+
+    verifyAll();
+
+    String expectedJSON = "{\"and\":[{\"nop\":\"foo\"},{\"nop\":\"baz\"}]}";
+
+    Assert.assertEquals(expectedJSON, actualJSON);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java
new file mode 100644
index 0000000..d27510f
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.easymock.EasyMockSupport;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expect;
+
+public class ContainsPredicateTest extends EasyMockSupport {
+
+  @Test
+  public void testEvaluate() {
+    Set<String> data1 = new HashSet<String>(Arrays.asList("ONE", "TWO", 
"THREE"));
+    Set<String> data2 = new HashSet<String>(Arrays.asList("TWO", "THREE"));
+
+    ContextTransformer transformer = 
createStrictMock(ContextTransformer.class);
+    
expect(transformer.transform(anyObject(Map.class))).andReturn(data1).times(1);
+    
expect(transformer.transform(anyObject(Map.class))).andReturn(data2).times(1);
+
+    replayAll();
+
+    ContainsPredicate predicate = new ContainsPredicate(transformer, "ONE");
+
+    Assert.assertTrue(predicate.evaluate(Collections.singletonMap("data", 
data1)));
+    Assert.assertFalse(predicate.evaluate(Collections.singletonMap("data", 
data2)));
+
+    verifyAll();
+  }
+
+  @Test
+  public void testToMap() {
+    ContextTransformer transformer = 
createStrictMock(ContextTransformer.class);
+    expect(transformer.getKey()).andReturn("data").times(1);
+
+    replayAll();
+
+    ContainsPredicate predicate = new ContainsPredicate(transformer, "ONE");
+    Map<String, Object> actualMap = predicate.toMap();
+
+    verifyAll();
+
+    Map<String, Object> expectedMap = new HashMap<String, Object>();
+    expectedMap.put("contains", new ArrayList<String>(Arrays.asList("data", 
"ONE")));
+
+    Assert.assertEquals(expectedMap, actualMap);
+  }
+
+  @Test
+  public void testToJSON() {
+    ContextTransformer transformer = 
createStrictMock(ContextTransformer.class);
+    expect(transformer.getKey()).andReturn("data").times(1);
+
+    replayAll();
+
+    ContainsPredicate predicate = new ContainsPredicate(transformer, "ONE");
+    String actualJSON = predicate.toJSON();
+
+    verifyAll();
+
+    String expectedJSON = "{\"contains\":[\"data\",\"ONE\"]}";
+
+    Assert.assertEquals(expectedJSON, actualJSON);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java
new file mode 100644
index 0000000..da77b35
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 junit.framework.Assert;
+import org.easymock.EasyMockSupport;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ContextTransformerTest extends EasyMockSupport {
+
+  @Test
+  public void testGetKey() {
+    ContextTransformer transformer = new ContextTransformer("key");
+    Assert.assertEquals("key", transformer.getKey());
+  }
+
+  @Test
+  public void testTransformSimple() {
+    Map<String, Object> context = new HashMap<String, Object>();
+    context.put("key", "value");
+    context.put("key1", "value1");
+    context.put("key2", "value2");
+
+    ContextTransformer transformer = new ContextTransformer("key");
+    Assert.assertEquals("value", transformer.transform(context));
+  }
+
+  @Test
+  public void testTransformTree() {
+    Map<String, Object> serviceSite = new HashMap<String, Object>();
+    serviceSite.put("property", "service-site-property");
+
+    Map<String, Object> configurations = new HashMap<String, Object>();
+    configurations.put("service-site", serviceSite);
+    configurations.put("property", "configuration-property");
+
+    Map<String, Object> context = new HashMap<String, Object>();
+    context.put("configurations", configurations);
+    context.put("property", "context-property");
+
+    ContextTransformer transformer;
+
+    // Without leading "/"
+    transformer = new 
ContextTransformer("configurations/service-site/property");
+    Assert.assertEquals("service-site-property", 
transformer.transform(context));
+
+    // With leading "/"
+    transformer = new 
ContextTransformer("/configurations/service-site/property");
+    Assert.assertEquals("service-site-property", 
transformer.transform(context));
+
+    // Get map of properties
+    transformer = new ContextTransformer("/configurations/service-site");
+    Assert.assertEquals(serviceSite, transformer.transform(context));
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java
new file mode 100644
index 0000000..0ea4454
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.easymock.EasyMockSupport;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expect;
+
+public class EqualsPredicateTest extends EasyMockSupport {
+
+  @Test
+  public void testEvaluate() {
+    String data1 = "value1";
+    String data2 = "value2";
+
+    ContextTransformer transformer = 
createStrictMock(ContextTransformer.class);
+    
expect(transformer.transform(anyObject(Map.class))).andReturn(data1).times(1);
+    
expect(transformer.transform(anyObject(Map.class))).andReturn(data2).times(1);
+
+    replayAll();
+
+    EqualsPredicate predicate = new EqualsPredicate(transformer, "value1");
+
+    Assert.assertTrue(predicate.evaluate(Collections.singletonMap("data", 
data1)));
+    Assert.assertFalse(predicate.evaluate(Collections.singletonMap("data", 
data2)));
+
+    verifyAll();
+  }
+
+  @Test
+  public void testToMap() {
+    ContextTransformer transformer = 
createStrictMock(ContextTransformer.class);
+    expect(transformer.getKey()).andReturn("data").times(1);
+
+    replayAll();
+
+    EqualsPredicate predicate = new EqualsPredicate(transformer, "value");
+    Map<String, Object> actualMap = predicate.toMap();
+
+    verifyAll();
+
+    Map<String, Object> expectedMap = new HashMap<String, Object>();
+    expectedMap.put("equals", new ArrayList<String>(Arrays.asList("data", 
"value")));
+
+    Assert.assertEquals(expectedMap, actualMap);
+  }
+
+  @Test
+  public void testToJSON() {
+    ContextTransformer transformer = 
createStrictMock(ContextTransformer.class);
+    expect(transformer.getKey()).andReturn("data").times(1);
+
+    replayAll();
+
+    EqualsPredicate predicate = new EqualsPredicate(transformer, "value");
+    String actualJSON = predicate.toJSON();
+
+    verifyAll();
+
+    String expectedJSON = "{\"equals\":[\"data\",\"value\"]}";
+
+    Assert.assertEquals(expectedJSON, actualJSON);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java
new file mode 100644
index 0000000..f0b8ae3
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.easymock.EasyMockSupport;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.expect;
+
+public class NotPredicateTest extends EasyMockSupport {
+
+  @Test
+  public void testEvaluate() {
+    Predicate mockPredicate = createStrictMock(Predicate.class);
+    expect(mockPredicate.evaluate("context")).andReturn(true).times(1);
+    expect(mockPredicate.evaluate("context")).andReturn(false).times(1);
+
+    replayAll();
+
+    NotPredicate predicate = new NotPredicate(mockPredicate);
+
+    // Try with true
+    Assert.assertFalse(predicate.evaluate("context"));
+
+    // Try with false
+    Assert.assertTrue(predicate.evaluate("context"));
+
+    verifyAll();
+
+    Assert.assertArrayEquals(new Predicate[]{mockPredicate}, 
predicate.getPredicates());
+  }
+
+  @Test
+  public void testToMap() {
+    Predicate mockPredicate = createStrictMock(Predicate.class);
+    expect(mockPredicate.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "foo")).times(1);
+
+    replayAll();
+
+    NotPredicate predicate = new NotPredicate(mockPredicate);
+    Map<String, Object> actualMap = predicate.toMap();
+
+    verifyAll();
+
+    Map<String, Object> expectedMap = new HashMap<String, Object>();
+    expectedMap.put("not", Collections.<String, Object>singletonMap("nop", 
"foo"));
+
+    Assert.assertEquals(expectedMap, actualMap);
+  }
+
+  @Test
+  public void testToJSON() {
+    Predicate mockPredicate = createStrictMock(Predicate.class);
+    expect(mockPredicate.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "foo")).times(1);
+
+    replayAll();
+
+    NotPredicate predicate = new NotPredicate(mockPredicate);
+    String actualJSON = predicate.toJSON();
+
+    verifyAll();
+
+    String expectedJSON = "{\"not\":{\"nop\":\"foo\"}}";
+
+    Assert.assertEquals(expectedJSON, actualJSON);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java
new file mode 100644
index 0000000..7e0bf1b
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.easymock.EasyMockSupport;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.expect;
+
+public class OrPredicateTest extends EasyMockSupport {
+
+  @Test
+  public void testEvaluate() {
+    Predicate mockPredicate1 = createStrictMock(Predicate.class);
+    expect(mockPredicate1.evaluate("context")).andReturn(true).times(1);
+    expect(mockPredicate1.evaluate("context")).andReturn(false).times(1);
+    expect(mockPredicate1.evaluate("context")).andReturn(true).times(1);
+
+    Predicate mockPredicate2 = createStrictMock(Predicate.class);
+    expect(mockPredicate2.evaluate("context")).andReturn(true).times(1);
+
+    replayAll();
+
+    OrPredicate predicate = new OrPredicate(mockPredicate1, mockPredicate2);
+
+    // Try with true and ???? (short circuited)
+    predicate.evaluate("context");
+
+    // Try with false and true
+    predicate.evaluate("context");
+
+    // Try with true and ???? (short circuited)
+    predicate.evaluate("context");
+
+    verifyAll();
+
+    Assert.assertArrayEquals(new Predicate[]{mockPredicate1, mockPredicate2}, 
predicate.getPredicates());
+  }
+
+  @Test
+  public void testToMap() {
+    Predicate mockPredicate1 = createStrictMock(Predicate.class);
+    expect(mockPredicate1.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "foo")).times(1);
+
+    Predicate mockPredicate2 = createStrictMock(Predicate.class);
+    expect(mockPredicate2.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "baz")).times(1);
+
+    replayAll();
+
+    OrPredicate predicate = new OrPredicate(mockPredicate1, mockPredicate2);
+    Map<String, Object> actualMap = predicate.toMap();
+
+    verifyAll();
+
+    Map<String, Object> expectedMap = new HashMap<String, Object>();
+    expectedMap.put("or", new ArrayList<Map<String, Object>>(
+        Arrays.asList(Collections.<String, Object>singletonMap("nop", "foo"),
+            Collections.<String, Object>singletonMap("nop", "baz"))
+    ));
+
+    Assert.assertEquals(expectedMap, actualMap);
+  }
+
+  @Test
+  public void testToJSON() {
+    Predicate mockPredicate1 = createStrictMock(Predicate.class);
+    expect(mockPredicate1.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "foo")).times(1);
+
+    Predicate mockPredicate2 = createStrictMock(Predicate.class);
+    expect(mockPredicate2.toMap()).andReturn(Collections.<String, 
Object>singletonMap("nop", "baz")).times(1);
+
+    replayAll();
+
+    OrPredicate predicate = new OrPredicate(mockPredicate1, mockPredicate2);
+    String actualJSON = predicate.toJSON();
+
+    verifyAll();
+
+    String expectedJSON = "{\"or\":[{\"nop\":\"foo\"},{\"nop\":\"baz\"}]}";
+
+    Assert.assertEquals(expectedJSON, actualJSON);
+  }
+}
\ No newline at end of file

Reply via email to