/*
* 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 gobblin.config.common.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import gobblin.config.store.api.ConfigKeyPath;
import gobblin.config.store.api.ConfigStore;
import gobblin.config.store.api.ConfigStoreWithBatchFetches;
import gobblin.config.store.api.ConfigStoreWithResolution;
/**
* ConfigStoreBackedValueInspector always query the underline {@link ConfigStore} to get the freshest
* {@link com.typesafe.config.Config}
* @author mitu
*
*/
public class ConfigStoreBackedValueInspector implements ConfigStoreValueInspector {
private final ConfigStore cs;
private final String version;
private final ConfigStoreTopologyInspector topology;
/**
* @param cs - internal {@link ConfigStore} to retrieve configuration
* @param version - version of the {@link ConfigStore}
* @param topology - corresponding {@link ConfigStoreTopologyInspector} for the input {@link ConfigStore}
*/
public ConfigStoreBackedValueInspector(ConfigStore cs, String version, ConfigStoreTopologyInspector topology) {
this.cs = cs;
this.version = version;
this.topology = topology;
}
public ConfigStore getConfigStore() {
return this.cs;
}
public String getVersion() {
return this.version;
}
/**
* {@inheritDoc}.
*
* <p>
* This implementation simply delegate the functionality to the internal {@link ConfigStore}/version
* </p>
*/
@Override
public Config getOwnConfig(ConfigKeyPath configKey) {
return this.cs.getOwnConfig(configKey, this.version);
}
/**
* {@inheritDoc}.
*
* <p>
* This implementation simply delegate the functionality to the internal {@link ConfigStore}/version
* if the internal {@link ConfigStore} is {@link ConfigStoreWithBatchFetches}, otherwise, will call
* configuration store for each config key path and put the result into {@link Map}
* </p>
*/
@Override
public Map<ConfigKeyPath, Config> getOwnConfigs(Collection<ConfigKeyPath> configKeys) {
if (this.cs instanceof ConfigStoreWithBatchFetches) {
ConfigStoreWithBatchFetches batchStore = (ConfigStoreWithBatchFetches) this.cs;
return batchStore.getOwnConfigs(configKeys, this.version);
}
Map<ConfigKeyPath, Config> result = new HashMap<>();
for (ConfigKeyPath configKey : configKeys) {
result.put(configKey, this.cs.getOwnConfig(configKey, this.version));
}
return result;
}
private Config getResolvedConfigRecursive(ConfigKeyPath configKey) {
if (this.cs instanceof ConfigStoreWithResolution) {
return ((ConfigStoreWithResolution) this.cs).getResolvedConfig(configKey, this.version);
}
/**
* currently use this function to check the circular dependency for the entire store, the result
* is NOT used
*
* root
* /
* l1 -> t1 (imports t1)
* /
* l2 -> t2 (imports t2)
*
* getImportsRecursively(l2) will return {t2,t1} as current implementation did NOT return the implicit
* imports ( l1 ), otherwise, there will be a lot of result for both getImportsRecursively and
* getImportedByRecursively
*
* if we use the result, the getResolvedConfig may equals
* l2.ownConfig withFallback t2.ownConfig withFallback t1.ownConfig withFallback l1.ownConfig
*
* but the correct result should be
* l2.ownConfig withFallback t2.ownConfig withFallback l1.ownConfig withFallback t1.ownConfig
*
* The wrong ordering for those is because of we did NOT include the implicit imports l1
*/
this.topology.getImportsRecursively(configKey);
Config initialConfig = this.getOwnConfig(configKey);
if (configKey.isRootPath()) {
return initialConfig;
}
List<ConfigKeyPath> ownImports = this.topology.getOwnImports(configKey);
// merge with other configs from imports
if (ownImports != null) {
for (ConfigKeyPath p : ownImports) {
initialConfig = initialConfig.withFallback(this.getResolvedConfigRecursive(p));
}
}
// merge with configs from parent for Non root
initialConfig = initialConfig.withFallback(this.getResolvedConfigRecursive(configKey.getParent()));
return initialConfig;
}
/**
* {@inheritDoc}.
*
* <p>
* This implementation simply delegate the functionality to the internal {@link ConfigStore}/version if
* the internal {@link ConfigStore} is {@link ConfigStoreWithResolution}, otherwise based on {@link ConfigStoreTopologyInspector}
*
* 1. find out all the imports recursively
* 2. resolved the config on the fly
* </p>
*/
@Override
public Config getResolvedConfig(ConfigKeyPath configKey) {
return getResolvedConfigRecursive(configKey).withFallback(ConfigFactory.defaultOverrides())
.withFallback(ConfigFactory.systemEnvironment()).resolve();
}
/**
* {@inheritDoc}.
*
* <p>
* This implementation simply delegate the functionality to the internal {@link ConfigStore}/version
* if the internal {@link ConfigStore} is {@link ConfigStoreWithBatchFetches}, otherwise, will call
* configuration store for each config key path and put the result into {@link Map}
* </p>
*/
@Override
public Map<ConfigKeyPath, Config> getResolvedConfigs(Collection<ConfigKeyPath> configKeys) {
if (this.cs instanceof ConfigStoreWithBatchFetches) {
ConfigStoreWithBatchFetches batchStore = (ConfigStoreWithBatchFetches) this.cs;
return batchStore.getResolvedConfigs(configKeys, this.version);
}
Map<ConfigKeyPath, Config> result = new HashMap<>();
for (ConfigKeyPath configKey : configKeys) {
result.put(configKey, this.getResolvedConfig(configKey));
}
return result;
}
}