/** * 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 distribut * ed 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.topology; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; /** * Configuration for a topology entity such as a blueprint, hostgroup or cluster. */ public class Configuration { /** * properties for this configuration instance */ private Map<String, Map<String, String>> properties; /** * attributes for this configuration instance */ private Map<String, Map<String, Map<String, String>>> attributes; /** * parent configuration */ private Configuration parentConfiguration; /** * Constructor. * * @param properties properties * @param attributes attributes * @param parentConfiguration parent configuration */ public Configuration(Map<String, Map<String, String>> properties, Map<String, Map<String, Map<String, String>>> attributes, Configuration parentConfiguration) { this.properties = properties; this.attributes = attributes; this.parentConfiguration = parentConfiguration; //todo: warning for deprecated global properties // String message = null; // for (BlueprintConfigEntity blueprintConfig: blueprint.getConfigurations()){ // if(blueprintConfig.getType().equals("global")){ // message = "WARNING: Global configurations are deprecated, please use *-env"; // break; // } // } } /** * Configuration. * * @param properties properties * @param attributes attributes */ public Configuration(Map<String, Map<String, String>> properties, Map<String, Map<String, Map<String, String>>> attributes) { this.properties = properties; this.attributes = attributes; } /** * Get the properties for this instance only; parent properties are not included. * * @return map of properties for this configuration instance keyed by config type */ public Map<String, Map<String, String>> getProperties() { return properties; } /** * Get a complete merged map of properties including this instance and the entire parent hierarchy. * Properties are merged so that children override the same property specified in it's parent hierarchy. * This result is re-calculated for each request in case a parent value has changed so the result * should be cached if possible. * * @return complete map of merged properties keyed by config type */ public Map<String, Map<String, String>> getFullProperties() { return getFullProperties(Integer.MAX_VALUE); } /** * Get a merged map of properties including this instance and n levels of the parent hierarchy. * Properties are merged so that children override the same property specified in it's parent hierarchy. * This result is re-calculated for each request in case a parent value has changed so the result * should be cached if possible. * * @param depthLimit the number of parent levels to include in the results. Specifying 0 is the same * as calling {@link #getProperties()} * * @return map of merged properties keyed by config type */ public Map<String, Map<String, String>> getFullProperties(int depthLimit) { if (depthLimit == 0) { HashMap<String, Map<String, String>> propertiesCopy = new HashMap<>(); for (Map.Entry<String, Map<String, String>> typeProperties : properties.entrySet()) { propertiesCopy.put(typeProperties.getKey(), new HashMap<>(typeProperties.getValue())); } return propertiesCopy; } Map<String, Map<String, String>> mergedProperties = parentConfiguration == null ? new HashMap<String, Map<String, String>>() : new HashMap<>(parentConfiguration.getFullProperties(--depthLimit)); for (Map.Entry<String, Map<String, String>> entry : properties.entrySet()) { String configType = entry.getKey(); Map<String, String> typeProps = new HashMap<>(entry.getValue()); if (mergedProperties.containsKey(configType)) { mergedProperties.get(configType).putAll(typeProps); } else { mergedProperties.put(configType, typeProps); } } return mergedProperties; } /** * Get the attributes for this instance only; parent attributes aren't included. * * @return map of attributes {configType -> {attributeName -> {propName, attributeValue}}} */ public Map<String, Map<String, Map<String, String>>> getAttributes() { return attributes; } /** * Get a complete merged map of attributes including this instance and the entire parent hierarchy. * Attributes are merged so that children override the same attribute specified in it's parent hierarchy. * This result is re-calculated for each request in case a parent value has changed so the result * should be cached if possible. * * @return complete map of merged attributes {configType -> {attributeName -> {propName, attributeValue}}} */ public Map<String, Map<String, Map<String, String>>> getFullAttributes() { Map<String, Map<String, Map<String, String>>> mergedAttributeMap = parentConfiguration == null ? new HashMap<String, Map<String, Map<String, String>>>() : new HashMap<>(parentConfiguration.getFullAttributes()); for (Map.Entry<String, Map<String, Map<String, String>>> typeEntry : attributes.entrySet()) { String type = typeEntry.getKey(); Map<String, Map<String, String>> typeAttributes = new HashMap<>(); for (Map.Entry<String, Map<String, String>> attributeEntry : typeEntry.getValue().entrySet()) { typeAttributes.put(attributeEntry.getKey(), new HashMap<>(attributeEntry.getValue())); } if (! mergedAttributeMap.containsKey(type)) { mergedAttributeMap.put(type, typeAttributes); } else { Map<String, Map<String, String>> mergedAttributes = mergedAttributeMap.get(type); for (Map.Entry<String, Map<String, String>> attributeEntry : typeAttributes.entrySet()) { String attribute = attributeEntry.getKey(); if (! mergedAttributes.containsKey(attribute)) { mergedAttributes.put(attribute, attributeEntry.getValue()); } else { Map<String, String> mergedAttributeProps = mergedAttributes.get(attribute); for (Map.Entry<String, String> propEntry : attributeEntry.getValue().entrySet()) { mergedAttributeProps.put(propEntry.getKey(), propEntry.getValue()); } } } } } return mergedAttributeMap; } /** * Get the requested property value from the full merged set of properties. * * @param configType configuration type * @param propertyName property name * * @return requested property value or null if property isn't set in configuration hierarchy */ public String getPropertyValue(String configType, String propertyName) { String value = null; if (properties.containsKey(configType) && properties.get(configType).containsKey(propertyName)) { value = properties.get(configType).get(propertyName); } else if (parentConfiguration != null) { value = parentConfiguration.getPropertyValue(configType, propertyName); } return value; } /** * Get the requested attribute value from the full merged set of attributes. * * @param configType configuration type * @param propertyName attribute property name * @param attributeName attribute name * * @return requested attribute value or null if the attribute isn't set in configuration hierarchy */ public String getAttributeValue(String configType, String propertyName, String attributeName) { String value = null; if (attributes.containsKey(configType) && attributes.get(configType).containsKey(attributeName) && attributes.get(configType).get(attributeName).containsKey(propertyName)) { value = attributes.get(configType).get(attributeName).get(propertyName); } else if (parentConfiguration != null) { value = parentConfiguration.getAttributeValue(configType, propertyName, attributeName); } return value; } /** * Set a property on the configuration. * The property will be set on this instance so it will override any value specified in * the parent hierarchy. * * @param configType configuration type * @param propertyName property name * @param value property value * * @return the previous value of the property or null if it didn't exist */ public String setProperty(String configType, String propertyName, String value) { String previousValue = getPropertyValue(configType, propertyName); Map<String, String> typeProperties = properties.get(configType); if (typeProperties == null) { typeProperties = new HashMap<>(); properties.put(configType, typeProperties); } typeProperties.put(propertyName, value); return previousValue; } /** * Remove a property from the configuration hierarchy. * All occurrences of the property are removed from the config hierarchy such that * a subsequent call to getPropertyValue() for the removed property will return null. * * @param configType configuration type * @param propertyName property name * * @return the previous value of the removed property or null if it didn't exist in the * configuration hierarchy */ public String removeProperty(String configType, String propertyName) { String previousValue = null; if (properties.containsKey(configType)) { previousValue = properties.get(configType).remove(propertyName); } if (parentConfiguration != null) { String parentPreviousValue = parentConfiguration.removeProperty(configType, propertyName); if (previousValue == null) { previousValue = parentPreviousValue; } } return previousValue; } /** * Set an attribute on the hierarchy. * The attribute will be set on this instance so it will override any value specified in * the parent hierarchy. * * @param configType configuration type * @param propertyName attribute property name * @param attributeName attribute name * @param attributeValue attribute property value * * @return the previous value of the attribute or null if it didn't exist */ public String setAttribute(String configType, String propertyName, String attributeName, String attributeValue) { String previousValue = getAttributeValue(configType, propertyName, attributeName); Map<String, Map<String, String>> typeAttributes = attributes.get(configType); if (typeAttributes == null) { typeAttributes = new HashMap<>(); attributes.put(configType, typeAttributes); } Map<String, String> attributes = typeAttributes.get(attributeName); if (attributes == null) { attributes = new HashMap<>(); typeAttributes.put(attributeName, attributes); } attributes.put(propertyName, attributeValue); return previousValue; } /** * Get the complete set of configuration types represented in both full properties and full attributes. * * @return collection of all represented configuration types */ public Collection<String> getAllConfigTypes() { Collection<String> allTypes = new HashSet<>(); for (String type : getFullProperties().keySet()) { allTypes.add(type); } for (String type : getFullAttributes().keySet()) { allTypes.add(type); } return allTypes; } /** * Get the parent configuration. * * @return the parent configuration or null if no parent is set */ public Configuration getParentConfiguration() { return parentConfiguration; } /** * Set the parent configuration. * * @param parent parent configuration to set */ public void setParentConfiguration(Configuration parent) { parentConfiguration = parent; } /** * Remove all occurrences of a config type */ public void removeConfigType(String configType) { if (properties != null && properties.containsKey(configType)) { properties.remove(configType); } if (attributes != null && attributes.containsKey(configType)) { attributes.remove(configType); } if (parentConfiguration != null) { parentConfiguration.removeConfigType(configType); } } }