AMBARI-16437. Add conditional constraints for Kerberos identities to control when they are created (rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/2fcc9475 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/2fcc9475 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/2fcc9475 Branch: refs/heads/trunk Commit: 2fcc947530eb521bbcb3bdc941a5a680a4145140 Parents: 069cc49 Author: Robert Levas <[email protected]> Authored: Sat May 28 08:02:00 2016 -0400 Committer: Robert Levas <[email protected]> Committed: Sat May 28 08:02:05 2016 -0400 ---------------------------------------------------------------------- .../ambari/server/collections/Predicate.java | 82 +++++++++++ .../server/collections/PredicateUtils.java | 111 ++++++++++++++ .../collections/functors/AndPredicate.java | 96 +++++++++++++ .../collections/functors/ContainsPredicate.java | 138 ++++++++++++++++++ .../functors/ContextTransformer.java | 142 ++++++++++++++++++ .../DelegatedMultiplePredicateContainer.java | 114 +++++++++++++++ .../DelegatedSinglePredicateContainer.java | 110 ++++++++++++++ .../collections/functors/EqualsPredicate.java | 115 +++++++++++++++ .../collections/functors/NotPredicate.java | 80 +++++++++++ .../functors/OperationPredicate.java | 112 +++++++++++++++ .../collections/functors/OrPredicate.java | 96 +++++++++++++ .../functors/PredicateClassFactory.java | 58 ++++++++ .../server/controller/KerberosHelperImpl.java | 64 ++++++--- .../AbstractPrepareKerberosServerAction.java | 10 +- .../AbstractKerberosDescriptorContainer.java | 49 ++++--- .../kerberos/KerberosIdentityDescriptor.java | 80 ++++++++++- .../server/upgrade/UpgradeCatalog240.java | 2 +- .../server/collections/PredicateUtilsTest.java | 143 +++++++++++++++++++ .../collections/functors/AndPredicateTest.java | 108 ++++++++++++++ .../functors/ContainsPredicateTest.java | 91 ++++++++++++ .../functors/ContextTransformerTest.java | 75 ++++++++++ .../functors/EqualsPredicateTest.java | 89 ++++++++++++ .../collections/functors/NotPredicateTest.java | 89 ++++++++++++ .../collections/functors/OrPredicateTest.java | 107 ++++++++++++++ .../server/controller/KerberosHelperTest.java | 32 +++-- .../state/kerberos/KerberosDescriptorTest.java | 4 +- .../KerberosIdentityDescriptorTest.java | 17 +++ 27 files changed, 2152 insertions(+), 62 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java new file mode 100644 index 0000000..09bc7d5 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections; + +import com.google.gson.Gson; + +import java.util.Map; + +/** + * {@link Predicate} wraps {@link org.apache.commons.collections.Predicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + */ +public abstract class Predicate implements org.apache.commons.collections.Predicate { + + /** + * The name of this predicate. For example "and", "or", etc... + */ + private final String name; + + protected Predicate(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + /** + * Serialize this {@link Predicate} to a {@link Map} + * + * @return a {@link Map} + */ + public abstract Map<String, Object> toMap(); + + /** + * Serialize this {@link Predicate} to a JSON-formatted {@link String} + * + * @return a JSON-formatted {@link String} + */ + public String toJSON() { + Map<String, Object> map = toMap(); + return (map == null) ? null : new Gson().toJson(map); + } + + + @Override + public int hashCode() { + return 37 * ((name == null) ? 0 : name.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if ((obj instanceof Predicate) && (hashCode() == obj.hashCode())) { + Predicate p = (Predicate) obj; + return (name == null) ? (p.name == null) : name.equals(p.name); + } else { + return false; + } + } + +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java new file mode 100644 index 0000000..2a75d81 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import org.apache.ambari.server.collections.functors.PredicateClassFactory; +import org.apache.commons.lang.StringUtils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Map; + +/** + * PredicateUtils is a utility class providing methods to help perform tasks on {@link Predicate}s. + */ +public class PredicateUtils { + private static final Type PARSED_TYPE = new TypeToken<Map<String, Object>>() { + }.getType(); + + /** + * Safely serializes the specified {@link Predicate} to a {@link Map} + * + * @param predicate the {@link Predicate} to process + * @return a {@link Map} or null of the supplied predicate is null + */ + public static Map<String, Object> toMap(Predicate predicate) { + return (predicate == null) ? null : predicate.toMap(); + } + + /** + * Builds a {@link Predicate} from a {@link Map} + * + * @param map a map containing the details of the predicate to create. + * @return a {@link Predicate} + */ + public static Predicate fromMap(Map<String, Object> map) { + Predicate predicate = null; + + if ((map != null) && !map.isEmpty()) { + if (map.size() == 1) { + Map.Entry<String, Object> entry = map.entrySet().iterator().next(); + String name = entry.getKey(); + + Class<? extends Predicate> predicateClass = PredicateClassFactory.getPredicateClass(name); + + if (predicateClass == null) { + throw new IllegalArgumentException(String.format("Unexpected predicate name - %s", name)); + } else { + try { + // Dynamically locate and invoke the static toMap method for the named Predicate + // implementation using reflection + Method method = predicateClass.getMethod("fromMap", Map.class); + if (method == null) { + throw new UnsupportedOperationException(String.format("Cannot translate data to a %s - %s", predicateClass.getName(), "Failed to find toMap method")); + } else { + predicate = (Predicate) method.invoke(null, Collections.singletonMap(name, entry.getValue())); + } + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new UnsupportedOperationException(String.format("Cannot translate data to a %s - %s", predicateClass.getName(), e.getLocalizedMessage()), e); + } + } + } else { + throw new IllegalArgumentException(String.format("Too many map entries have been encountered - %d", map.size())); + } + } + + return predicate; + } + + + /** + * Safely serializes the specified {@link Predicate} to a JSON-formatted {@link String} + * + * @param predicate the {@link Predicate} to process + * @return a {@link String} or null of the supplied predicate is null + */ + public static String toJSON(Predicate predicate) { + return (predicate == null) ? null : predicate.toJSON(); + } + + /** + * Builds a {@link Predicate} from a JSON-formatted {@link String} + * + * @param json a string containing the details of the predicate to create. + * @return a {@link Predicate} + * @see #fromMap(Map) + */ + public static Predicate fromJSON(String json) { + Map<String, Object> map = new Gson().fromJson(json, PARSED_TYPE); + return (StringUtils.isEmpty(json) ? null : fromMap(map)); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java new file mode 100644 index 0000000..62a953a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.PredicateUtils; +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * {@link AndPredicate} wraps {@link org.apache.commons.collections.functors.AndPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + * <p> + * See {@link DelegatedMultiplePredicateContainer} + */ +public class AndPredicate extends DelegatedMultiplePredicateContainer { + /** + * The name of this {@link org.apache.ambari.server.collections.Predicate} implementation + */ + public static final String NAME = "and"; + + /** + * Creates a new {@link AndPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "and" and the value is a {@link Collection} of {@link Map}s representing the contained + * predicates. + * + * @return a new {@link AndPredicate} + */ + public static AndPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof Map) && (d2 instanceof Map)) { + return new AndPredicate(PredicateUtils.fromMap((Map) d1), PredicateUtils.fromMap((Map) d2)); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types for predicates: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor. + * + * @param predicate1 the first predicate to process + * @param predicate2 the second predicate to process (if the first one yields <code>true</code> + */ + public AndPredicate(Predicate predicate1, Predicate predicate2) { + super(NAME, + (PredicateDecorator) org.apache.commons.collections.functors.AndPredicate.getInstance(predicate1, predicate2)); + } + + @Override + public int hashCode() { + return super.hashCode() + this.getClass().getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || + ((obj != null) && (super.equals(obj) && this.getClass().isInstance(obj) && (hashCode() == obj.hashCode()))); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java new file mode 100644 index 0000000..503405e --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * {@link ContainsPredicate} is a predicate implementation testing whether a string value exists + * within some set of strings. + * <p> + * It it expected that the data supplied to the {@link #evaluate(Object)} method is as {@link Set} + * of {@link String}s retrieved from a context (a {@link Map} of keys to values, where each value + * can be an explicit value or a {@link Map} of values creating a tree structure. + */ +public class ContainsPredicate extends OperationPredicate { + + /** + * The name of this {@link Predicate} implementation + */ + public static final String NAME = "contains"; + + /** + * The value to test for + */ + private final String value; + + /** + * Creates a new {@link ContainsPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "contains" and the value is a {@link Collection} of 2 {@link String}s. The first value + * should be the key to use when retrieving data from the context, which should resolve to a + * {@link Set} of {@link String}s (or null). The second value should be the value to check for + * existing in the relevant set. + * + * @return a new {@link ContainsPredicate} + */ + public static ContainsPredicate fromMap(Map<String, Object> map) { + + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof String) && (d2 instanceof String)) { + return new ContainsPredicate(new ContextTransformer((String) d1), (String) d2); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor + * + * @param transformer the {@link ContextTransformer} configured with the context key to find + * @param value the value to test + */ + public ContainsPredicate(ContextTransformer transformer, String value) { + super(NAME, transformer); + this.value = value; + } + + /** + * Gets the test value + * + * @return a string + */ + public String getValue() { + return value; + } + + @Override + protected boolean evaluateTransformedData(Object data) { + return (this.value != null) && (data instanceof Set) && ((Set<?>) data).contains(this.value); + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(NAME, + new ArrayList<String>(Arrays.asList(getContextKey(), value))); + } + + @Override + public int hashCode() { + return super.hashCode() + + (37 * ((this.value == null) ? 0 : this.value.hashCode())); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof ContainsPredicate) && (hashCode() == obj.hashCode())) { + ContainsPredicate p = (ContainsPredicate) obj; + return (value == null) ? (p.value == null) : value.equals(p.value); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java new file mode 100644 index 0000000..b20963a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.commons.collections.Transformer; + +import java.util.Map; + +/** + * {@link ContextTransformer} is a {@link Transformer} implementation that traverses a {@link Map} + * of {@link Map}s to find the value related to the specified key. + * <p> + * This implementation first checks the context map for the explicit key. If the key exists, it + * returns the value associated with it. If the key does not exist and appears to represent a path + * via "/"-delimited keys (configurations/service-site/property_name), the path is traversed + * recursively looking for the requested data. + * <p> + * Example context: + * <pre> + * |- services : [set of services] + * | + * |- configurations + * | |- service-site + * | | |- property : [value] + * | + * |- key/looks/like/path : [value2] + * </pre> + * <ul> + * <li>If the key was <code>services</code>, <code>[set of services]</code> would be returned</li> + * <li>If the key was <code>configurations/service-site/property</code>, <code>value</code> would be returned</li> + * <li>If the key was <code>configurations/service-site</code>, <code>[map of service-site properties]</code> would be returned</li> + * <li>If the key was <code>key/looks/like/path</code>, <code>value2</code> would be returned</li> + * </ul> + */ +public class ContextTransformer implements Transformer { + + /** + * The key to search for + */ + private final String key; + + /** + * Constructor. + * + * @param key the key to search for + */ + public ContextTransformer(String key) { + this.key = key; + } + + /** + * Returns the key this transformer is using to search for data + * + * @return a string + */ + public String getKey() { + return key; + } + + @Override + public Object transform(Object o) { + return transform(key, o); + } + + /** + * Traverses the input object (expected to be a {@link Map}) to find the data associated with + * the specified key. + * <p> + * Note: This method is recursive in the even the key represents a path + * + * @param key the key to search for + * @param o the object containing data to process + * @return the found data or null + */ + private Object transform(String key, Object o) { + Object transformedData = null; + + if (key != null) { + if (o instanceof Map) { + Map<?, ?> data = (Map) o; + + if (data.containsKey(key)) { + transformedData = data.get(key); + } else { + // See if the key implies a tree that needs to be traversed... + // For example: configurations/service-conf/property_name + // A map of maps is expected such that the top level map has a map identified by the key + // of "configuration". The "configuration" map has a map identified by the key of + // "service-conf". The "service-conf" has a value identified by the key of "property_name". + String[] parts = key.split("\\/", 2); + + // If only a single item is returned, than the key does not indicate a tree. + if (parts.length == 2) { + // If the first item is empty, a leading "/" was encountered... retry with the pruned key + if (parts[0].isEmpty()) { + transformedData = transform(parts[1], o); + } else { + transformedData = transform(parts[1], data.get(parts[0])); + } + } + } + } + } + + return transformedData; + } + + @Override + public int hashCode() { + return (37 * ((key == null) ? 0 : key.hashCode())); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if ((obj instanceof ContextTransformer) && (hashCode() == obj.hashCode())) { + ContextTransformer t = (ContextTransformer) obj; + return (key == null) ? (t.key == null) : key.equals(t.key); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java new file mode 100644 index 0000000..b9add7a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * DelegatedMultiplePredicateContainer is an abstract class providing functionality related to + * managing a delegate used to hold multiple {@link Predicate}s. For example <code>and</code> and + * <code>or</code> operators. + */ +abstract class DelegatedMultiplePredicateContainer extends Predicate implements PredicateDecorator { + + /** + * The delegate {@link PredicateDecorator} used to handle the internal logic for the container + */ + private final PredicateDecorator delegate; + + /** + * Constructor. + * + * @param name the name of this predicate + * @param delegate the delegate used to handle the internal logic for the container + */ + DelegatedMultiplePredicateContainer(String name, PredicateDecorator delegate) { + super(name); + this.delegate = delegate; + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(getName(), containedPredicatesToMaps()); + } + + @Override + public boolean evaluate(Object o) { + return delegate.evaluate(o); + } + + @Override + public org.apache.commons.collections.Predicate[] getPredicates() { + return delegate.getPredicates(); + } + + @Override + public int hashCode() { + return super.hashCode() + ((delegate == null) ? 0 : delegate.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof DelegatedMultiplePredicateContainer) && (hashCode() == obj.hashCode())) { + DelegatedMultiplePredicateContainer p = (DelegatedMultiplePredicateContainer) obj; + return (delegate == null) ? (p.delegate == null) : delegate.equals(p.delegate); + } else { + return false; + } + } + + /** + * Processes the contained predicates into a {@link List} of {@link Map}s. + * <p> + * This is used to serialize this {@link DelegatedMultiplePredicateContainer} into a {@link Map} + * + * @return a list of maps representing the contained predicates + */ + private List<Map<String, Object>> containedPredicatesToMaps() { + + List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); + + if (delegate != null) { + org.apache.commons.collections.Predicate[] predicates = delegate.getPredicates(); + + if (predicates != null) { + for (org.apache.commons.collections.Predicate p : predicates) { + if (p instanceof Predicate) { + list.add(((Predicate) p).toMap()); + } else { + throw new UnsupportedOperationException(String.format("Cannot convert a %s to a Map", p.getClass().getName())); + } + } + } + } + + return list; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java new file mode 100644 index 0000000..0e800ea --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Collections; +import java.util.Map; + +/** + * DelegatedSinglePredicateContainer is an abstract class providing functionality to managing a + * delegate used to hold a since {@link Predicate}s. For example <code>not</code>. + */ +abstract class DelegatedSinglePredicateContainer extends Predicate implements PredicateDecorator { + + /** + * The delegate {@link PredicateDecorator} used to handle the internal logic for the container + */ + private final PredicateDecorator delegate; + + /** + * Constructor. + * + * @param name the name of this predicate + * @param delegate the delegate used to handle the internal logic for the container + */ + DelegatedSinglePredicateContainer(String name, PredicateDecorator delegate) { + super(name); + this.delegate = delegate; + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(getName(), containedPredicateToMap()); + } + + @Override + public boolean evaluate(Object o) { + return delegate.evaluate(o); + } + + @Override + public org.apache.commons.collections.Predicate[] getPredicates() { + return delegate.getPredicates(); + } + + @Override + public int hashCode() { + return super.hashCode() + ((delegate == null) ? 0 : delegate.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof DelegatedSinglePredicateContainer) && (hashCode() == obj.hashCode())) { + DelegatedSinglePredicateContainer p = (DelegatedSinglePredicateContainer) obj; + return (delegate == null) ? (p.delegate == null) : delegate.equals(p.delegate); + } else { + return false; + } + } + + /** + * Processes the contained predicate into a Map. + * <p> + * This is used to serialize this {@link DelegatedSinglePredicateContainer} into a {@link Map} + * + * @return a map representing the contained predicate + */ + private Map<String, Object> containedPredicateToMap() { + + Map<String, Object> map = null; + + if (delegate != null) { + org.apache.commons.collections.Predicate[] predicates = delegate.getPredicates(); + + if ((predicates != null) && (predicates.length > 0)) { + // Only process the 1st predicate. + org.apache.commons.collections.Predicate p = predicates[0]; + if (p instanceof Predicate) { + map = ((Predicate) p).toMap(); + } else { + throw new UnsupportedOperationException(String.format("Cannot convert a %s to a Map", p.getClass().getName())); + } + } + } + + return map; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java new file mode 100644 index 0000000..a1a8716 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.functors.EqualPredicate; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +/** + * {@link EqualsPredicate} wraps {@link org.apache.commons.collections.functors.EqualPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data as well + * as obtaining data using a {@link ContextTransformer} + */ +public class EqualsPredicate extends OperationPredicate { + + /** + * The name of this {@link Predicate} implementation + */ + public static final String NAME = "equals"; + + /** + * The {@link org.apache.commons.collections.functors.EqualPredicate} to delegate operations to + */ + private final org.apache.commons.collections.functors.EqualPredicate delegate; + + /** + * Creates a new {@link EqualsPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "equals" and the value is a {@link Collection} of 2 {@link String}s. The first value + * should be the key to use when retrieving data from the context, which should resolve to a + * {@link String} (or null). The second value should be the value to use when checking for equality. + * + * @return a new {@link EqualsPredicate} + */ + public static EqualsPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof String) && (d2 instanceof String)) { + return new EqualsPredicate(new ContextTransformer((String) d1), (String) d2); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor. + * + * @param transformer the {@link ContextTransformer} + * @param value the value to test + */ + public EqualsPredicate(ContextTransformer transformer, String value) { + super(NAME, transformer); + delegate = new EqualPredicate(value); + } + + /** + * Gets the test value + * + * @return a string + */ + public String getValue() { + Object o = (delegate == null) ? null : delegate.getValue(); + return (o == null) ? null : o.toString(); + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(NAME, + new ArrayList<String>(Arrays.asList(getContextKey(), delegate.getValue().toString()))); + } + + @Override + public boolean evaluateTransformedData(Object data) { + return delegate.evaluate(data); + } +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java new file mode 100644 index 0000000..64dc592 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.PredicateUtils; +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Map; + +/** + * {@link NotPredicate} wraps {@link org.apache.commons.collections.functors.NotPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + * <p> + * See {@link DelegatedSinglePredicateContainer} + */ +public class NotPredicate extends DelegatedSinglePredicateContainer { + + /** + * The name of this {@link org.apache.ambari.server.collections.Predicate} implementation + */ + public static final String NAME = "not"; + + /** + * Creates a new {@link NotPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "not" and the value is a {@link Map} representing the contained predicate. + * + * @return a new {@link NotPredicate} + */ + public static NotPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Map) { + return new NotPredicate(PredicateUtils.fromMap((Map) data)); + } else { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } + } + + /** + * Constructor. + * + * @param predicate the predicate to negate + */ + public NotPredicate(Predicate predicate) { + super(NAME, + (PredicateDecorator) org.apache.commons.collections.functors.NotPredicate.getInstance(predicate)); + } + + @Override + public int hashCode() { + return super.hashCode() + this.getClass().getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || + ((obj != null) && (super.equals(obj) && this.getClass().isInstance(obj) && (hashCode() == obj.hashCode()))); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java new file mode 100644 index 0000000..c57f7c2 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.Transformer; + +/** + * OperationPredicate is an abstract class providing functionality of transforming the input context + * before executing the implementation-specific + * {@link org.apache.commons.collections.Predicate#evaluate(Object)} method. + */ +abstract class OperationPredicate extends Predicate { + /** + * The {@link Transformer} to use to transform the data before evaluation + */ + private ContextTransformer transformer = null; + + /** + * Constructor + * + * @param name the name of this {@link Predicate} + * @param transformer the {@link Transformer} to use to transform the data before evaluation + */ + OperationPredicate(String name, ContextTransformer transformer) { + super(name); + this.transformer = transformer; + } + + /** + * Gets the {@link Transformer} + * + * @return the assigned {@link ContextTransformer} + */ + public ContextTransformer getTransformer() { + return transformer; + } + + /** + * Sets the {@link Transformer} + * + * @param transformer a {@link ContextTransformer} + */ + public void setTransformer(ContextTransformer transformer) { + this.transformer = transformer; + } + + + /** + * Gets the context key assigned to the {@link ContextTransformer}. + * <p> + * This key is used to identify which value from the context a passed to the + * {@link org.apache.commons.collections.Predicate#evaluate(Object)} method + * + * @return a key name + */ + public String getContextKey() { + return (this.transformer == null) ? null : this.transformer.getKey(); + } + + @Override + public boolean evaluate(Object o) { + Object data = (transformer == null) ? o : transformer.transform(o); + return evaluateTransformedData(data); + } + + @Override + public int hashCode() { + return super.hashCode() + (37 * ((transformer == null) ? 0 : transformer.hashCode())); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof OperationPredicate) && (hashCode() == obj.hashCode())) { + OperationPredicate p = (OperationPredicate) obj; + return (transformer == null) ? (p.transformer == null) : transformer.equals(p.transformer); + } else { + return false; + } + } + + /** + * Abstract method used internally to process the {@link #evaluate(Object)} logic after the + * input data is transformed. + * + * @param data the transformed data to use + * @return the result of the evaluation (<code>true</code>, <code>false</code>) + * @see org.apache.commons.collections.Predicate#evaluate(Object) + */ + protected abstract boolean evaluateTransformedData(Object data); + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java new file mode 100644 index 0000000..b207e40 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.PredicateUtils; +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * {@link OrPredicate} wraps {@link org.apache.commons.collections.functors.OrPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + * <p> + * See {@link DelegatedMultiplePredicateContainer} + */ +public class OrPredicate extends DelegatedMultiplePredicateContainer { + /** + * The name of this {@link org.apache.ambari.server.collections.Predicate} implementation + */ + public static final String NAME = "or"; + + /** + * Creates a new {@link OrPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "or" and the value is a {@link Collection} of {@link Map}s representing the contained + * predicates. + * + * @return a new {@link OrPredicate} + */ + public static OrPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof Map) && (d2 instanceof Map)) { + return new OrPredicate(PredicateUtils.fromMap((Map) d1), PredicateUtils.fromMap((Map) d2)); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types for predicates: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor. + * + * @param predicate1 the first predicate to process + * @param predicate2 the second predicate to process (if the first one yields <code>false</code> + */ + public OrPredicate(Predicate predicate1, Predicate predicate2) { + super(NAME, + (PredicateDecorator) org.apache.commons.collections.functors.OrPredicate.getInstance(predicate1, predicate2)); + } + + @Override + public int hashCode() { + return super.hashCode() + this.getClass().getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || + ((obj != null) && (super.equals(obj) && this.getClass().isInstance(obj) && (hashCode() == obj.hashCode()))); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java new file mode 100644 index 0000000..f1f85c0 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link PredicateClassFactory} is a factory class used to derive a {@link Predicate} implementation + * class from its name. + */ +public class PredicateClassFactory { + /** + * A static map of names to {@link Class}s. + */ + private static final Map<String, Class<? extends Predicate>> NAME_TO_CLASS; + + static { + Map<String, Class<? extends Predicate>> map = new HashMap<String, Class<? extends Predicate>>(); + + map.put(AndPredicate.NAME, AndPredicate.class); + map.put(OrPredicate.NAME, OrPredicate.class); + map.put(NotPredicate.NAME, NotPredicate.class); + map.put(ContainsPredicate.NAME, ContainsPredicate.class); + map.put(EqualsPredicate.NAME, EqualsPredicate.class); + + NAME_TO_CLASS = Collections.unmodifiableMap(map); + } + + /** + * Return a {@link Predicate} implementation class give its name + * + * @param name the name of a {@link Predicate} implementation + * @return a {@link Predicate} implementation class give its name or <code>null</code> is not found + */ + public static Class<? extends Predicate> getPredicateClass(String name) { + return (name == null) ? null : NAME_TO_CLASS.get(name); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java index c67c55d..1c46a93 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java @@ -353,6 +353,11 @@ public class KerberosHelperImpl implements KerberosHelper { Map<String, Set<String>> propertiesToIgnore = new HashMap<String, Set<String>>(); + // Create the context to use for filtering Kerberos Identities based on the state of the cluster + Map<String, Object> filterContext = new HashMap<String, Object>(); + filterContext.put("configurations", configurations); + filterContext.put("services", services); + for (String serviceName : services) { // Set properties... KerberosServiceDescriptor serviceDescriptor = kerberosDescriptor.getService(serviceName); @@ -363,7 +368,7 @@ public class KerberosHelperImpl implements KerberosHelper { if (componentDescriptor != null) { Map<String, Map<String, String>> identityConfigurations; - identityConfigurations = getIdentityConfigurations(serviceDescriptor.getIdentities(true)); + identityConfigurations = getIdentityConfigurations(serviceDescriptor.getIdentities(true, filterContext)); if (identityConfigurations != null) { for (Map.Entry<String, Map<String, String>> entry : identityConfigurations.entrySet()) { String configType = entry.getKey(); @@ -382,7 +387,7 @@ public class KerberosHelperImpl implements KerberosHelper { } } - identityConfigurations = getIdentityConfigurations(componentDescriptor.getIdentities(true)); + identityConfigurations = getIdentityConfigurations(componentDescriptor.getIdentities(true, filterContext)); if (identityConfigurations != null) { for (Map.Entry<String, Map<String, String>> entry : identityConfigurations.entrySet()) { String configType = entry.getKey(); @@ -699,6 +704,11 @@ public class KerberosHelperImpl implements KerberosHelper { throw new AmbariException(message, e); } + // Create the context to use for filtering Kerberos Identities based on the state of the cluster + Map<String, Object> filterContext = new HashMap<String, Object>(); + filterContext.put("configurations", configurations); + filterContext.put("services", services); + for (String serviceName : services) { // Set properties... KerberosServiceDescriptor serviceDescriptor = kerberosDescriptor.getService(serviceName); @@ -710,7 +720,7 @@ public class KerberosHelperImpl implements KerberosHelper { List<KerberosIdentityDescriptor> identityDescriptors; // Handle the service-level Kerberos identities - identityDescriptors = serviceDescriptor.getIdentities(true); + identityDescriptors = serviceDescriptor.getIdentities(true, filterContext); if (identityDescriptors != null) { for (KerberosIdentityDescriptor identityDescriptor : identityDescriptors) { createUserIdentity(identityDescriptor, kerberosConfiguration, kerberosOperationHandler, configurations); @@ -718,7 +728,7 @@ public class KerberosHelperImpl implements KerberosHelper { } // Handle the component-level Kerberos identities - identityDescriptors = componentDescriptor.getIdentities(true); + identityDescriptors = componentDescriptor.getIdentities(true, filterContext); if (identityDescriptors != null) { for (KerberosIdentityDescriptor identityDescriptor : identityDescriptors) { createUserIdentity(identityDescriptor, kerberosConfiguration, kerberosOperationHandler, configurations); @@ -831,9 +841,14 @@ public class KerberosHelperImpl implements KerberosHelper { // Additional realms that need to be handled according to the Kerberos Descriptor String additionalRealms = kerberosDescriptor.getProperty("additional_realms"); + // Create the context to use for filtering Kerberos Identities based on the state of the cluster + Map<String, Object> filterContext = new HashMap<String, Object>(); + filterContext.put("configurations", existingConfigurations); + filterContext.put("services", cluster.getServices().keySet()); + // Determine which properties need to be set AuthToLocalBuilder authToLocalBuilder = new AuthToLocalBuilder(realm, additionalRealms, caseInsensitiveUser); - addIdentities(authToLocalBuilder, kerberosDescriptor.getIdentities(), null, existingConfigurations); + addIdentities(authToLocalBuilder, kerberosDescriptor.getIdentities(true, filterContext), null, existingConfigurations); authToLocalProperties = kerberosDescriptor.getAuthToLocalProperties(); if (authToLocalProperties != null) { @@ -847,7 +862,7 @@ public class KerberosHelperImpl implements KerberosHelper { for (KerberosServiceDescriptor service : services.values()) { if (installedServices.containsKey(service.getName())) { Service svc = installedServices.get(service.getName()); - addIdentities(authToLocalBuilder, service.getIdentities(true), null, existingConfigurations); + addIdentities(authToLocalBuilder, service.getIdentities(true, filterContext), null, existingConfigurations); authToLocalProperties = service.getAuthToLocalProperties(); if (authToLocalProperties != null) { @@ -897,7 +912,7 @@ public class KerberosHelperImpl implements KerberosHelper { if (addSvcCompIdentities) { LOG.info("Adding identity for " + component.getName() + " to auth to local mapping"); - addIdentities(authToLocalBuilder, component.getIdentities(true), null, existingConfigurations); + addIdentities(authToLocalBuilder, component.getIdentities(true, filterContext), null, existingConfigurations); authToLocalProperties = component.getAuthToLocalProperties(); if (authToLocalProperties != null) { @@ -905,8 +920,6 @@ public class KerberosHelperImpl implements KerberosHelper { } } - - } } } @@ -1281,22 +1294,30 @@ public class KerberosHelperImpl implements KerberosHelper { if (kerberosDescriptor != null) { Map<String, String> kerberosDescriptorProperties = kerberosDescriptor.getProperties(); + Set<String> existingServices = cluster.getServices().keySet(); + for (String hostname : hosts) { + // Calculate the current host-specific configurations. These will be used to replace + // variables within the Kerberos descriptor data + Map<String, Map<String, String>> configurations = calculateConfigurations(cluster, + hostname.equals(ambariServerHostname) ? null : hostname, + kerberosDescriptorProperties); + + // Create the context to use for filtering Kerberos Identities based on the state of the cluster + Map<String, Object> filterContext = new HashMap<String, Object>(); + filterContext.put("configurations", configurations); + filterContext.put("services", existingServices); + + Map<String, KerberosIdentityDescriptor> hostActiveIdentities = new HashMap<String, KerberosIdentityDescriptor>(); List<KerberosIdentityDescriptor> identities = getActiveIdentities(cluster, hostname, - serviceName, componentName, kerberosDescriptor); + serviceName, componentName, kerberosDescriptor, filterContext); if (hostname.equals(ambariServerHostname)) { addAmbariServerIdentity(kerberosEnvConfig.getProperties(), kerberosDescriptor, identities); } if (!identities.isEmpty()) { - // Calculate the current host-specific configurations. These will be used to replace - // variables within the Kerberos descriptor data - Map<String, Map<String, String>> configurations = calculateConfigurations(cluster, hostname.equals - (ambariServerHostname) ? null : hostname, - kerberosDescriptorProperties); - for (KerberosIdentityDescriptor identity : identities) { KerberosPrincipalDescriptor principalDescriptor = identity.getPrincipalDescriptor(); String principal = null; @@ -1352,7 +1373,8 @@ public class KerberosHelperImpl implements KerberosHelper { hostActiveIdentities.put(uniqueKey, new KerberosIdentityDescriptor( identity.getName(), resolvedPrincipalDescriptor, - resolvedKeytabDescriptor)); + resolvedKeytabDescriptor, + identity.getWhen())); } } } @@ -2305,13 +2327,15 @@ public class KerberosHelperImpl implements KerberosHelper { * components * @param kerberosDescriptor the relevant Kerberos Descriptor @return a list of KerberosIdentityDescriptors representing the active identities for the * requested service component + * @param filterContext the context to use for filtering identities based on the state of the cluster * @throws AmbariException if an error occurs processing the cluster's active identities */ private List<KerberosIdentityDescriptor> getActiveIdentities(Cluster cluster, String hostname, String serviceName, String componentName, - KerberosDescriptor kerberosDescriptor) + KerberosDescriptor kerberosDescriptor, + Map<String, Object> filterContext) throws AmbariException { List<KerberosIdentityDescriptor> identities = new ArrayList<KerberosIdentityDescriptor>(); @@ -2329,14 +2353,14 @@ public class KerberosHelperImpl implements KerberosHelper { KerberosServiceDescriptor serviceDescriptor = kerberosDescriptor.getService(schServiceName); if (serviceDescriptor != null) { - List<KerberosIdentityDescriptor> serviceIdentities = serviceDescriptor.getIdentities(true); + List<KerberosIdentityDescriptor> serviceIdentities = serviceDescriptor.getIdentities(true, filterContext); if (serviceIdentities != null) { identities.addAll(serviceIdentities); } KerberosComponentDescriptor componentDescriptor = serviceDescriptor.getComponent(schComponentName); if (componentDescriptor != null) { - List<KerberosIdentityDescriptor> componentIdentities = componentDescriptor.getIdentities(true); + List<KerberosIdentityDescriptor> componentIdentities = componentDescriptor.getIdentities(true, filterContext); if (componentIdentities != null) { identities.addAll(componentIdentities); } http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java index 0dbd357..b6b0713 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java @@ -36,7 +36,6 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.lang.reflect.Type; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -89,6 +88,11 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer // variables within the Kerberos descriptor data Map<String, Map<String, String>> configurations = kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptorProperties); + // Create the context to use for filtering Kerberos Identities based on the state of the cluster + Map<String, Object> filterContext = new HashMap<String, Object>(); + filterContext.put("configurations", configurations); + filterContext.put("services", cluster.getServices().keySet()); + actionLog.writeStdOut(String.format("Writing Kerberos identity data metadata file to %s", identityDataFile.getAbsolutePath())); try { kerberosIdentityDataFileWriter = kerberosIdentityDataFileWriterFactory.createKerberosIdentityDataFileWriter(identityDataFile); @@ -118,7 +122,7 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer KerberosServiceDescriptor serviceDescriptor = kerberosDescriptor.getService(serviceName); if (serviceDescriptor != null) { - List<KerberosIdentityDescriptor> serviceIdentities = serviceDescriptor.getIdentities(true); + List<KerberosIdentityDescriptor> serviceIdentities = serviceDescriptor.getIdentities(true, filterContext); // Add service-level principals (and keytabs) kerberosHelper.addIdentities(kerberosIdentityDataFileWriter, serviceIdentities, @@ -128,7 +132,7 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer KerberosComponentDescriptor componentDescriptor = serviceDescriptor.getComponent(componentName); if (componentDescriptor != null) { - List<KerberosIdentityDescriptor> componentIdentities = componentDescriptor.getIdentities(true); + List<KerberosIdentityDescriptor> componentIdentities = componentDescriptor.getIdentities(true, filterContext); // Calculate the set of configurations to update and replace any variables // using the previously calculated Map of configurations for the host. http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java index bb2ed1c..64d9292 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java @@ -162,14 +162,14 @@ public abstract class AbstractKerberosDescriptorContainer extends AbstractKerber * <p/> * The returned KerberosIdentityDescriptors are not merged with data from referenced * KerberosConfigurationDescriptors. This is the same calling - * {@link AbstractKerberosDescriptorContainer#getIdentities(boolean)} and setting the argument to - * 'false' + * {@link AbstractKerberosDescriptorContainer#getIdentities(boolean, Map)} and setting the + * argument to 'false' * * @return the relevant List of KerberosIdentityDescriptors */ public List<KerberosIdentityDescriptor> getIdentities() { try { - return getIdentities(false); + return getIdentities(false, null); } catch (AmbariException e) { // AmbariException will not be thrown unless an error occurs while trying to dereference // identities. This method does not attempt to dereference identities. @@ -200,19 +200,20 @@ public abstract class AbstractKerberosDescriptorContainer extends AbstractKerber * (false) * @return a List of the requested KerberosIdentityDescriptors */ - public List<KerberosIdentityDescriptor> getIdentities(boolean resolveReferences) throws AmbariException { - if (resolveReferences) { - if (identities == null) { - return Collections.emptyList(); - } else { - List<KerberosIdentityDescriptor> list = new ArrayList<KerberosIdentityDescriptor>(); - - // For each KerberosIdentityDescriptor, copy it and then attempt to find the referenced - // KerberosIdentityDescriptor. - // * If a reference is found, copy that, update it with the initial KerberosIdentityDescriptor - // and then add it to the list. - // * If a reference is not found, simply add the initial KerberosIdentityDescriptor to the list - for (KerberosIdentityDescriptor identity : identities) { + public List<KerberosIdentityDescriptor> getIdentities(boolean resolveReferences, Map<String,Object> contextForFilter) throws AmbariException { + if (identities == null) { + return Collections.emptyList(); + } else { + List<KerberosIdentityDescriptor> list = new ArrayList<KerberosIdentityDescriptor>(); + + for (KerberosIdentityDescriptor identity : identities) { + KerberosIdentityDescriptor identityToAdd; + + if (resolveReferences) { + // Copy this KerberosIdentityDescriptor and then attempt to find the referenced one. + // * If a reference is found, copy that, update it with the initial KerberosIdentityDescriptor + // and then add it to the list. + // * If a reference is not found, simply add the initial KerberosIdentityDescriptor to the list KerberosIdentityDescriptor referencedIdentity; try { referencedIdentity = getReferencedIdentityDescriptor(identity.getName()); @@ -226,16 +227,22 @@ public abstract class AbstractKerberosDescriptorContainer extends AbstractKerber if (referencedIdentity != null) { KerberosIdentityDescriptor detachedIdentity = new KerberosIdentityDescriptor(referencedIdentity.toMap()); detachedIdentity.update(identity); - list.add(detachedIdentity); + + identityToAdd = detachedIdentity; } else { - list.add(identity); + identityToAdd = identity; } + } else { + identityToAdd = identity; } - return list; + // Make sure this Kerberos Identity is not to be filtered out based on its "when" clause + if ((identityToAdd != null) && ((contextForFilter == null) || identityToAdd.shouldInclude(contextForFilter))) { + list.add(identityToAdd); + } } - } else { - return identities; + + return list; } } http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java index d31dd21..2631d35 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java @@ -17,6 +17,9 @@ */ package org.apache.ambari.server.state.kerberos; +import org.apache.ambari.server.collections.Predicate; +import org.apache.ambari.server.collections.PredicateUtils; + import java.util.Map; /** @@ -86,16 +89,25 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { private String password = null; /** + * An expression used to determine when this {@link KerberosIdentityDescriptor} is relevant for the + * cluster. If the process expression is not <code>null</code> and evaluates to <code>false</code> + * then this {@link KerberosIdentityDescriptor} will be ignored when processing identities. + */ + private Predicate when = null; + + /** * Creates a new KerberosIdentityDescriptor * * @param name the name of this identity descriptor * @param principal a KerberosPrincipalDescriptor * @param keytab a KerberosKeytabDescriptor + * @param when a predicate */ - public KerberosIdentityDescriptor(String name, KerberosPrincipalDescriptor principal, KerberosKeytabDescriptor keytab) { + public KerberosIdentityDescriptor(String name, KerberosPrincipalDescriptor principal, KerberosKeytabDescriptor keytab, Predicate when) { setName(name); setPrincipalDescriptor(principal); setKeytabDescriptor(keytab); + setWhen(when); } /** @@ -126,6 +138,11 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { if (item instanceof Map) { setKeytabDescriptor(new KerberosKeytabDescriptor((Map<?, ?>) item)); } + + item = data.get("when"); + if (item instanceof Map) { + setWhen(PredicateUtils.fromMap((Map<String, Object>) item)); + } } } @@ -193,6 +210,48 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { this.password = password; } + + /** + * Gets the expression (or {@link Predicate}) to use to determine when to include this Kerberos + * identity while processing Kerberos identities. + * <p> + * <code>null</code> indicates there is nothing to evaluate and this Kerberos identity is to always + * be included when processing Kerberos identities. + * + * @return a predicate + */ + public Predicate getWhen() { + return when; + } + + /** + * Sets the expression (or {@link Predicate}) to use to determine when to include this Kerberos + * identity while processing Kerberos identities. + * <p> + * <code>null</code> indicates there is nothing to evaluate and this Kerberos identity is to always + * be included when processing Kerberos identities. + * + * @param when a predicate + */ + public void setWhen(Predicate when) { + this.when = when; + } + + /** + * Processes the expression indicating when this {@link KerberosIdentityDescriptor} is to be included + * in the set of Kerberos identities to process. + * <p> + * <code>True</code> will be returned if the expression is <code>null</code> or if it evaluates + * as such. + * + * @param context A Map of context values, including at least the list of services and available configurations + * @return true if this {@link KerberosIdentityDescriptor} is to be included when processing the + * Kerberos identities; otherwise false. + */ + public boolean shouldInclude(Map<String, Object> context) { + return (this.when == null) || this.when.evaluate(context); + } + /** * Updates this KerberosIdentityDescriptor with data from another KerberosIdentityDescriptor * <p/> @@ -219,6 +278,11 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { } else { existingKeytabDescriptor.update(updates.getKeytabDescriptor()); } + + Predicate updatedWhen = updates.getWhen(); + if(updatedWhen != null) { + setWhen(updatedWhen); + } } } @@ -246,6 +310,10 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { dataMap.put("password", password); } + if(when != null) { + dataMap.put("when", PredicateUtils.toMap(when)); + } + return dataMap; } @@ -257,7 +325,10 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { : getPrincipalDescriptor().hashCode()) + ((getKeytabDescriptor() == null) ? 0 - : getKeytabDescriptor().hashCode()); + : getKeytabDescriptor().hashCode()) + + ((getWhen() == null) + ? 0 + : getWhen().hashCode()); } @Override @@ -283,6 +354,11 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { (getPassword() == null) ? (descriptor.getPassword() == null) : getPassword().equals(descriptor.getPassword()) + ) && + ( + (getWhen() == null) + ? (descriptor.getWhen() == null) + : getWhen().equals(descriptor.getWhen()) ); } else { return false; http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java index 9a58b8d..0d483fc 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog240.java @@ -2095,7 +2095,7 @@ public class UpgradeCatalog240 extends AbstractUpgradeCatalog { componentDescriptor.removeIdentity("hbase_queryserver_hbase"); // Add the new identity - componentDescriptor.putIdentity(new KerberosIdentityDescriptor("/spnego", newPrincipalDescriptor, newKeytabDescriptor)); + componentDescriptor.putIdentity(new KerberosIdentityDescriptor("/spnego", newPrincipalDescriptor, newKeytabDescriptor, null)); artifactEntity.setArtifactData(kerberosDescriptor.toMap()); artifactDAO.merge(artifactEntity); http://git-wip-us.apache.org/repos/asf/ambari/blob/2fcc9475/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java new file mode 100644 index 0000000..b97212d --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.collections; + +import junit.framework.Assert; +import org.apache.ambari.server.collections.functors.AndPredicate; +import org.apache.ambari.server.collections.functors.ContainsPredicate; +import org.apache.ambari.server.collections.functors.ContextTransformer; +import org.apache.ambari.server.collections.functors.EqualsPredicate; +import org.apache.ambari.server.collections.functors.NotPredicate; +import org.apache.ambari.server.collections.functors.OrPredicate; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +public class PredicateUtilsTest { + @Test + public void toMap() throws Exception { + // Test null predicate safely returns null + Assert.assertNull(PredicateUtils.toMap(null)); + + Assert.assertEquals(createMap(), PredicateUtils.toMap(createPredicate())); + } + + @Test + public void fromMap() throws Exception { + verifyPredicate(PredicateUtils.fromMap(createMap())); + } + + @Test + public void toJSON() throws Exception { + // Test null predicate safely returns null + Assert.assertNull(PredicateUtils.toJSON(null)); + + Assert.assertEquals(createJSON(), PredicateUtils.toJSON(createPredicate())); + } + + @Test + public void fromJSON() throws Exception { + verifyPredicate(PredicateUtils.fromJSON(createJSON())); + } + + private Predicate createPredicate() { + ContextTransformer transformer1 = new ContextTransformer("services"); + ContextTransformer transformer2 = new ContextTransformer("configurations/service-env/property1"); + ContextTransformer transformer3 = new ContextTransformer("configurations/cluster-env/property1"); + ContainsPredicate predicate1 = new ContainsPredicate(transformer1, "HDFS"); + EqualsPredicate predicate2 = new EqualsPredicate(transformer2, "true"); + EqualsPredicate predicate3 = new EqualsPredicate(transformer3, "false"); + + AndPredicate andPredicate = new AndPredicate(predicate1, predicate2); + OrPredicate orPredicate = new OrPredicate(predicate3, andPredicate); + + return new NotPredicate(orPredicate); + } + + private Map<String, Object> createMap() { + Map<String, Object> andMap = + Collections.<String, Object>singletonMap( + AndPredicate.NAME, Arrays.asList( + Collections.<String, Object>singletonMap(ContainsPredicate.NAME, Arrays.asList("services", "HDFS")), + Collections.<String, Object>singletonMap(EqualsPredicate.NAME, Arrays.asList("configurations/service-env/property1", "true")) + ) + ); + + Map<String, Object> orMap = + Collections.<String, Object>singletonMap(OrPredicate.NAME, + Arrays.asList( + Collections.<String, Object>singletonMap(EqualsPredicate.NAME, Arrays.asList("configurations/cluster-env/property1", "false")), + andMap + ) + ); + + return Collections.<String, Object>singletonMap(NotPredicate.NAME, orMap); + } + + private String createJSON() { + String andJSON = "{\"and\":[{\"contains\":[\"services\",\"HDFS\"]},{\"equals\":[\"configurations/service-env/property1\",\"true\"]}]}"; + String orJSON = "{\"or\":[{\"equals\":[\"configurations/cluster-env/property1\",\"false\"]}," + andJSON + "]}"; + return "{\"not\":" + orJSON + "}"; + } + + private void verifyPredicate(Predicate predicate) { + Assert.assertNotNull(predicate); + Assert.assertEquals(NotPredicate.NAME, predicate.getName()); + Assert.assertTrue(predicate instanceof NotPredicate); + + org.apache.commons.collections.Predicate[] predicates; + + predicates = ((NotPredicate) predicate).getPredicates(); + Assert.assertEquals(1, predicates.length); + + Assert.assertNotNull(predicates[0]); + Assert.assertTrue(predicates[0] instanceof OrPredicate); + Assert.assertEquals(OrPredicate.NAME, ((OrPredicate) predicates[0]).getName()); + + predicates = ((OrPredicate) predicates[0]).getPredicates(); + Assert.assertEquals(2, predicates.length); + + Assert.assertNotNull(predicates[0]); + Assert.assertTrue(predicates[0] instanceof EqualsPredicate); + Assert.assertEquals(EqualsPredicate.NAME, ((EqualsPredicate) predicates[0]).getName()); + Assert.assertEquals("configurations/cluster-env/property1", ((EqualsPredicate) predicates[0]).getContextKey()); + Assert.assertEquals("false", ((EqualsPredicate) predicates[0]).getValue()); + + Assert.assertNotNull(predicates[1]); + Assert.assertTrue(predicates[1] instanceof AndPredicate); + Assert.assertEquals(AndPredicate.NAME, ((AndPredicate) predicates[1]).getName()); + + predicates = ((AndPredicate) predicates[1]).getPredicates(); + Assert.assertEquals(2, predicates.length); + + Assert.assertNotNull(predicates[0]); + Assert.assertTrue(predicates[0] instanceof ContainsPredicate); + Assert.assertEquals(ContainsPredicate.NAME, ((ContainsPredicate) predicates[0]).getName()); + Assert.assertEquals("services", ((ContainsPredicate) predicates[0]).getContextKey()); + Assert.assertEquals("HDFS", ((ContainsPredicate) predicates[0]).getValue()); + + Assert.assertNotNull(predicates[1]); + Assert.assertTrue(predicates[1] instanceof EqualsPredicate); + Assert.assertEquals(EqualsPredicate.NAME, ((EqualsPredicate) predicates[1]).getName()); + Assert.assertEquals("configurations/service-env/property1", ((EqualsPredicate) predicates[1]).getContextKey()); + Assert.assertEquals("true", ((EqualsPredicate) predicates[1]).getValue()); + } +} \ No newline at end of file
