org.apache.ambari.server.state.ServiceInfo.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ambari.server.state.ServiceInfo.java

Source

/**
 * 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 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;
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.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.map.annotate.JsonFilter;

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 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;

@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;

    @XmlTransient
    private List<PropertyInfo> properties;

    @XmlElementWrapper(name = "components")
    @XmlElements(@XmlElement(name = "component"))
    private List<ComponentInfo> components;

    @XmlElement(name = "deleted")
    private boolean isDeleted = false;

    @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<String>();

    @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;

    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 alertsFile = null;

    @XmlTransient
    private File kerberosDescriptorFile = null;

    @XmlTransient
    private File widgetsDescriptorFile = null;

    @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<String>();

    @Override
    public void setErrors(String error) {
        errorSet.add(error);
    }

    @Override
    public Collection getErrors() {
        return errorSet;
    }

    @Override
    public void setErrors(Collection error) {
        this.errorSet.addAll(error);
    }

    /**
     * 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<String>();

    /**
     * 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;

    public boolean isDeleted() {
        return isDeleted;
    }

    public void setDeleted(boolean deleted) {
        isDeleted = deleted;
    }

    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 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<PropertyInfo>();
        return properties;
    }

    public List<ComponentInfo> getComponents() {
        if (components == null)
            components = new ArrayList<ComponentInfo>();
        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;
    }

    @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<String, Map<String, Map<String, String>>>();
        }
        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<String, Map<String, Map<String, String>>>();
        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<String, Set<String>>();

                    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<String>();
        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;
    }

    /**
     * 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<String, ServiceOsSpecific>();
                    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<CustomCommandDefinition>();
        }
        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;
    }

    /**
     * @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<String, PropertyInfo>();
                    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) {
            synchronized (this) {
            }
            if (themesMap == null) {
                Map<String, ThemeInfo> tmp = new TreeMap<String, ThemeInfo>();
                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) {
            synchronized (this) {
            }
            if (quickLinksConfigurationsMap == null) {
                Map<String, QuickLinksConfigurationInfo> tmp = new TreeMap<String, QuickLinksConfigurationInfo>();
                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);
                setErrors("Duplicate service property with name '" + propertyName + "' found in " + getName() + ":"
                        + getVersion() + " service definition !");
            }
        }
    }

}