/** * 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.state; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.ambari.server.controller.StackVersionResponse; import org.apache.ambari.server.stack.Validable; import org.apache.ambari.server.state.repository.VersionDefinitionXml; import org.apache.ambari.server.state.stack.ConfigUpgradePack; import org.apache.ambari.server.state.stack.RepositoryXml; import org.apache.ambari.server.state.stack.StackRoleCommandOrder; import org.apache.ambari.server.state.stack.UpgradePack; import org.apache.ambari.server.utils.VersionUtils; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; import com.google.common.io.Files; public class StackInfo implements Comparable<StackInfo>, Validable{ private String minJdk; private String maxJdk; private String name; private String version; private String minUpgradeVersion; private boolean active; private String rcoFileLocation; private String kerberosDescriptorFileLocation; private String widgetsDescriptorFileLocation; private List<RepositoryInfo> repositories; private Collection<ServiceInfo> services; private Collection<ExtensionInfo> extensions; private String parentStackVersion; // stack-level properties private List<PropertyInfo> properties; private Map<String, Map<String, Map<String, String>>> configTypes; private Map<String, UpgradePack> upgradePacks; private ConfigUpgradePack configUpgradePack; private StackRoleCommandOrder roleCommandOrder; private boolean valid = true; private Map<String, Map<PropertyInfo.PropertyType, Set<String>>> propertiesTypesCache = Collections.synchronizedMap(new HashMap<String, Map<PropertyInfo.PropertyType, Set<String>>>()); private Map<String, Map<String, Map<String, String>>> configPropertyAttributes = null; /** * Meaning: stores subpath from stack root to exact hooks folder for stack. These hooks are * applied to all commands for services in current stack. */ private String stackHooksFolder; private String upgradesFolder = null; private volatile Map<String, PropertyInfo> requiredProperties; private Map<String, VersionDefinitionXml> versionDefinitions = new ConcurrentHashMap<>(); private Set<String> errorSet = new HashSet<>(); private RepositoryXml repoXml = null; /** * List of services removed from current stack * */ private List<String> removedServices = new ArrayList<>(); /** * List of services withnot configurations * */ private List<String> servicesWithNoConfigs = new ArrayList<String>(); public String getMinJdk() { return minJdk; } public void setMinJdk(String minJdk) { this.minJdk = minJdk; } public String getMaxJdk() { return maxJdk; } public void setMaxJdk(String maxJdk) { this.maxJdk = maxJdk; } /** * * @return valid xml flag */ @Override public boolean isValid() { return valid; } /** * * @param valid set validity flag */ @Override public void setValid(boolean valid) { this.valid = valid; } @Override public void addError(String error) { errorSet.add(error); } @Override public Collection<String> getErrors() { return errorSet; } @Override public void addErrors(Collection<String> errors) { this.errorSet.addAll(errors); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public List<RepositoryInfo> getRepositories() { if( repositories == null ) repositories = new ArrayList<>(); return repositories; } /** * @return A list containing all repos for this stack, grouped by os */ public ListMultimap<String, RepositoryInfo> getRepositoriesByOs() { return Multimaps.index(getRepositories(), RepositoryInfo.GET_OSTYPE_FUNCTION); } public synchronized Collection<ServiceInfo> getServices() { if (services == null) services = new ArrayList<>(); return services; } public ServiceInfo getService(String name) { Collection<ServiceInfo> services = getServices(); for (ServiceInfo service : services) { if (service.getName().equals(name)) { return service; } } //todo: exception? return null; } public synchronized void setServices(Collection<ServiceInfo> services) { this.services = services; } public synchronized Collection<ExtensionInfo> getExtensions() { if (extensions == null) extensions = new ArrayList<>(); return extensions; } public ExtensionInfo getExtension(String name) { Collection<ExtensionInfo> extensions = getExtensions(); for (ExtensionInfo extension : extensions) { if (extension.getName().equals(name)) { return extension; } } //todo: exception? return null; } public ExtensionInfo getExtensionByService(String serviceName) { Collection<ExtensionInfo> extensions = getExtensions(); for (ExtensionInfo extension : extensions) { Collection<ServiceInfo> services = extension.getServices(); for (ServiceInfo service : services) { if (service.getName().equals(serviceName)) return extension; } } //todo: exception? return null; } public void addExtension(ExtensionInfo extension) { Collection<ExtensionInfo> extensions = getExtensions(); extensions.add(extension); Collection<ServiceInfo> services = getServices(); for (ServiceInfo service : extension.getServices()) { services.add(service); } } public void removeExtension(ExtensionInfo extension) { Collection<ExtensionInfo> extensions = getExtensions(); extensions.remove(extension); Collection<ServiceInfo> services = getServices(); for (ServiceInfo service : extension.getServices()) { services.remove(service); } } public List<PropertyInfo> getProperties() { if (properties == null) properties = new ArrayList<>(); return properties; } public void setProperties(List<PropertyInfo> properties) { this.properties = properties; } /** * Obtain the config types associated with this stack. * The returned map is an unmodifiable view. * @return copy of the map of config types associated with this stack */ public synchronized Map<String, Map<String, Map<String, String>>> getConfigTypeAttributes() { return configTypes == null ? Collections.<String, Map<String, Map<String, String>>>emptyMap() : Collections.unmodifiableMap(configTypes); } /** * Add the given type and set it's attributes. * * @param type configuration type * @param typeAttributes attributes associated with the type */ public synchronized void setConfigTypeAttributes(String type, Map<String, Map<String, String>> typeAttributes) { if (this.configTypes == null) { configTypes = new HashMap<>(); } // todo: no exclusion mechanism for stack config types configTypes.put(type, typeAttributes); } /** * Set all types and associated attributes. Any previously existing types and * attributes are removed prior to setting the new values. * * @param types map of type attributes */ public synchronized void setAllConfigAttributes(Map<String, Map<String, Map<String, String>>> types) { configTypes = new HashMap<>(); for (Map.Entry<String, Map<String, Map<String, String>>> entry : types.entrySet()) { setConfigTypeAttributes(entry.getKey(), entry.getValue()); } } @Override public String toString() { StringBuilder sb = new StringBuilder("Stack name:" + name + "\nversion:" + version + "\nactive:" + active + " \nvalid:" + isValid()); if (services != null) { sb.append("\n\t\tService:"); for (ServiceInfo service : services) { sb.append("\t\t"); sb.append(service); } } if (repositories != null) { sb.append("\n\t\tRepositories:"); for (RepositoryInfo repository : repositories) { sb.append("\t\t"); sb.append(repository.toString()); } } return sb.toString(); } @Override public int hashCode() { return 31 + name.hashCode() + version.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof StackInfo)) { return false; } if (this == obj) { return true; } StackInfo stackInfo = (StackInfo) obj; return getName().equals(stackInfo.getName()) && getVersion().equals(stackInfo.getVersion()); } public StackVersionResponse convertToResponse() { // Get the stack-level Kerberos descriptor file path String stackDescriptorFileFilePath = getKerberosDescriptorFileLocation(); // Collect the services' Kerberos descriptor files Collection<ServiceInfo> serviceInfos = getServices(); // The collection of service descriptor files. A Set is being used because some Kerberos descriptor // files contain multiple services, therefore the same File may be encountered more than once. // For example the YARN directory may contain YARN and MAPREDUCE2 services. Collection<File> serviceDescriptorFiles = new HashSet<>(); if (serviceInfos != null) { for (ServiceInfo serviceInfo : serviceInfos) { File file = serviceInfo.getKerberosDescriptorFile(); if (file != null) { serviceDescriptorFiles.add(file); } } } return new StackVersionResponse(getVersion(), getMinUpgradeVersion(), isActive(), getParentStackVersion(), getConfigTypeAttributes(), (stackDescriptorFileFilePath == null) ? null : new File(stackDescriptorFileFilePath), serviceDescriptorFiles, null == upgradePacks ? Collections.<String>emptySet() : upgradePacks.keySet(), isValid(), getErrors(), getMinJdk(), getMaxJdk()); } public String getMinUpgradeVersion() { return minUpgradeVersion; } public void setMinUpgradeVersion(String minUpgradeVersion) { this.minUpgradeVersion = minUpgradeVersion; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } public String getParentStackVersion() { return parentStackVersion; } public void setParentStackVersion(String parentStackVersion) { this.parentStackVersion = parentStackVersion; } public StackRoleCommandOrder getRoleCommandOrder() { return roleCommandOrder; } public void setRoleCommandOrder(StackRoleCommandOrder roleCommandOrder) { this.roleCommandOrder = roleCommandOrder; } public String getRcoFileLocation() { return rcoFileLocation; } public void setRcoFileLocation(String rcoFileLocation) { this.rcoFileLocation = rcoFileLocation; } /** * Gets the path to the stack-level Kerberos descriptor file * * @return a String containing the path to the stack-level Kerberos descriptor file */ public String getKerberosDescriptorFileLocation() { return kerberosDescriptorFileLocation; } /** * Sets the path to the stack-level Kerberos descriptor file * * @param kerberosDescriptorFileLocation a String containing the path to the stack-level Kerberos * descriptor file */ public void setKerberosDescriptorFileLocation(String kerberosDescriptorFileLocation) { this.kerberosDescriptorFileLocation = kerberosDescriptorFileLocation; } public String getWidgetsDescriptorFileLocation() { return widgetsDescriptorFileLocation; } public void setWidgetsDescriptorFileLocation(String widgetsDescriptorFileLocation) { this.widgetsDescriptorFileLocation = widgetsDescriptorFileLocation; } public String getStackHooksFolder() { return stackHooksFolder; } public void setStackHooksFolder(String stackHooksFolder) { this.stackHooksFolder = stackHooksFolder; } /** * Set the path of the stack upgrade directory. * * @param path the path to the upgrades directory */ public void setUpgradesFolder(String path) { upgradesFolder = path; } /** * Obtain the path of the upgrades folder or null if directory doesn't exist. * * @return the upgrades folder, or {@code null} if not set */ public String getUpgradesFolder() { return upgradesFolder; } /** * Obtain all stack upgrade packs. * * @return map of upgrade pack name to upgrade pack or {@code null} if no packs */ public Map<String, UpgradePack> getUpgradePacks() { return upgradePacks; } /** * Set upgrade packs. * * @param upgradePacks map of upgrade packs */ public void setUpgradePacks(Map<String, UpgradePack> upgradePacks) { this.upgradePacks = upgradePacks; } /** * Get config upgrade pack for stack * @return config upgrade pack for stack or null if it is * not defined */ public ConfigUpgradePack getConfigUpgradePack() { return configUpgradePack; } /** * Set config upgrade pack for stack * @param configUpgradePack config upgrade pack for stack or null if it is * not defined */ public void setConfigUpgradePack(ConfigUpgradePack configUpgradePack) { this.configUpgradePack = configUpgradePack; } @Override public int compareTo(StackInfo o) { if (name.equals(o.name)) { return VersionUtils.compareVersions(version, o.version); } return name.compareTo(o.name); } //todo: ensure that required properties are never modified... public Map<String, PropertyInfo> getRequiredProperties() { Map<String, PropertyInfo> result = requiredProperties; if (result == null) { synchronized(this) { result = requiredProperties; if (result == null) { requiredProperties = result = new HashMap<>(); List<PropertyInfo> properties = getProperties(); for (PropertyInfo propertyInfo : properties) { if (propertyInfo.isRequireInput()) { result.put(propertyInfo.getName(), propertyInfo); } } } } } return result; } public Map<PropertyInfo.PropertyType, Set<String>> getConfigPropertiesTypes(String configType) { if(!propertiesTypesCache.containsKey(configType)) { Map<PropertyInfo.PropertyType, Set<String>> propertiesTypes = new HashMap<>(); Collection<ServiceInfo> services = getServices(); for (ServiceInfo serviceInfo : services) { for (PropertyInfo propertyInfo : serviceInfo.getProperties()) { if (propertyInfo.getFilename().contains(configType) && !propertyInfo.getPropertyTypes().isEmpty()) { Set<PropertyInfo.PropertyType> types = propertyInfo.getPropertyTypes(); for (PropertyInfo.PropertyType propertyType : types) { if (!propertiesTypes.containsKey(propertyType)) propertiesTypes.put(propertyType, new HashSet<String>()); propertiesTypes.get(propertyType).add(propertyInfo.getName()); } } } } propertiesTypesCache.put(configType, propertiesTypes); } return propertiesTypesCache.get(configType); } /** * Return default config attributes for specified config type. * @param configType config type * @return config attributes */ public synchronized Map<String, Map<String, String>> getDefaultConfigAttributesForConfigType(String configType){ if(configPropertyAttributes == null){ configPropertyAttributes = getDefaultConfigAttributes(); } if(configPropertyAttributes.containsKey(configType)){ return configPropertyAttributes.get(configType); } return null; } private Map<String, Map<String, Map<String, String>>> getDefaultConfigAttributes(){ Map<String, Map<String, Map<String, String>>> result = new HashMap<>(); for(ServiceInfo si : services){ for(PropertyInfo pi : si.getProperties()) { String propertyConfigType = Files.getNameWithoutExtension(pi.getFilename()); String propertyName = pi.getName(); String hidden = pi.getPropertyValueAttributes().getHidden(); if(hidden != null){ if(!result.containsKey(propertyConfigType)){ result.put(propertyConfigType, new HashMap<String, Map<String, String>>()); } if(!result.get(propertyConfigType).containsKey("hidden")){ result.get(propertyConfigType).put("hidden", new HashMap<String, String>()); } result.get(propertyConfigType).get("hidden").put(propertyName, hidden); } } } return result; } /** * @param key the version that the xml represents * @param xml the version definition object */ public void addVersionDefinition(String key, VersionDefinitionXml xml) { versionDefinitions.put(key, xml); } /** * @return the list of available definitions on this stack */ public Collection<VersionDefinitionXml> getVersionDefinitions() { return versionDefinitions.values(); } /** * @param rxml the repository xml object */ public void setRepositoryXml(RepositoryXml rxml) { repoXml = rxml; } /** * @return the repository xml object, or {@code null} if it couldn't be loaded */ public RepositoryXml getRepositoryXml() { return repoXml; } public List<String> getRemovedServices() { return removedServices; } public void setRemovedServices(List<String> removedServices) { this.removedServices = removedServices; } public List<String> getServicesWithNoConfigs() { return servicesWithNoConfigs; } public void setServicesWithNoConfigs(List<String> servicesWithNoConfigs) { this.servicesWithNoConfigs = servicesWithNoConfigs; } }