You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2016/05/28 12:02:11 UTC
[2/2] ambari git commit: AMBARI-16437. Add conditional constraints
for Kerberos identities to control when they are created (rlevas)
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 <rl...@hortonworks.com>
Authored: Sat May 28 08:02:00 2016 -0400
Committer: Robert Levas <rl...@hortonworks.com>
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