/** * 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.AbstractMap; 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.TreeMap; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlTransient; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.stack.Validable; import org.apache.ambari.server.state.stack.MetricDefinition; import org.apache.ambari.server.state.stack.StackRoleCommandOrder; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.map.annotate.JsonFilter; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @XmlAccessorType(XmlAccessType.FIELD) @JsonFilter("propertiesfilter") public class ServiceInfo implements Validable{ public static final AbstractMap.SimpleEntry<String, String> DEFAULT_SERVICE_INSTALLABLE_PROPERTY = new AbstractMap.SimpleEntry<>("installable", "true"); public static final AbstractMap.SimpleEntry<String, String> DEFAULT_SERVICE_MANAGED_PROPERTY = new AbstractMap.SimpleEntry<>("managed", "true"); public static final AbstractMap.SimpleEntry<String, String> DEFAULT_SERVICE_MONITORED_PROPERTY = new AbstractMap.SimpleEntry<>("monitored", "true"); /** * Format version. Added at schema ver 2 */ @XmlTransient private String schemaVersion; private String name; private String displayName; private String version; private String comment; private String serviceType; private Selection selection; @XmlTransient private List<PropertyInfo> properties; @XmlElementWrapper(name="components") @XmlElements(@XmlElement(name="component")) private List<ComponentInfo> components; @XmlElement(name="deleted") private boolean isDeleted = false; @XmlElement(name="supportDeleteViaUI") private Boolean supportDeleteViaUIField; private boolean supportDeleteViaUIInternal = true; @JsonIgnore @XmlTransient private volatile Map<String, Set<String>> configLayout = null; @XmlElementWrapper(name="configuration-dependencies") @XmlElement(name="config-type") private List<String> configDependencies; @XmlElementWrapper(name="excluded-config-types") @XmlElement(name="config-type") private Set<String> excludedConfigTypes = new HashSet<>(); @XmlTransient private Map<String, Map<String, Map<String, String>>> configTypes; @JsonIgnore private Boolean monitoringService; @JsonIgnore @XmlElement(name = "restartRequiredAfterChange") private Boolean restartRequiredAfterChange; @JsonIgnore @XmlElement(name = "restartRequiredAfterRackChange") private Boolean restartRequiredAfterRackChange; @XmlElement(name = "extends") private String parent; @XmlElement(name = "widgetsFileName") private String widgetsFileName = AmbariMetaInfo.WIDGETS_DESCRIPTOR_FILE_NAME; @XmlElement(name = "metricsFileName") private String metricsFileName = AmbariMetaInfo.SERVICE_METRIC_FILE_NAME; @XmlTransient private volatile Map<String, PropertyInfo> requiredProperties; /** * Credential store information */ @XmlElements(@XmlElement(name = "credential-store")) private CredentialStoreInfo credentialStoreInfo; public Boolean isRestartRequiredAfterChange() { return restartRequiredAfterChange; } public void setRestartRequiredAfterChange(Boolean restartRequiredAfterChange) { this.restartRequiredAfterChange = restartRequiredAfterChange; } @XmlTransient private File metricsFile = null; @XmlTransient private Map<String, Map<String, List<MetricDefinition>>> metrics = null; @XmlTransient private File advisorFile = null; @XmlTransient private String advisorName = null; @XmlTransient private File alertsFile = null; @XmlTransient private File kerberosDescriptorFile = null; @XmlTransient private File widgetsDescriptorFile = null; private StackRoleCommandOrder roleCommandOrder; @XmlTransient private boolean valid = true; @XmlElementWrapper(name = "properties") @XmlElement(name="property") private List<ServicePropertyInfo> servicePropertyList = Lists.newArrayList(); @XmlTransient private Map<String, String> servicePropertyMap = ImmutableMap.copyOf(ensureMandatoryServiceProperties(Maps.<String, String>newHashMap())); /** * * @return valid xml flag */ @Override public boolean isValid() { return valid; } /** * * @param valid set validity flag */ @Override public void setValid(boolean valid) { this.valid = valid; } @XmlTransient private Set<String> errorSet = new HashSet<>(); @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); } /** * Internal list of os-specific details (loaded from xml). Added at schema ver 2 */ @JsonIgnore @XmlElementWrapper(name="osSpecifics") @XmlElements(@XmlElement(name="osSpecific")) private List<ServiceOsSpecific> serviceOsSpecifics; @JsonIgnore @XmlElement(name="configuration-dir") private String configDir = AmbariMetaInfo.SERVICE_CONFIG_FOLDER_NAME; @JsonIgnore @XmlElement(name = "themes-dir") private String themesDir = AmbariMetaInfo.SERVICE_THEMES_FOLDER_NAME; @JsonIgnore @XmlElementWrapper(name = "themes") @XmlElements(@XmlElement(name = "theme")) private List<ThemeInfo> themes; @XmlTransient private volatile Map<String, ThemeInfo> themesMap; @JsonIgnore @XmlElement(name = "quickLinksConfigurations-dir") private String quickLinksConfigurationsDir = AmbariMetaInfo.SERVICE_QUICKLINKS_CONFIGURATIONS_FOLDER_NAME; @JsonIgnore @XmlElementWrapper(name = "quickLinksConfigurations") @XmlElements(@XmlElement(name = "quickLinksConfiguration")) private List<QuickLinksConfigurationInfo> quickLinksConfigurations; @XmlTransient private volatile Map<String, QuickLinksConfigurationInfo> quickLinksConfigurationsMap; /** * Map of of os-specific details that is exposed (and initialised from list) * at getter. * Added at schema ver 2 */ private volatile Map<String, ServiceOsSpecific> serviceOsSpecificsMap; /** * This is used to add service check actions for services. * Added at schema ver 2 */ private CommandScriptDefinition commandScript; /** * Added at schema ver 2 */ @XmlElementWrapper(name="customCommands") @XmlElements(@XmlElement(name="customCommand")) private List<CustomCommandDefinition> customCommands; @XmlElementWrapper(name="requiredServices") @XmlElement(name="service") private List<String> requiredServices = new ArrayList<>(); /** * Meaning: stores subpath from stack root to exact directory, that contains * service scripts and templates. Since schema ver 2, * we may have multiple service metadata inside folder. * Added at schema ver 2 */ @XmlTransient private String servicePackageFolder; /** * Stores the path to the upgrades folder which contains the upgrade xmls for the given service. */ @XmlTransient private File serviceUpgradesFolder; /** * Stores the path to the checks folder which contains prereq check jars for the given service. */ @XmlTransient private File checksFolder; public boolean isDeleted() { return isDeleted; } public void setDeleted(boolean deleted) { isDeleted = deleted; } public Boolean getSupportDeleteViaUIField(){ return supportDeleteViaUIField; } public void setSupportDeleteViaUIField(Boolean supportDeleteViaUIField) { this.supportDeleteViaUIField = supportDeleteViaUIField; } public boolean isSupportDeleteViaUI() { if (null != supportDeleteViaUIField) { return supportDeleteViaUIField.booleanValue(); } // If set to null and has a parent, then the value would have already been resolved and set. // Otherwise, return the default value (true). return this.supportDeleteViaUIInternal; } public void setSupportDeleteViaUI(boolean supportDeleteViaUI){ this.supportDeleteViaUIInternal = supportDeleteViaUI; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getParent() { return parent; } public void setParent(String parent) { this.parent = parent; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getServiceType() { return serviceType; } public void setServiceType(String serviceType) { this.serviceType = serviceType; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public Selection getSelection() { if (selection == null) { return Selection.DEFAULT; } return selection; } public void setSelection(Selection selection) { this.selection = selection; } /** * Check if selection was presented in xml. We need this for proper stack inheritance, because {@link ServiceInfo#getSelection} * by default returns {@link Selection#DEFAULT}, even if no value found in metainfo.xml. * @return true, if selection not defined in metainfo.xml */ public boolean isSelectionEmpty() { return selection == null; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public List<String> getRequiredServices() { return requiredServices; } public String getWidgetsFileName() { return widgetsFileName; } public void setWidgetsFileName(String widgetsFileName) { this.widgetsFileName = widgetsFileName; } public String getMetricsFileName() { return metricsFileName; } public void setMetricsFileName(String metricsFileName) { this.metricsFileName = metricsFileName; } public void setRequiredServices(List<String> requiredServices) { this.requiredServices = requiredServices; } public List<PropertyInfo> getProperties() { if (properties == null) properties = new ArrayList<>(); return properties; } public void setProperties(List properties) { this.properties = properties; } public List<ComponentInfo> getComponents() { if (components == null) components = new ArrayList<>(); return components; } /** * Finds ComponentInfo by component name * @param componentName name of the component * @return ComponentInfo componentName or null */ public ComponentInfo getComponentByName(String componentName){ for(ComponentInfo componentInfo : getComponents()) { if(componentInfo.getName().equals(componentName)){ return componentInfo; } } return null; } public boolean isClientOnlyService() { if (components == null || components.isEmpty()) { return false; } for (ComponentInfo compInfo : components) { if (!compInfo.isClient()) { return false; } } return true; } public ComponentInfo getClientComponent() { ComponentInfo client = null; if (components != null) { for (ComponentInfo compInfo : components) { if (compInfo.isClient()) { client = compInfo; break; } } } return client; } public File getAdvisorFile() { return advisorFile; } public void setAdvisorFile(File advisorFile) { this.advisorFile = advisorFile; } public String getAdvisorName() { return advisorName; } public void setAdvisorName(String advisorName) { this.advisorName = advisorName; } /** * Indicates if this service supports credential store. * False, it was not specified. * * @return true or false */ public boolean isCredentialStoreSupported() { if (credentialStoreInfo != null) { if (credentialStoreInfo.isSupported() != null) { return credentialStoreInfo.isSupported(); } } return false; } /** * Set a value indicating if this service supports credential store. * @param credentialStoreSupported */ public void setCredentialStoreSupported(boolean credentialStoreSupported) { if (credentialStoreInfo == null) { credentialStoreInfo = new CredentialStoreInfo(); } credentialStoreInfo.setSupported(credentialStoreSupported); } /** * Indicates if this service is requires credential store. * False if it was not specified. * * @return true or false */ public boolean isCredentialStoreRequired() { if (credentialStoreInfo != null) { if (credentialStoreInfo.isRequired() != null) { return credentialStoreInfo.isRequired(); } } return false; } /** * Set a value indicating if this service requires credential store. * @param credentialStoreRequired */ public void setCredentialStoreRequired(boolean credentialStoreRequired) { if (credentialStoreInfo == null) { credentialStoreInfo = new CredentialStoreInfo(); } credentialStoreInfo.setRequired(credentialStoreRequired); } /** * Indicates if this service is enabled for credential store use. * False if it was not specified. * * @return true or false */ public boolean isCredentialStoreEnabled() { if (credentialStoreInfo != null) { if (credentialStoreInfo.isEnabled() != null) { return credentialStoreInfo.isEnabled(); } } return false; } /** * Set a value indicating if this service is enabled for credential store use. * @param credentialStoreEnabled */ public void setCredentialStoreEnabled(boolean credentialStoreEnabled) { if (credentialStoreInfo == null) { credentialStoreInfo = new CredentialStoreInfo(); } credentialStoreInfo.setEnabled(credentialStoreEnabled); } /** * Get the credential store information object. * * @return */ public CredentialStoreInfo getCredentialStoreInfo() { return credentialStoreInfo; } /** * Set a new value for the credential store information. * * @param credentialStoreInfo */ public void setCredentialStoreInfo(CredentialStoreInfo credentialStoreInfo) { this.credentialStoreInfo = credentialStoreInfo; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Service name:"); sb.append(name); sb.append("\nService type:"); sb.append(serviceType); sb.append("\nversion:"); sb.append(version); sb.append("\ncomment:"); sb.append(comment); //for (PropertyInfo property : getProperties()) { // sb.append("\tProperty name=" + property.getName() + //"\nproperty value=" + property.getValue() + "\ndescription=" + property.getDescription()); //} for (ComponentInfo component : getComponents()) { sb.append("\n\n\nComponent:\n"); sb.append("name="); sb.append(component.getName()); sb.append("\tcategory="); sb.append(component.getCategory()); } return sb.toString(); } /** * Obtain the config types associated with this service. * The returned map is an unmodifiable view. * @return unmodifiable map of config types associated with this service */ public synchronized Map<String, Map<String, Map<String, String>>> getConfigTypeAttributes() { Map<String, Map<String, Map<String, String>>> tmpConfigTypes = configTypes == null ? new HashMap<String, Map<String, Map<String, String>>>() : configTypes; for(String excludedtype : excludedConfigTypes){ tmpConfigTypes.remove(excludedtype); } return Collections.unmodifiableMap(tmpConfigTypes); } /** * Add the given type and set it's attributes. * If the type is marked for exclusion, it will not be added. * * @param type configuration type * @param typeAttributes attributes associated with the type */ public synchronized void setTypeAttributes(String type, Map<String, Map<String, String>> typeAttributes) { if (this.configTypes == null) { configTypes = new HashMap<>(); } 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()) { setTypeAttributes(entry.getKey(), entry.getValue()); } } /** * Determine of the service has a dependency on the provided configuration type. * @param type the config type * @return <code>true</code> if the service defines a dependency on the provided type */ public boolean hasConfigDependency(String type) { return configDependencies != null && configDependencies.contains(type); } /** * Determine if the service contains the specified config type * @param type config type to check * @return true if the service has the specified config type; false otherwise */ public boolean hasConfigType(String type) { return configTypes != null && configTypes.containsKey(type) && !excludedConfigTypes.contains(type); } /** * Determine if the service has a dependency on the provided type and contains any of the provided properties. * This can be used in determining if a property is stale. * @param type the config type * @param keyNames the names of all the config keys for the given type * @return <code>true</code> if the config is stale */ public boolean hasDependencyAndPropertyFor(String type, Collection<String> keyNames) { if (!hasConfigDependency(type)) return false; buildConfigLayout(); Set<String> keys = configLayout.get(type); for (String staleCheck : keyNames) { if (keys != null && keys.contains(staleCheck)) return true; } return false; } /** * Builds the config map specific to this service. */ private void buildConfigLayout() { if (null == configLayout) { synchronized(this) { if (null == configLayout) { configLayout = new HashMap<>(); for (PropertyInfo pi : getProperties()) { String type = pi.getFilename(); int idx = type.indexOf(".xml"); type = type.substring(0, idx); if (!configLayout.containsKey(type)) configLayout.put(type, new HashSet<String>()); configLayout.get(type).add(pi.getName()); } } } } } public List<String> getConfigDependencies() { return configDependencies; } public List<String> getConfigDependenciesWithComponents(){ List<String> retVal = new ArrayList<>(); if(configDependencies != null){ retVal.addAll(configDependencies); } if(components != null){ for (ComponentInfo c : components) { if(c.getConfigDependencies() != null){ retVal.addAll(c.getConfigDependencies()); } } } return retVal.size() == 0 ? (configDependencies == null ? null : configDependencies) : retVal; } public void setConfigDependencies(List<String> configDependencies) { this.configDependencies = configDependencies; } public String getSchemaVersion() { if (schemaVersion == null) { return AmbariMetaInfo.SCHEMA_VERSION_2; } else { return schemaVersion; } } public void setSchemaVersion(String schemaVersion) { this.schemaVersion = schemaVersion; } public String getServicePackageFolder() { return servicePackageFolder; } public void setServicePackageFolder(String servicePackageFolder) { this.servicePackageFolder = servicePackageFolder; } public File getServiceUpgradesFolder() { return serviceUpgradesFolder; } public void setServiceUpgradesFolder(File serviceUpgradesFolder) { this.serviceUpgradesFolder = serviceUpgradesFolder; } public File getChecksFolder() { return checksFolder; } public void setChecksFolder(File checksFolder) { this.checksFolder = checksFolder; } /** * Exposes (and initializes on first use) map of os-specific details. * @return map of OS specific details keyed by family */ public Map<String, ServiceOsSpecific> getOsSpecifics() { if (serviceOsSpecificsMap == null) { synchronized (this) { // Double-checked locking pattern if (serviceOsSpecificsMap == null) { Map<String, ServiceOsSpecific> tmpMap = new TreeMap<>(); if (serviceOsSpecifics != null) { for (ServiceOsSpecific osSpecific : serviceOsSpecifics) { tmpMap.put(osSpecific.getOsFamily(), osSpecific); } } serviceOsSpecificsMap = tmpMap; } } } return serviceOsSpecificsMap; } public void setOsSpecifics(Map<String, ServiceOsSpecific> serviceOsSpecificsMap) { this.serviceOsSpecificsMap = serviceOsSpecificsMap; } public List<CustomCommandDefinition> getCustomCommands() { if (customCommands == null) { customCommands = new ArrayList<>(); } return customCommands; } public void setCustomCommands(List<CustomCommandDefinition> customCommands) { this.customCommands = customCommands; } public CommandScriptDefinition getCommandScript() { return commandScript; } public void setCommandScript(CommandScriptDefinition commandScript) { this.commandScript = commandScript; } /** * @param file the file containing the metrics definitions */ public void setMetricsFile(File file) { metricsFile = file; } /** * @return the metrics file, or <code>null</code> if none exists */ public File getMetricsFile() { return metricsFile; } /** * @return the metrics defined for this service */ public Map<String, Map<String, List<MetricDefinition>>> getMetrics() { return metrics; } /** * @param map the metrics for this service */ public void setMetrics(Map<String, Map<String, List<MetricDefinition>>> map) { metrics = map; } /** * @return the configuration directory name */ public String getConfigDir() { return configDir; } /** * @return whether the service is a monitoring service */ public Boolean isMonitoringService() { return monitoringService; } /** * @param monitoringService whether the service is a monitoring service */ public void setMonitoringService(Boolean monitoringService) { this.monitoringService = monitoringService; } /** * @param file the file containing the alert definitions */ public void setAlertsFile(File file) { alertsFile = file; } /** * @return the alerts file, or <code>null</code> if none exists */ public File getAlertsFile() { return alertsFile; } /** * @param file the file containing the alert definitions */ public void setKerberosDescriptorFile(File file) { kerberosDescriptorFile = file; } /** * @return the kerberos descriptor file, or <code>null</code> if none exists */ public File getKerberosDescriptorFile() { return kerberosDescriptorFile; } /** * @return the widgets descriptor file, or <code>null</code> if none exists */ public File getWidgetsDescriptorFile() { return widgetsDescriptorFile; } public void setWidgetsDescriptorFile(File widgetsDescriptorFile) { this.widgetsDescriptorFile = widgetsDescriptorFile; } public StackRoleCommandOrder getRoleCommandOrder() { return roleCommandOrder; } public void setRoleCommandOrder(StackRoleCommandOrder roleCommandOrder) { this.roleCommandOrder = roleCommandOrder; } /** * @return config types this service contains configuration for, but which are primarily related to another service */ public Set<String> getExcludedConfigTypes() { return excludedConfigTypes; } public void setExcludedConfigTypes(Set<String> excludedConfigTypes) { this.excludedConfigTypes = excludedConfigTypes; } //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; } /** * Determine whether or not a restart is required for this service after a host rack info change. * * @return true if a restart is required */ public Boolean isRestartRequiredAfterRackChange() { return restartRequiredAfterRackChange; } /** * Set indicator for required restart after a host rack info change. * * @param restartRequiredAfterRackChange true if a restart is required */ public void setRestartRequiredAfterRackChange(Boolean restartRequiredAfterRackChange) { this.restartRequiredAfterRackChange = restartRequiredAfterRackChange; } public String getThemesDir() { return themesDir; } public void setThemesDir(String themesDir) { this.themesDir = themesDir; } public List<ThemeInfo> getThemes() { return themes; } public void setThemes(List<ThemeInfo> themes) { this.themes = themes; } public Map<String, ThemeInfo> getThemesMap() { if (themesMap == null) { Map<String, ThemeInfo> tmp = new TreeMap<>(); if (themes != null) { for (ThemeInfo theme : themes) { tmp.put(theme.getFileName(), theme); } } themesMap = tmp; } return themesMap; } public void setThemesMap(Map<String, ThemeInfo> themesMap) { this.themesMap = themesMap; } //Quick links configurations public String getQuickLinksConfigurationsDir() { return quickLinksConfigurationsDir; } public void setQuickLinksConfigurationsDir(String quickLinksConfigurationsDir) { this.quickLinksConfigurationsDir = quickLinksConfigurationsDir; } public List<QuickLinksConfigurationInfo> getQuickLinksConfigurations() { return quickLinksConfigurations; } public void setQuickLinksConfigurations(List<QuickLinksConfigurationInfo> quickLinksConfigurations) { this.quickLinksConfigurations = quickLinksConfigurations; } public Map<String, QuickLinksConfigurationInfo> getQuickLinksConfigurationsMap() { if (quickLinksConfigurationsMap == null) { Map<String, QuickLinksConfigurationInfo> tmp = new TreeMap<>(); if (quickLinksConfigurations != null) { for (QuickLinksConfigurationInfo quickLinksConfiguration : quickLinksConfigurations) { tmp.put(quickLinksConfiguration.getFileName(), quickLinksConfiguration); } } quickLinksConfigurationsMap = tmp; } return quickLinksConfigurationsMap; } public void setQuickLinksConfigurationsMap(Map<String, QuickLinksConfigurationInfo> quickLinksConfigurationsMap) { this.quickLinksConfigurationsMap = quickLinksConfigurationsMap; } public List<ServicePropertyInfo> getServicePropertyList() { return servicePropertyList; } public void setServicePropertyList(List<ServicePropertyInfo> servicePropertyList) { this.servicePropertyList = servicePropertyList; afterServicePropertyListSet(); } private void afterServicePropertyListSet(){ validateServiceProperties(); buildServiceProperties(); } /** * Returns the service properties defined in the xml service definition. * @return Service property map */ public Map<String, String> getServiceProperties() { return servicePropertyMap; } /** * Constructs the map that stores the service properties defined in the xml service definition. * The keys are the property names and values the property values. * It ensures that missing required service properties are added with default values. */ private void buildServiceProperties() { if (isValid()) { Map<String, String> properties = Maps.newHashMap(); for (ServicePropertyInfo property : getServicePropertyList()) { properties.put(property.getName(), property.getValue()); } servicePropertyMap = ImmutableMap.copyOf(ensureMandatoryServiceProperties(properties)); } else servicePropertyMap = ImmutableMap.of(); } private Map<String, String> ensureMandatoryServiceProperties(Map<String, String> properties) { return ensureVisibilityServiceProperties(properties); } private Map<String, String> ensureVisibilityServiceProperties(Map<String, String> properties) { if (!properties.containsKey(DEFAULT_SERVICE_INSTALLABLE_PROPERTY.getKey())) properties.put(DEFAULT_SERVICE_INSTALLABLE_PROPERTY.getKey(), DEFAULT_SERVICE_INSTALLABLE_PROPERTY.getValue()); if (!properties.containsKey(DEFAULT_SERVICE_MANAGED_PROPERTY.getKey())) properties.put(DEFAULT_SERVICE_MANAGED_PROPERTY.getKey(), DEFAULT_SERVICE_MANAGED_PROPERTY.getValue()); if (!properties.containsKey(DEFAULT_SERVICE_MONITORED_PROPERTY.getKey())) properties.put(DEFAULT_SERVICE_MONITORED_PROPERTY.getKey(), DEFAULT_SERVICE_MONITORED_PROPERTY.getValue()); return properties; } void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { afterServicePropertyListSet(); } private void validateServiceProperties() { // Verify if there are duplicate service properties by name Multimap<String, ServicePropertyInfo> servicePropsByName = Multimaps.index( getServicePropertyList(), new Function<ServicePropertyInfo, String>() { @Override public String apply(ServicePropertyInfo servicePropertyInfo) { return servicePropertyInfo.getName(); } } ); for (String propertyName: servicePropsByName.keySet()) { if (servicePropsByName.get(propertyName).size() > 1) { setValid(false); addError("Duplicate service property with name '" + propertyName + "' found in " + getName() + ":" + getVersion() + " service definition !"); } } for (ComponentInfo component : getComponents()) { int primaryLogs = 0; for (LogDefinition log : component.getLogs()) { primaryLogs += log.isPrimary() ? 1 : 0; } if (primaryLogs > 1) { setValid(false); addError("More than one primary log exists for the component " + component.getName()); } } // validate credential store information if (credentialStoreInfo != null) { // if both are specified, supported must be true if enabled is false or true. if (credentialStoreInfo.isSupported() != null && credentialStoreInfo.isEnabled() != null) { if (!credentialStoreInfo.isSupported() && credentialStoreInfo.isEnabled()) { setValid(false); addError("Credential store cannot be enabled for service " + getName() + " as it does not support it."); } } // Must be specified if (credentialStoreInfo.isSupported() == null) { setValid(false); addError("Credential store supported is not specified for service " + getName()); } // Must be specified if (credentialStoreInfo.isEnabled() == null) { setValid(false); addError("Credential store enabled is not specified for service " + getName()); } } } public enum Selection { DEFAULT, TECH_PREVIEW, MANDATORY, DEPRECATED } }