All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.archaius.persisted2.JsonPersistedV2Reader Maven / Gradle / Ivy

There is a newer version: 2.8.3
Show newest version
package com.netflix.archaius.persisted2;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.archaius.config.polling.PollingResponse;

/**
 * Reader for Netflix persisted properties (not yet available in OSS).  
 * 
 * Properties are read as a single JSON blob that contains a full list of properties.
 * Multiple property values may exist for different scopes and are resolved into 
 * a single property value using a ProperyValueResolver.
 * 
 * Example,
 * 
 * 
 * {@code 
 *  JsonPersistedV2Reader reader =
 *      JsonPersistedV2Reader.builder(new HTTPStreamLoader(url))     // url from which config is fetched
 *          .withPredicate(ScopePredicates.fromMap(instanceScopes))  // Map of scope values for the running instance
 *          .build();
 *          
 *  appConfig.addConfigFirst(new PollingDynamicConfig("dyn", reader, new FixedPollingStrategy(30, TimeUnit.SECONDS)));
 *
 * }
 * 
* * @author elandau * */ public class JsonPersistedV2Reader implements Callable { private static final Logger LOG = LoggerFactory.getLogger(JsonPersistedV2Reader.class); private final static List DEFAULT_ORDERED_SCOPES = Arrays.asList("serverId", "asg", "ami", "cluster", "appId", "env", "countries", "stack", "zone", "region"); private final static String DEFAULT_KEY_FIELD = "key"; private final static String DEFAULT_VALUE_FIELD = "value"; private final static List DEFAULT_PATH = Arrays.asList("persistedproperties", "properties", "property"); public static class Builder { private final Callable reader; private List path = DEFAULT_PATH; private List scopeFields = DEFAULT_ORDERED_SCOPES; private String keyField = DEFAULT_KEY_FIELD; private String valueField = DEFAULT_VALUE_FIELD; private ScopePredicate predicate = ScopePredicates.alwaysTrue(); private ScopedValueResolver resolver = new ScopePriorityPropertyValueResolver(); public Builder(Callable reader) { this.reader = reader; } public Builder withPath(String path) { return withPath(Arrays.asList(StringUtils.split(path, "/"))); } public Builder withPath(List path) { List copy = new ArrayList(); copy.addAll(path); this.path = Collections.unmodifiableList(copy); return this; } public Builder withScopes(List scopes) { this.scopeFields = scopes; return this; } public Builder withPredicate(ScopePredicate predicate) { this.predicate = predicate; return this; } public Builder withKeyField(String keyField) { this.keyField = keyField; return this; } public Builder withValueField(String valueField) { this.valueField = valueField; return this; } public Builder withValueResolver(ScopedValueResolver resolver) { this.resolver = resolver; return this; } public JsonPersistedV2Reader build() { return new JsonPersistedV2Reader(this); } } public static Builder builder(Callable reader) { return new Builder(reader); } private final Callable reader; private final ScopePredicate predicate; private final ScopedValueResolver valueResolver; private final ObjectMapper mapper = new ObjectMapper(); private final List scopeFields; private final String keyField; private final String valueField; private final List path; private JsonPersistedV2Reader(Builder builder) { this.reader = builder.reader; this.predicate = builder.predicate; this.valueResolver = builder.resolver; this.keyField = builder.keyField; this.valueField = builder.valueField; this.scopeFields = builder.scopeFields; this.path = builder.path; } @Override public PollingResponse call() throws Exception { Map> props = new HashMap>(); InputStream is = reader.call(); if (is == null) { return PollingResponse.noop(); } try { JsonNode node = mapper.readTree(is); for (String part : this.path) { node = node.path(part); } for (final JsonNode property : node) { String key = null; try { key = property.get(keyField).asText(); String value = property.has(valueField) ? property.get(valueField).asText() : ""; LinkedHashMap> scopes = new LinkedHashMap>(); for (String scope : this.scopeFields) { String[] values = StringUtils.splitByWholeSeparator(property.has(scope) ? property.get(scope).asText().toLowerCase() : "", ","); scopes.put(scope, values.length == 0 ? Collections.emptySet() : immutableSetFrom(values)); } // Filter out scopes that don't match at all if (!this.predicate.evaluate(scopes)) { continue; } // Build up a list of valid scopes List variations = props.get(key); if (variations == null) { variations = new ArrayList(); props.put(key, variations); } variations.add(new ScopedValue(value, scopes)); } catch (Exception e) { LOG.warn("Unable to process property '{}'", key); } } } finally { try { is.close(); } catch (Exception e) { // OK to ignore } } // Resolve to a single property value final Map result = new HashMap(); for (Entry> entry : props.entrySet()) { result.put(entry.getKey(), valueResolver.resolve(entry.getKey(), entry.getValue())); } return PollingResponse.forSnapshot(result); } private static Set immutableSetFrom(String[] values) { if (values.length == 0) { return Collections.emptySet(); } else { HashSet set = new HashSet(); set.addAll(Arrays.asList(values)); return Collections.unmodifiableSet(set); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy