package com.netflix.fabricator.archaius; import java.util.Iterator; import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import org.apache.commons.configuration.AbstractConfiguration; import org.apache.commons.lang.StringUtils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Joiner; import com.google.common.collect.Maps; import com.netflix.config.ConfigurationManager; import com.netflix.fabricator.ConfigurationNode; import com.netflix.fabricator.ComponentConfigurationResolver; import com.netflix.fabricator.TypeConfigurationResolver; import com.netflix.fabricator.jackson.JacksonComponentConfiguration; /** * Main configuration access using Archaius as the configuration source. * Configuration for type and key follows a specific naming convention * unless otherwise specified via a MapBinding of type (string) to a * specific ConfigurationFactory. * * The naming convention is * ${key}.${type} * * Where * 'key' is the unique id for the instance of the specified type * 'type' is the component interface type and not the type of a specified * implementation of the compoennt * * @author elandau * */ @Singleton public class ArchaiusTypeConfigurationResolver implements TypeConfigurationResolver { private static String DEFAULT_FORMAT_STRING = "%s.%s"; private static String TYPE_FIELD = "type"; // TODO: Inject this private AbstractConfiguration config = ConfigurationManager.getConfigInstance(); private final Map<String, ComponentConfigurationResolver> overrides; private final ObjectMapper mapper = new ObjectMapper(); @Inject public ArchaiusTypeConfigurationResolver(Map<String, ComponentConfigurationResolver> overrides) { if (overrides == null) { this.overrides = Maps.newHashMap(); } else { this.overrides = overrides; } } @Override public ComponentConfigurationResolver getConfigurationFactory(final String componentType) { ComponentConfigurationResolver factory = overrides.get(componentType); if (factory != null) return factory; return new ComponentConfigurationResolver() { @Override public ConfigurationNode getConfiguration(final String key) { String prefix = String.format(DEFAULT_FORMAT_STRING, key, componentType); if (config.containsKey(prefix)) { String json = Joiner.on(config.getListDelimiter()).join(config.getStringArray(prefix)).trim(); if (!json.isEmpty() && json.startsWith("{") && json.endsWith("}")) { try { JsonNode node = mapper.readTree(json); if (node.get(TYPE_FIELD) == null) throw new Exception("Missing 'type' field"); return new JacksonComponentConfiguration(key, node.get(TYPE_FIELD).asText(), node); } catch (Exception e) { throw new RuntimeException( String.format("Unable to parse json from '%s'. (%s)", prefix, StringUtils.abbreviate(json, 256)), e); } } } String typeField = Joiner.on(".").join(prefix, TYPE_FIELD); String typeValue = config.getString(typeField); if (componentType == null) { throw new RuntimeException(String.format("Type for '%s' not specified '%s'", typeField, componentType)); } return new ArchaiusComponentConfiguration( key, typeValue, config, prefix); } @Override public Map<String, ConfigurationNode> getAllConfigurations() { Map<String, ConfigurationNode> configs = Maps.newHashMap(); Iterator<String> keys = config.getKeys(); while (keys.hasNext()) { String key = keys.next(); String[] parts = StringUtils.split(key, "."); if (parts.length > 1) { String type = parts[1]; if (type.equals(componentType)) { String id = parts[0]; if (!configs.containsKey(id)) { configs.put(id, getConfiguration(id)); } } } } return configs; } }; } }