/**
* 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.Map;
/**
* Creates a configuration instance from user provided properties.
* Supports both forms of configuration syntax.
* todo: document both forms here
*/
public class ConfigurationFactory {
private static final String SCHEMA_IS_NOT_SUPPORTED_MESSAGE =
"Provided configuration format is not supported";
public Configuration getConfiguration(Collection<Map<String, String>> configProperties) {
Map<String, Map<String, String>> properties = new HashMap<>();
Map<String, Map<String, Map<String, String>>> attributes = new HashMap<>();
Configuration configuration = new Configuration(properties, attributes);
if (configProperties != null) {
for (Map<String, String> typeMap : configProperties) {
//todo: can we have a different strategy for each type?
ConfigurationStrategy strategy = decidePopulationStrategy(typeMap);
for (Map.Entry<String, String> entry : typeMap.entrySet()) {
String[] propertyNameTokens = entry.getKey().split("/");
strategy.setConfiguration(configuration, propertyNameTokens, entry.getValue());
}
}
}
return configuration;
}
private ConfigurationStrategy decidePopulationStrategy(Map<String, String> configuration) {
if (configuration != null && !configuration.isEmpty()) {
String keyEntry = configuration.keySet().iterator().next();
String[] keyNameTokens = keyEntry.split("/");
int levels = keyNameTokens.length;
String propertiesType = keyNameTokens[1];
if (levels == 2) {
return new ConfigurationStrategyV1();
} else if ((levels == 3 && BlueprintFactory.PROPERTIES_PROPERTY_ID.equals(propertiesType))
|| (levels == 4 && BlueprintFactory.PROPERTIES_ATTRIBUTES_PROPERTY_ID.equals(propertiesType))) {
return new ConfigurationStrategyV2();
} else {
throw new IllegalArgumentException(SCHEMA_IS_NOT_SUPPORTED_MESSAGE);
}
} else {
return new ConfigurationStrategyV2();
}
}
/**
* The structure of blueprints is evolving where multiple resource
* structures are to be supported. This class abstracts the population
* of configurations which have changed from a map of key-value strings,
* to an map containing 'properties' and 'properties_attributes' maps.
*
* Extending classes can determine how they want to populate the
* configuration maps depending on input.
*/
private static abstract class ConfigurationStrategy {
protected abstract void setConfiguration(Configuration configuration,
String[] propertyNameTokens,
String propertyValue);
}
/**
* Original blueprint configuration format where configs were a map
* of strings.
*/
protected static class ConfigurationStrategyV1 extends ConfigurationStrategy {
@Override
protected void setConfiguration(Configuration configuration, String[] propertyNameTokens, String propertyValue) {
configuration.setProperty(propertyNameTokens[0], propertyNameTokens[1], propertyValue);
}
}
/**
* New blueprint configuration format where configs are a map from 'properties' and
* 'properties_attributes' to a map of strings.
*
* @since 1.7.0
*/
protected static class ConfigurationStrategyV2 extends ConfigurationStrategy {
@Override
protected void setConfiguration(Configuration configuration, String[] propertyNameTokens, String propertyValue) {
String type = propertyNameTokens[0];
if (BlueprintFactory.PROPERTIES_PROPERTY_ID.equals(propertyNameTokens[1])) {
configuration.setProperty(type, propertyNameTokens[2], propertyValue);
} else if (BlueprintFactory.PROPERTIES_ATTRIBUTES_PROPERTY_ID.equals(propertyNameTokens[1])) {
configuration.setAttribute(type, propertyNameTokens[3], propertyNameTokens[2], propertyValue);
}
}
}
}