package org.datadog.jmxfetch; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map.Entry; import java.util.Set; public class Configuration { private LinkedHashMap<String, Object> conf; private Filter include; private Filter exclude; /** * Access configuration elements more easily * * Also provides helper methods to extract common information among filters. */ public Configuration(LinkedHashMap<String, Object> conf) { this.conf = conf; this.include = new Filter(conf.get("include")); this.exclude = new Filter(conf.get("exclude")); } public LinkedHashMap<String, Object> getConf() { return conf; } public Filter getInclude() { return include; } public Filter getExclude() { return exclude; } public String toString() { return "include: " + this.include + " - exclude: " + this.exclude; } private Boolean hasInclude(){ return getInclude() != null; } /** * Filter a configuration list to keep the ones with `include` filters. * * @param configurationList the configuration list to filter * * @return a configuration list */ private static LinkedList<Configuration> getIncludeConfigurationList(LinkedList<Configuration> configurationList){ LinkedList<Configuration> includeConfigList = new LinkedList<Configuration>(configurationList); Iterator<Configuration> confItr = includeConfigList.iterator(); while(confItr.hasNext()) { Configuration conf = confItr.next(); if (!conf.hasInclude()) { confItr.remove(); } } return includeConfigList; } /** * Extract `include` filters from the configuration list and index then by domain name. * * @param configurationList the configuration list to process * * @return filters by domain name */ private static HashMap<String, LinkedList<Filter>> getIncludeFiltersByDomain(LinkedList<Configuration> configurationList){ HashMap<String, LinkedList<Filter>> includeFiltersByDomain = new HashMap<String, LinkedList<Filter>>(); for (Configuration conf : configurationList) { Filter filter = conf.getInclude(); LinkedList<Filter> filters = new LinkedList<Filter>(); // Convert bean name, to a proper filter, i.e. a hash if (!filter.isEmptyBeanName()) { ArrayList<String> beanNames = filter.getBeanNames(); for (String beanName : beanNames) { String[] splitBeanName = beanName.split(":"); String domain = splitBeanName[0]; String rawBeanParameters = splitBeanName[1]; HashMap<String, String> beanParametersHash = JMXAttribute.getBeanParametersHash(rawBeanParameters); beanParametersHash.put("domain", domain); filters.add(new Filter(beanParametersHash)); } } else { filters.add(filter); } for (Filter f: filters) { // Retrieve the existing filters for the domain, add the new filters LinkedList<Filter> domainFilters; String domainName = f.getDomain(); if (includeFiltersByDomain.containsKey(domainName)) { domainFilters = includeFiltersByDomain.get(domainName); } else { domainFilters = new LinkedList<Filter>(); } domainFilters.add(f); includeFiltersByDomain.put(domainName, domainFilters); } } return includeFiltersByDomain; } /** * Extract, among filters, bean key parameters in common. * * @param filtersByDomain filters by domain name * * @return common bean key parameters by domain name */ private static HashMap<String, Set<String>> getCommonBeanKeysByDomain(HashMap<String, LinkedList<Filter>> filtersByDomain){ HashMap<String, Set<String>> beanKeysIntersectionByDomain = new HashMap<String,Set<String>>(); for (Entry<String, LinkedList<Filter>> filtersEntry : filtersByDomain.entrySet()) { String domainName = filtersEntry.getKey(); LinkedList<Filter> mFilters= filtersEntry.getValue(); // Compute keys intersection Set<String> keysIntersection = new HashSet<String>(mFilters.getFirst().keySet()); for (Filter f: mFilters) { keysIntersection.retainAll(f.keySet()); } // Remove special parameters for(String param : JMXAttribute.getExcludedBeanParams()){ keysIntersection.remove(param); } beanKeysIntersectionByDomain.put(domainName, keysIntersection); } return beanKeysIntersectionByDomain; } /** * Build a map of common bean keys->values, with the specified bean keys, among the given filters. * * @param beanKeysByDomain bean keys by domain name * @param filtersByDomain filters by domain name * * @return bean pattern (keys->values) by domain name */ private static HashMap<String, LinkedHashMap<String, String>> getCommonScopeByDomain(HashMap<String, Set<String>> beanKeysByDomain, HashMap<String, LinkedList<Filter>> filtersByDomain){ // Compute a common scope a among filters by domain name HashMap<String, LinkedHashMap<String, String>> commonScopeByDomain = new HashMap<String, LinkedHashMap<String, String>>(); for (Entry<String, Set<String>> commonParametersByDomainEntry : beanKeysByDomain.entrySet()) { String domainName = commonParametersByDomainEntry.getKey(); Set<String> commonParameters = commonParametersByDomainEntry.getValue(); LinkedList<Filter> filters = filtersByDomain.get(domainName); LinkedHashMap<String, String> commonScope = new LinkedHashMap<String, String>(); for (String parameter : commonParameters) { // Check if all values associated with the parameters are the same String commonValue = null; Boolean hasCommonValue = true; for (Filter f : filters) { ArrayList<String> parameterValues = f.getParameterValues(parameter); if (parameterValues.size() != 1 || (commonValue != null && !commonValue.equals(parameterValues.get(0)))) { hasCommonValue = false; break; } commonValue = parameterValues.get(0); } if (hasCommonValue) { commonScope.put(parameter, commonValue); } } commonScopeByDomain.put(domainName, commonScope); } return commonScopeByDomain; } /** * Stringify a bean pattern. * * @param domain domain name * @param beanScope map of bean keys-> values * * @return string pattern identifying the bean scope */ private static String beanScopeToString(String domain, LinkedHashMap<String, String> beanScope){ String result = ""; // Domain domain = (domain != null) ? domain : "*"; result += domain + ":"; // Scope parameters for (Entry<String, String> beanScopeEntry : beanScope.entrySet()) { String param = beanScopeEntry.getKey(); String value = beanScopeEntry.getValue(); result += param + "=" + value + ","; } result += "*"; return result; } /** * Find, among the configuration list, a potential common bean pattern by domain name. * * @param configurationList the configuration list to process * * @return common bean pattern strings */ public static LinkedList<String> getGreatestCommonScopes(LinkedList<Configuration> configurationList){ LinkedList<Configuration> includeConfigList = getIncludeConfigurationList(configurationList); HashMap<String, LinkedList<Filter>> includeFiltersByDomain = getIncludeFiltersByDomain(includeConfigList); HashMap<String, Set<String>> parametersIntersectionByDomain = getCommonBeanKeysByDomain(includeFiltersByDomain); HashMap<String, LinkedHashMap<String, String>> commonBeanScopeByDomain = getCommonScopeByDomain(parametersIntersectionByDomain, includeFiltersByDomain); LinkedList<String> result = new LinkedList<String>(); for (Entry<String, LinkedHashMap<String, String>> beanScopeEntry: commonBeanScopeByDomain.entrySet()) { String domain = beanScopeEntry.getKey(); LinkedHashMap<String, String> beanScope = beanScopeEntry.getValue(); result.add(beanScopeToString(domain, beanScope)); } return result; } }