summaryrefslogtreecommitdiffstats
path: root/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl
diff options
context:
space:
mode:
Diffstat (limited to 'common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl')
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AgglomerateConfiguration.java33
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AggregateConfiguration.java172
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/CliConfigurationImpl.java354
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationChangeNotifier.java474
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationDataSource.java105
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationFilter.java39
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationImpl.java679
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationRepository.java415
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ContextListener.java28
-rw-r--r--common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/YamlConfiguration.java18
10 files changed, 2317 insertions, 0 deletions
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AgglomerateConfiguration.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AgglomerateConfiguration.java
new file mode 100644
index 0000000000..0934608c93
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AgglomerateConfiguration.java
@@ -0,0 +1,33 @@
+package org.onap.config.impl;
+
+import org.apache.commons.configuration2.DatabaseConfiguration;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * The type Agglomerate configuration.
+ */
+public class AgglomerateConfiguration extends DatabaseConfiguration {
+
+ private final Map<String, Object> store =
+ Collections.synchronizedMap(new WeakHashMap<String, Object>());
+
+ /**
+ * Gets property value.
+ *
+ * @param key the key
+ * @return the property value
+ */
+ public Object getPropertyValue(String key) {
+ Object objToReturn;
+ objToReturn = store.get(key);
+ if (objToReturn == null && !store.containsKey(key)) {
+ objToReturn = super.getProperty(key);
+ store.put(key, objToReturn);
+ }
+ return objToReturn;
+ }
+
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AggregateConfiguration.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AggregateConfiguration.java
new file mode 100644
index 0000000000..c8d76d9bd9
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/AggregateConfiguration.java
@@ -0,0 +1,172 @@
+package org.onap.config.impl;
+
+import org.apache.commons.configuration2.CombinedConfiguration;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.tree.MergeCombiner;
+import org.apache.commons.configuration2.tree.OverrideCombiner;
+import org.apache.commons.configuration2.tree.UnionCombiner;
+import static org.onap.config.Constants.LOAD_ORDER_KEY;
+import org.onap.config.ConfigurationUtils;
+import org.onap.config.type.ConfigurationMode;
+
+import java.io.File;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.*;
+
+/**
+ * The type Aggregate configuration.
+ */
+public final class AggregateConfiguration {
+
+ private Map<String, Configuration> rootConfig = new HashMap<>();
+ private Map<String, Configuration> unionConfig = new HashMap<>();
+ private Map<String, Configuration> mergeConfig = new HashMap<>();
+ private Map<String, Configuration> overrideConfig = new LinkedHashMap<>();
+
+ /**
+ * Instantiates a new Aggregate configuration.
+ */
+ public AggregateConfiguration() {
+ try {
+ Class clazz = Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
+ if (!clazz.getCanonicalName()
+ .equals(ConfigurationImpl.class.getCanonicalName())) {
+ throw new RuntimeException("Illegal access.");
+ }
+ } catch (ClassNotFoundException cfe) {
+ throw new RuntimeException("Class not found while loading change notifier");
+ }
+ }
+
+ private void addConfig(String path, ConfigurationMode configMode, Configuration config) {
+ if (configMode != null) {
+ switch (configMode) {
+ case MERGE:
+ mergeConfig.put(path, config);
+ break;
+ case OVERRIDE:
+ overrideConfig.put(path, config);
+ break;
+ case UNION:
+ unionConfig.put(path, config);
+ break;
+ default:
+ }
+ } else {
+ rootConfig.put(path, config);
+ }
+ }
+
+ /**
+ * Add config.
+ *
+ * @param file the file
+ * @throws Exception the exception
+ */
+ public void addConfig(File file) throws Exception {
+ addConfig(file.getAbsolutePath().toUpperCase(), ConfigurationUtils.getMergeStrategy(file),
+ ConfigurationUtils.getConfigurationBuilder(file, false).getConfiguration());
+ }
+
+ /**
+ * Add config.
+ *
+ * @param url the url
+ * @throws Exception the exception
+ */
+ public void addConfig(URL url) throws Exception {
+ addConfig(url.getFile().toUpperCase(), ConfigurationUtils.getMergeStrategy(url),
+ ConfigurationUtils.getConfigurationBuilder(url).getConfiguration());
+ }
+
+ /**
+ * Remove config.
+ *
+ * @param file the file
+ */
+ public void removeConfig(File file) {
+ String key = file.getAbsolutePath().toUpperCase();
+ if (rootConfig.containsKey(key)) {
+ rootConfig.remove(key);
+ } else if (mergeConfig.containsKey(key)) {
+ mergeConfig.remove(key);
+ } else if (unionConfig.containsKey(key)) {
+ unionConfig.remove(key);
+ } else if (overrideConfig.containsKey(key)) {
+ overrideConfig.remove(key);
+ }
+ }
+
+ /**
+ * Contains config boolean.
+ *
+ * @param file the file
+ * @return the boolean
+ */
+ public boolean containsConfig(File file) {
+ String key = file.getAbsolutePath().toUpperCase();
+ return rootConfig.containsKey(key) || mergeConfig.containsKey(key)
+ || unionConfig.containsKey(key) || overrideConfig.containsKey(key);
+ }
+
+ /**
+ * Gets final configuration.
+ *
+ * @return the final configuration
+ */
+ public Configuration getFinalConfiguration() {
+ CombinedConfiguration ccRoot = new CombinedConfiguration(new MergeCombiner());
+ ArrayList<Configuration> tempList = new ArrayList<>(rootConfig.values());
+ Collections.sort(tempList, this::sortForMerge);
+ for (Configuration conf : tempList) {
+ ccRoot.addConfiguration(conf);
+ }
+ CombinedConfiguration ccMergeRoot = new CombinedConfiguration(new MergeCombiner());
+ ccMergeRoot.addConfiguration(ccRoot);
+ tempList = new ArrayList<>(mergeConfig.values());
+ Collections.sort(tempList, this::sortForMerge);
+ for (Configuration conf : tempList) {
+ ccMergeRoot.addConfiguration(conf);
+ }
+ CombinedConfiguration ccUnionRoot = new CombinedConfiguration(new UnionCombiner());
+ ccUnionRoot.addConfiguration(ccMergeRoot);
+ for (Configuration conf : unionConfig.values()) {
+ ccUnionRoot.addConfiguration(conf);
+ }
+ ArrayList<Configuration> tempOverrideConfigs = new ArrayList<>(overrideConfig.values());
+ Collections.reverse(tempOverrideConfigs);
+ Collections.sort(tempOverrideConfigs, this::sortForOverride);
+ CombinedConfiguration ccOverrideRoot = new CombinedConfiguration(new OverrideCombiner());
+ for (Configuration conf : tempOverrideConfigs) {
+ ccOverrideRoot.addConfiguration(conf);
+ }
+ ccOverrideRoot.addConfiguration(ccUnionRoot);
+ return ccOverrideRoot;
+ }
+
+ private int sortForOverride(Configuration conf1, Configuration conf2){
+ String order1 = conf1.getString(LOAD_ORDER_KEY);
+ String order2 = conf2.getString(LOAD_ORDER_KEY);
+ if (ConfigurationUtils.isBlank(order1) || !order1.trim().matches("\\d+")){
+ order1 = "0";
+ }
+ if (ConfigurationUtils.isBlank(order2) || !order2.trim().matches("\\d+")){
+ order2 = "0";
+ }
+ return Integer.parseInt(order2.trim())-Integer.parseInt(order1.trim());
+ }
+
+ private int sortForMerge(Configuration conf1, Configuration conf2){
+ String order1 = conf1.getString(LOAD_ORDER_KEY);
+ String order2 = conf2.getString(LOAD_ORDER_KEY);
+ if (ConfigurationUtils.isBlank(order1) || !order1.trim().matches("\\d+")){
+ order1 = "0";
+ }
+ if (ConfigurationUtils.isBlank(order2) || !order2.trim().matches("\\d+")){
+ order2 = "0";
+ }
+ return Integer.parseInt(order1.trim())-Integer.parseInt(order2.trim());
+ }
+
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/CliConfigurationImpl.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/CliConfigurationImpl.java
new file mode 100644
index 0000000000..3bdebffb48
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/CliConfigurationImpl.java
@@ -0,0 +1,354 @@
+package org.onap.config.impl;
+
+import static org.onap.config.Constants.DB_NAMESPACE;
+import static org.onap.config.Constants.DEFAULT_NAMESPACE;
+import static org.onap.config.Constants.DEFAULT_TENANT;
+import static org.onap.config.Constants.KEY_ELEMENTS_DELEMETER;
+import static org.onap.config.Constants.LOAD_ORDER_KEY;
+import static org.onap.config.Constants.MBEAN_NAME;
+import static org.onap.config.Constants.MODE_KEY;
+import static org.onap.config.Constants.NAMESPACE_KEY;
+
+import org.apache.commons.configuration2.CombinedConfiguration;
+import org.apache.commons.configuration2.CompositeConfiguration;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.FileBasedConfiguration;
+import org.apache.commons.configuration2.PropertiesConfiguration;
+import org.onap.config.type.ConfigurationQuery;
+import org.onap.config.ConfigurationUtils;
+import org.onap.config.Constants;
+import org.onap.config.api.ConfigurationManager;
+import org.onap.config.api.Hint;
+import org.onap.config.type.ConfigurationUpdate;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+/**
+ * The type Cli configuration.
+ */
+public final class CliConfigurationImpl extends ConfigurationImpl implements ConfigurationManager {
+
+ /**
+ * Instantiates a new Cli configuration.
+ *
+ * @throws Exception the exception
+ */
+ public CliConfigurationImpl() throws Exception {
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ ObjectName name = new ObjectName(MBEAN_NAME);
+ if (mbs.isRegistered(name)) {
+ mbs.unregisterMBean(name);
+ }
+ mbs.registerMBean(new StandardMBean(this, ConfigurationManager.class), name);
+ //mbs.registerMBean(getMBean(), name);
+ mbs.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, this::handleNotification, null,
+ null);
+ //mbs.addNotificationListener(name, this::handleNotification, null, null);
+ }
+
+
+ /**
+ * Handle notification.
+ *
+ * @param notification the notification
+ * @param handback the handback
+ */
+ public void handleNotification(Notification notification, Object handback) {
+ if (notification instanceof MBeanServerNotification) {
+ MBeanServerNotification mbs = (MBeanServerNotification) notification;
+ if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(mbs.getType())) {
+ try {
+ String mbean =
+ ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, DB_NAMESPACE)
+ .getString("shutdown.mbean");
+ if (mbs.getMBeanName()
+ .equals(mbean == null ? new ObjectName(MBEAN_NAME) : new ObjectName(mbean))) {
+ changeNotifier.shutdown();
+ ConfigurationDataSource.lookup().close();
+ }
+ } catch (Exception exception) {
+ //do nothing.
+ }
+ } else if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(mbs.getType())) {
+ mbs.getMBeanName();
+ }
+ }
+ }
+
+ public String getConfigurationValue(Map<String, Object> input) {
+ return getConfigurationValue((ConfigurationQuery) getInput(input));
+ }
+
+ private String getConfigurationValue(ConfigurationQuery queryData) {
+ try {
+ if (queryData.isFallback()) {
+ return ConfigurationUtils.getCommaSeparatedList(
+ get(queryData.getTenant(), queryData.getNamespace(), queryData.getKey(), String[].class,
+ queryData.isLatest() ? Hint.LATEST_LOOKUP : Hint.DEFAULT,
+ queryData.isExternalLookup() ? Hint.EXTERNAL_LOOKUP : Hint.DEFAULT,
+ queryData.isNodeSpecific() ? Hint.NODE_SPECIFIC : Hint.DEFAULT));
+ } else {
+ String[] list =
+ getInternal(queryData.getTenant(), queryData.getNamespace(), queryData.getKey(),
+ String[].class, queryData.isLatest() ? Hint.LATEST_LOOKUP : Hint.DEFAULT,
+ queryData.isExternalLookup() ? Hint.EXTERNAL_LOOKUP : Hint.DEFAULT,
+ queryData.isNodeSpecific() ? Hint.NODE_SPECIFIC : Hint.DEFAULT);
+ return ConfigurationUtils
+ .getCommaSeparatedList(list == null ? Arrays.asList() : Arrays.asList(list));
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ return null;
+ }
+
+ public void updateConfigurationValue(Map<String, Object> input) {
+ updateConfigurationValue((ConfigurationUpdate) getInput(input));
+ }
+
+ private void updateConfigurationValue(ConfigurationUpdate updateData) {
+
+ try {
+ if (!ConfigurationRepository.lookup().isValidTenant(updateData.getTenant())) {
+ throw new RuntimeException("Invalid tenantId.");
+ }
+ if (!ConfigurationRepository.lookup().isValidNamespace(updateData.getNamespace())) {
+ throw new RuntimeException("Invalid Namespace.");
+ }
+ } catch (NullPointerException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ try {
+ boolean keyPresent =
+ isKeyExists(updateData.getTenant(), updateData.getNamespace(), updateData.getKey());
+ if (keyPresent) {
+ boolean isUpdated = false;
+ Object[] paramArray = new Object[]{
+ updateData.getTenant() + KEY_ELEMENTS_DELEMETER + updateData.getNamespace(),
+ new Long(System.currentTimeMillis()), updateData.getKey(),
+ getConfigurationValue(updateData), updateData.getValue()};
+ Configuration config = ConfigurationRepository.lookup()
+ .getConfigurationFor(updateData.getTenant(), updateData.getNamespace());
+ if (config instanceof AgglomerateConfiguration || config instanceof CombinedConfiguration) {
+ CompositeConfiguration cc = new CompositeConfiguration();
+ cc.addConfiguration(config);
+ config = cc;
+ }
+ CompositeConfiguration configuration = (CompositeConfiguration) config;
+ int overrideIndex = -1;
+ for (int i = 0; i < configuration.getNumberOfConfigurations(); i++) {
+ if (!updateData.isNodeOverride()
+ && (configuration.getConfiguration(i) instanceof AgglomerateConfiguration
+ || configuration.getConfiguration(i) instanceof CombinedConfiguration)) {
+ configuration.getConfiguration(i)
+ .setProperty(updateData.getKey(), updateData.getValue());
+ isUpdated = true;
+ break;
+ } else if (updateData.isNodeOverride()
+ && configuration.getConfiguration(i) instanceof FileBasedConfiguration) {
+ configuration.getConfiguration(i)
+ .setProperty(updateData.getKey(), updateData.getValue());
+ isUpdated = true;
+ overrideIndex = i;
+ break;
+ }
+ }
+ if (!isUpdated) {
+ if (updateData.isNodeOverride()) {
+ PropertiesConfiguration pc = new PropertiesConfiguration();
+ pc.setProperty(NAMESPACE_KEY,
+ updateData.getTenant() + Constants.TENANT_NAMESPACE_SAPERATOR
+ + updateData.getNamespace());
+ pc.setProperty(MODE_KEY, "OVERRIDE");
+ pc.setProperty(updateData.getKey(), updateData.getValue());
+ if (System.getProperty("node.config.location") != null
+ && System.getProperty("node.config.location").trim().length() > 0) {
+ File file = new File(System.getProperty("node.config.location"),
+ updateData.getTenant() + File.separator + updateData.getNamespace()
+ + File.separator + "config.properties");
+ file.getParentFile().mkdirs();
+ PrintWriter out = new PrintWriter(file);
+ pc.write(out);
+ out.close();
+ ConfigurationRepository.lookup().populateOverrideConfigurtaion(
+ updateData.getTenant() + KEY_ELEMENTS_DELEMETER + updateData.getNamespace(),
+ file);
+ }
+ } else {
+ configuration.getConfiguration(0)
+ .setProperty(updateData.getKey(), updateData.getValue());
+ }
+ }
+ if (!updateData.isNodeOverride()) {
+ ConfigurationUtils.executeInsertSql(
+ ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, DB_NAMESPACE)
+ .getString("insertconfigurationchangecql"), paramArray);
+ } else {
+ ConfigurationRepository.lookup().refreshOverrideConfigurtaionFor(
+ updateData.getTenant() + KEY_ELEMENTS_DELEMETER + updateData.getNamespace(),
+ overrideIndex);
+ }
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ private boolean isKeyExists(String tenant, String namespace, String key) {
+ boolean keyExist = false;
+ try {
+ keyExist =
+ ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace).containsKey(key);
+ if (!keyExist && !DEFAULT_TENANT.equals(tenant)) {
+ keyExist = ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, namespace)
+ .containsKey(key);
+ }
+ if (!keyExist && !DEFAULT_NAMESPACE.equals(namespace)) {
+ keyExist = ConfigurationRepository.lookup().getConfigurationFor(tenant, DEFAULT_NAMESPACE)
+ .containsKey(key);
+ }
+ if (!keyExist && !DEFAULT_TENANT.equals(tenant) && !DEFAULT_NAMESPACE.equals(namespace)) {
+ keyExist =
+ ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, DEFAULT_NAMESPACE)
+ .containsKey(key);
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ return keyExist;
+ }
+
+ public Map<String, String> listConfiguration(Map<String, Object> input) {
+ return listConfiguration((ConfigurationQuery) getInput(input));
+ }
+
+ private Map<String, String> listConfiguration(ConfigurationQuery query) {
+ Map<String, String> map = new HashMap<>();
+ try {
+ Collection<String> keys = getKeys(query.getTenant(), query.getNamespace());
+ for (String key : keys) {
+ map.put(key, getConfigurationValue(query.key(key)));
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ return null;
+ }
+ return map;
+ }
+
+ @Override
+ public boolean updateConfigurationValues(String tenant, String namespace,
+ Map configKeyValueStore) {
+ boolean valueToReturn = true;
+ Iterator<String> keys = configKeyValueStore.keySet().iterator();
+ while (keys.hasNext()) {
+ try {
+ String key = keys.next();
+ ConfigurationUpdate updateData = new ConfigurationUpdate();
+ updateData.tenant(tenant).namespace(namespace).key(key);
+ updateData.value(configKeyValueStore.get(key).toString());
+ updateConfigurationValue(updateData);
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ valueToReturn = false;
+ }
+ }
+ return valueToReturn;
+ }
+
+ private Object getInput(Map<String, Object> input) {
+ Object toReturn = null;
+ try {
+ toReturn = Class.forName(input.get("ImplClass").toString()).newInstance();
+ Method[] methods = toReturn.getClass().getMethods();
+ for (Method method : methods) {
+ if (input.containsKey(method.getName())) {
+ method.invoke(toReturn, input.get(method.getName()));
+ }
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+
+ return toReturn;
+ }
+
+ @Override
+ public Collection<String> getTenants() {
+ return ConfigurationRepository.lookup().getTenants();
+ }
+
+ @Override
+ public Collection<String> getNamespaces() {
+ return ConfigurationRepository.lookup().getNamespaces();
+ }
+
+ private ArrayList<String> getInMemoryKeys(String tenant, String namespace) {
+ ArrayList<String> keys = new ArrayList<>();
+
+ try {
+ Iterator<String> iter =
+ ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace).getKeys();
+ while (iter.hasNext()) {
+ String key = iter.next();
+ if (!(key.equals(NAMESPACE_KEY) || key.equals(MODE_KEY)
+ || key.equals(LOAD_ORDER_KEY))) {
+ keys.add(key);
+ }
+ }
+ } catch (Exception exception) {
+ //do nothing
+ }
+
+ return keys;
+ }
+
+ @Override
+ public Collection<String> getKeys(String tenant, String namespace) {
+ Set<String> keyCollection = new HashSet<>();
+ try {
+ keyCollection.addAll(ConfigurationUtils.executeSelectSql(
+ ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, DB_NAMESPACE)
+ .getString("fetchkeysql"),
+ new String[]{tenant + KEY_ELEMENTS_DELEMETER + DEFAULT_NAMESPACE}));
+ keyCollection.addAll(ConfigurationUtils.executeSelectSql(
+ ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, DB_NAMESPACE)
+ .getString("fetchkeysql"),
+ new String[]{tenant + KEY_ELEMENTS_DELEMETER + namespace}));
+ keyCollection.addAll(ConfigurationUtils.executeSelectSql(
+ ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, DB_NAMESPACE)
+ .getString("fetchkeysql"),
+ new String[]{DEFAULT_TENANT + KEY_ELEMENTS_DELEMETER + namespace}));
+ keyCollection.addAll(ConfigurationUtils.executeSelectSql(
+ ConfigurationRepository.lookup().getConfigurationFor(DEFAULT_TENANT, DB_NAMESPACE)
+ .getString("fetchkeysql"),
+ new String[]{DEFAULT_TENANT + KEY_ELEMENTS_DELEMETER + DEFAULT_NAMESPACE}));
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ keyCollection.addAll(getInMemoryKeys(tenant, DEFAULT_NAMESPACE));
+ keyCollection.addAll(getInMemoryKeys(tenant, namespace));
+ keyCollection.addAll(getInMemoryKeys(DEFAULT_TENANT, namespace));
+ keyCollection.addAll(getInMemoryKeys(DEFAULT_TENANT, DEFAULT_NAMESPACE));
+ }
+ return keyCollection;
+ }
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationChangeNotifier.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationChangeNotifier.java
new file mode 100644
index 0000000000..a7c10caae9
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationChangeNotifier.java
@@ -0,0 +1,474 @@
+package org.onap.config.impl;
+
+import org.onap.config.ConfigurationUtils;
+import org.onap.config.Constants;
+import org.onap.config.api.ConfigurationChangeListener;
+import org.onap.config.api.ConfigurationManager;
+import org.onap.config.api.Hint;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.ArrayList;
+import java.util.Collection;
+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.Objects;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import javax.management.JMX;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+
+/**
+ * The type Configuration change notifier.
+ */
+public final class ConfigurationChangeNotifier {
+
+ private final HashMap<String, List<NotificationData>> store = new HashMap<>();
+ private final ScheduledExecutorService executor =
+ Executors.newScheduledThreadPool(5, ConfigurationUtils.getThreadFactory());
+ private final ExecutorService notificationExecutor =
+ Executors.newCachedThreadPool(ConfigurationUtils.getThreadFactory());
+ private final Map<String, WatchService> watchServiceCollection =
+ Collections.synchronizedMap(new HashMap<>());
+
+ static {
+ if (!Thread.currentThread().getStackTrace()[2].getClassName()
+ .equals(ConfigurationImpl.class.getName())) {
+ throw new RuntimeException("Illegal access.");
+ }
+ }
+
+ /**
+ * Instantiates a new Configuration change notifier.
+ *
+ * @param inMemoryConfig the in memory config
+ */
+ public ConfigurationChangeNotifier(Map<String, AggregateConfiguration> inMemoryConfig) {
+ executor.scheduleWithFixedDelay(() -> this
+ .pollFilesystemAndUpdateConfigurationIfRequired(inMemoryConfig,
+ System.getProperty("config.location"), false), 1, 1, TimeUnit.MILLISECONDS);
+ executor.scheduleWithFixedDelay(() -> this
+ .pollFilesystemAndUpdateConfigurationIfRequired(inMemoryConfig,
+ System.getProperty("tenant.config.location"), true), 1, 1, TimeUnit.MILLISECONDS);
+ executor.scheduleWithFixedDelay(() -> this
+ .pollFilesystemAndUpdateNodeSpecificConfigurationIfRequired(
+ System.getProperty("node.config.location")), 1, 1, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Shutdown.
+ */
+ public void shutdown() {
+ for (WatchService watch : watchServiceCollection.values()) {
+ try {
+ watch.close();
+ } catch (IOException exception) {
+ //do nothing
+ }
+ }
+ executor.shutdownNow();
+ }
+
+ /**
+ * Poll filesystem and update configuration if required.
+ *
+ * @param inMemoryConfig the in memory config
+ * @param location the location
+ * @param isTenantLocation the is tenant location
+ */
+ public void pollFilesystemAndUpdateConfigurationIfRequired(
+ Map<String, AggregateConfiguration> inMemoryConfig, String location,
+ boolean isTenantLocation) {
+ try {
+ Set<Path> paths = watchForChange(location);
+ if (paths != null) {
+ for (Path path : paths) {
+ File file = path.toAbsolutePath().toFile();
+ String repositoryKey = null;
+ if (ConfigurationUtils.isConfig(file) && file.isFile()) {
+ if (isTenantLocation) {
+ Collection<File> tenantsRoot =
+ ConfigurationUtils.getAllFiles(new File(location), false, true);
+ for (File tenantRoot : tenantsRoot) {
+ if (file.getAbsolutePath().startsWith(tenantRoot.getAbsolutePath())) {
+ repositoryKey = ConfigurationUtils.getConfigurationRepositoryKey(
+ (tenantRoot.getName() + Constants.TENANT_NAMESPACE_SAPERATOR
+ + ConfigurationUtils.getNamespace(file))
+ .split(Constants.TENANT_NAMESPACE_SAPERATOR));
+ }
+ }
+ } else {
+ repositoryKey = ConfigurationUtils.getConfigurationRepositoryKey(file);
+ }
+ AggregateConfiguration config = inMemoryConfig.get(repositoryKey);
+ if (config != null) {
+ LinkedHashMap origConfig = ConfigurationUtils.toMap(config.getFinalConfiguration());
+ config.addConfig(file);
+ LinkedHashMap latestConfig = ConfigurationUtils.toMap(config.getFinalConfiguration());
+ Map map = ConfigurationUtils.diff(origConfig, latestConfig);
+ String[] tenantNamespaceArray =
+ repositoryKey.split(Constants.KEY_ELEMENTS_DELEMETER);
+ updateConfigurationValues(tenantNamespaceArray[0], tenantNamespaceArray[1], map);
+ }
+ } else {
+ for (String configKey : inMemoryConfig.keySet()) {
+ repositoryKey = configKey;
+ AggregateConfiguration config = inMemoryConfig.get(repositoryKey);
+ if (config.containsConfig(file)) {
+ LinkedHashMap origConfig = ConfigurationUtils.toMap(config.getFinalConfiguration());
+ config.removeConfig(file);
+ LinkedHashMap latestConfig =
+ ConfigurationUtils.toMap(config.getFinalConfiguration());
+ Map map = ConfigurationUtils.diff(origConfig, latestConfig);
+ String[] tenantNamespaceArray =
+ repositoryKey.split(Constants.KEY_ELEMENTS_DELEMETER);
+ updateConfigurationValues(tenantNamespaceArray[0], tenantNamespaceArray[1],
+ map);
+ }
+ }
+ }
+ }
+ }
+ } catch (ClosedWatchServiceException exception) {
+ // do nothing.
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ private void updateConfigurationValues(String tenant, String namespace, Map map)
+ throws Exception {
+ MBeanServerConnection mbsc = ManagementFactory.getPlatformMBeanServer();
+ ObjectName mbeanName = new ObjectName(Constants.MBEAN_NAME);
+ ConfigurationManager conf =
+ JMX.newMBeanProxy(mbsc, mbeanName, ConfigurationManager.class,
+ true);
+ conf.updateConfigurationValues(tenant, namespace, map);
+ }
+
+ /**
+ * Poll filesystem and update node specific configuration if required.
+ *
+ * @param location the location
+ */
+ public void pollFilesystemAndUpdateNodeSpecificConfigurationIfRequired(String location) {
+ try {
+ Set<Path> paths = watchForChange(location);
+ if (paths != null) {
+ for (Path path : paths) {
+ File file = path.toAbsolutePath().toFile();
+
+ if (ConfigurationUtils.isConfig(file)) {
+ String repositoryKey = ConfigurationUtils.getConfigurationRepositoryKey(file);
+ ConfigurationRepository.lookup().populateOverrideConfigurtaion(repositoryKey, file);
+ } else {
+ ConfigurationRepository.lookup().removeOverrideConfigurtaion(file);
+ }
+ }
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ /**
+ * Notify changes towards.
+ *
+ * @param tenant the tenant
+ * @param component the component
+ * @param key the key
+ * @param myself the myself
+ * @throws Exception the exception
+ */
+ public void notifyChangesTowards(String tenant, String component, String key,
+ ConfigurationChangeListener myself) throws Exception {
+ List<NotificationData> notificationList =
+ store.get(tenant + Constants.KEY_ELEMENTS_DELEMETER + component);
+ if (notificationList == null) {
+ notificationList = Collections.synchronizedList(new ArrayList<NotificationData>());
+ store.put(tenant + Constants.KEY_ELEMENTS_DELEMETER + component, notificationList);
+ executor.scheduleWithFixedDelay(
+ () -> triggerScanning(tenant + Constants.KEY_ELEMENTS_DELEMETER + component), 1, 30000,
+ TimeUnit.MILLISECONDS);
+ }
+ notificationList.add(new NotificationData(tenant, component, key, myself));
+ }
+
+ /**
+ * Stop notification towards.
+ *
+ * @param tenant the tenant
+ * @param component the component
+ * @param key the key
+ * @param myself the myself
+ * @throws Exception the exception
+ */
+ public void stopNotificationTowards(String tenant, String component, String key,
+ ConfigurationChangeListener myself) throws Exception {
+ List<NotificationData> notificationList =
+ store.get(tenant + Constants.KEY_ELEMENTS_DELEMETER + component);
+ if (notificationList != null) {
+ boolean removed =
+ notificationList.remove(new NotificationData(tenant, component, key, myself));
+ if (removed && notificationList.isEmpty()) {
+ store.remove(tenant + Constants.KEY_ELEMENTS_DELEMETER + component);
+ }
+ }
+
+ }
+
+ private void triggerScanning(String key) {
+ if (store.get(key) != null) {
+ notificationExecutor.submit(() -> scanForChanges(key));
+ } else {
+ throw new IllegalArgumentException("Notification service for " + key + " is suspended.");
+ }
+ }
+
+ private void scanForChanges(String key) {
+ List<NotificationData> list = store.get(key);
+ if (list != null) {
+ list.stream()
+ .filter(NotificationData::isChanged)
+ .forEach(notificationData -> notificationExecutor.submit(() -> sendNotification(notificationData)));
+ }
+ }
+
+ private void sendNotification(NotificationData notificationData) {
+ try {
+ notificationData.dispatchNotification();
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ private Set<Path> watchForChange(String location) throws Exception {
+ if (location == null || location.trim().length() == 0) {
+ return null;
+ }
+ File file = new File(location);
+ if (!file.exists()) {
+ return null;
+ }
+ Path path = file.toPath();
+ Set<Path> toReturn = new HashSet<>();
+ try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {
+ watchServiceCollection.put(location, watchService);
+ path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY,
+ StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE);
+ for (File dir : ConfigurationUtils.getAllFiles(file, true, true)) {
+ dir.toPath().register(watchService, StandardWatchEventKinds.ENTRY_MODIFY,
+ StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE);
+ }
+ while (true) {
+ final WatchKey wk = watchService.take();
+ Thread.sleep(ConfigurationRepository.lookup()
+ .getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE)
+ .getLong("event.fetch.delay"));
+ for (WatchEvent<?> event : wk.pollEvents()) {
+ Object context = event.context();
+ if (context instanceof Path) {
+ File newFile = new File(((Path) wk.watchable()).toFile(), context.toString());
+ if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
+ if (newFile.isDirectory()) {
+ newFile.toPath().register(watchService, StandardWatchEventKinds.ENTRY_MODIFY,
+ StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE);
+ continue;
+ }
+ } else if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
+ if (newFile.isDirectory()) {
+ continue;
+ }
+ }
+ toReturn.add(newFile.toPath());
+ }
+ }
+ if (toReturn.isEmpty()) {
+ continue;
+ }
+ break;
+ }
+ }
+ return toReturn;
+ }
+
+ /**
+ * The type Notification data.
+ */
+ static class NotificationData {
+
+ /**
+ * The Tenant.
+ */
+ final String tenant;
+ /**
+ * The Namespace.
+ */
+ final String namespace;
+ /**
+ * The Key.
+ */
+ final String key;
+ /**
+ * The Myself.
+ */
+ final ConfigurationChangeListener myself;
+ /**
+ * The Current value.
+ */
+ Object currentValue;
+ /**
+ * The Is array.
+ */
+ boolean isArray;
+
+ /**
+ * Instantiates a new Notification data.
+ *
+ * @param tenant the tenant
+ * @param component the component
+ * @param key the key
+ * @param myself the myself
+ * @throws Exception the exception
+ */
+ public NotificationData(String tenant, String component, String key,
+ ConfigurationChangeListener myself) throws Exception {
+ this.tenant = tenant;
+ this.namespace = component;
+ this.key = key;
+ this.myself = myself;
+ if (!ConfigurationRepository.lookup().getConfigurationFor(tenant, component)
+ .containsKey(key)) {
+ throw new RuntimeException("Key[" + key + "] not found.");
+ }
+ isArray = ConfigurationUtils.isArray(tenant, component, key, Hint.DEFAULT.value());
+ if (isArray) {
+ currentValue = ConfigurationManager.lookup().getAsStringValues(tenant, component, key);
+ } else {
+ currentValue = ConfigurationManager.lookup().getAsString(tenant, component, key);
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof NotificationData)) {
+ return false;
+ }
+ NotificationData nd = (NotificationData) obj;
+ return Objects.equals(tenant, nd.tenant)
+ && Objects.equals(namespace, nd.namespace)
+ && Objects.equals(key, nd.key)
+ && Objects.equals(myself, nd.myself)
+ && Objects.equals(currentValue, nd.currentValue) // it's either String or List<String>
+ && isArray == nd.isArray;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tenant, namespace, key, myself, currentValue, isArray);
+ }
+
+ /**
+ * Is changed boolean.
+ *
+ * @return the boolean
+ */
+ public boolean isChanged() {
+ Object latestValue;
+ try {
+ if (isArray) {
+ latestValue = ConfigurationManager.lookup().getAsStringValues(tenant, namespace, key);
+ } else {
+ latestValue = ConfigurationManager.lookup().getAsString(tenant, namespace, key);
+ }
+ if (!isArray) {
+ return !currentValue.equals(latestValue);
+ } else {
+ Collection<String> oldCollection = (Collection<String>) currentValue;
+ Collection<String> newCollection = (Collection<String>) latestValue;
+ for (String val : oldCollection) {
+ if (!newCollection.remove(val)) {
+ return true;
+ }
+ }
+ return !newCollection.isEmpty();
+ }
+ } catch (Exception exception) {
+ return false;
+ }
+ }
+
+ /**
+ * Dispatch notification.
+ *
+ * @throws Exception the exception
+ */
+ public void dispatchNotification() throws Exception {
+ Method method = null;
+ Vector<Object> parameters = null;
+ try {
+ Object latestValue;
+ if (isArray) {
+ latestValue = ConfigurationManager.lookup().getAsStringValues(tenant, namespace, key);
+ } else {
+ latestValue = ConfigurationManager.lookup().getAsString(tenant, namespace, key);
+ }
+ Method[] methods = myself.getClass().getDeclaredMethods();
+ if (methods != null && methods.length > 0) {
+ method = methods[0];
+ int paramCount = method.getParameterCount();
+ parameters = new Vector<>();
+ if (paramCount > 4) {
+ if (tenant.equals(Constants.DEFAULT_TENANT)) {
+ parameters.add(null);
+ } else {
+ parameters.add(tenant);
+ }
+ }
+ if (paramCount > 3) {
+ if (namespace.equals(Constants.DEFAULT_NAMESPACE)) {
+ parameters.add(null);
+ } else {
+ parameters.add(namespace);
+ }
+ }
+ parameters.add(key);
+ parameters.add(currentValue);
+ parameters.add(latestValue);
+ method.setAccessible(true);
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ } finally {
+ isArray = ConfigurationUtils.isArray(tenant, namespace, key, Hint.DEFAULT.value());
+ if (isArray) {
+ currentValue = ConfigurationManager.lookup().getAsStringValues(tenant, namespace, key);
+ } else {
+ currentValue = ConfigurationManager.lookup().getAsString(tenant, namespace, key);
+ }
+ if (method != null && parameters != null) {
+ method.invoke(myself, parameters.toArray());
+ }
+ }
+ }
+ }
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationDataSource.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationDataSource.java
new file mode 100644
index 0000000000..ea9c61f635
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationDataSource.java
@@ -0,0 +1,105 @@
+package org.onap.config.impl;
+
+import org.apache.commons.configuration2.ImmutableConfiguration;
+import org.apache.commons.dbcp2.BasicDataSource;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.config.ConfigurationUtils;
+import org.onap.config.Constants;
+
+import java.sql.Driver;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+/**
+ * The type Configuration data source.
+ */
+public final class ConfigurationDataSource {
+
+ private static BasicDataSource configDBDatasource = null;
+ private static Set<String> validCallers = Collections.unmodifiableSet(new HashSet<>(
+ Arrays.asList(ConfigurationUtils.class.getName(), CliConfigurationImpl.class.getName())));
+
+ private ConfigurationDataSource() {
+ //Hide constructor to prevent instantiation using the default implicit constructor
+ }
+
+ static {
+ try {
+ configDBDatasource = initDataSource();
+ } catch (Exception exception) {
+ System.err.println("Datasource initialization error. Configuration management will be using"
+ + "in-memory persistence.");
+ }
+ }
+
+ /**
+ * Lookup basic data source.
+ *
+ * @return the basic data source
+ * @throws Exception the exception
+ */
+ public static BasicDataSource lookup() throws Exception {
+ if (validCallers.contains(Thread.currentThread().getStackTrace()[2].getClassName())) {
+ return configDBDatasource;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Init data source basic data source.
+ *
+ * @return the basic data source
+ * @throws Exception the exception
+ */
+ public static BasicDataSource initDataSource() throws Exception {
+ ImmutableConfiguration dbConfig = ConfigurationRepository.lookup()
+ .getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE);
+ if (StringUtils.isEmpty(dbConfig.getString("dbhost"))) {
+ return null;
+ }
+ BasicDataSource datasource = new BasicDataSource();
+ String driverClassName = dbConfig.getString("driverClassName");
+ String jdbcUrl = dbConfig.getString("jdbcURL");
+ if (!isDriverSuitable(driverClassName, jdbcUrl)) {
+ driverClassName = getDriverFor(jdbcUrl);
+ }
+ datasource.setDriverClassName(driverClassName);
+ datasource.setUrl(jdbcUrl);
+ String dbuser = dbConfig.getString("dbuser");
+ String dbpassword = dbConfig.getString("dbpassword");
+ if (dbuser != null && dbuser.trim().length() > 0) {
+ datasource.setUsername(dbuser);
+ }
+ if (dbpassword != null && dbpassword.trim().length() > 0) {
+ datasource.setPassword(dbpassword);
+ }
+ return datasource;
+ }
+
+ private static boolean isDriverSuitable(String driverClassName, String url) {
+ if (driverClassName == null || driverClassName.trim().length() == 0) {
+ return false;
+ }
+ try {
+ Driver driver = Driver.class.cast(Class.forName(driverClassName).newInstance());
+ return driver.acceptsURL(url);
+ } catch (Exception exception) {
+ return false;
+ }
+ }
+
+ private static String getDriverFor(String url) throws Exception {
+ ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
+ for (Driver driver : loader) {
+ if (driver.acceptsURL(url)) {
+ return driver.getClass().getName();
+ }
+ }
+ throw new RuntimeException("No Suitable driver found for " + url);
+ }
+
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationFilter.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationFilter.java
new file mode 100644
index 0000000000..9df69ed1d7
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationFilter.java
@@ -0,0 +1,39 @@
+package org.onap.config.impl;
+
+import org.onap.config.Constants;
+import org.onap.config.api.Configuration;
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+
+@WebFilter("/")
+public class ConfigurationFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig paramFilterConfig) throws ServletException {
+ //Use the default behavior
+ }
+
+ @Override
+ public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse,
+ FilterChain paramFilterChain) throws IOException, ServletException {
+ Configuration.tenant.set(Constants.DEFAULT_TENANT);
+ try {
+ paramFilterChain.doFilter(paramServletRequest, paramServletResponse);
+ } finally {
+ Configuration.tenant.remove();
+ }
+ }
+
+ @Override
+ public void destroy() {
+ //Use the default behavior
+ }
+
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationImpl.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationImpl.java
new file mode 100644
index 0000000000..43b1b96fc5
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationImpl.java
@@ -0,0 +1,679 @@
+package org.onap.config.impl;
+
+import static org.onap.config.ConfigurationUtils.getConfigurationRepositoryKey;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.DatabaseConfiguration;
+import org.onap.config.ConfigurationUtils;
+import org.onap.config.Constants;
+import org.onap.config.NonConfigResource;
+import org.onap.config.api.Config;
+import org.onap.config.api.ConfigurationChangeListener;
+import org.onap.config.api.Hint;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * The type Configuration.
+ */
+public class ConfigurationImpl implements org.onap.config.api.Configuration {
+
+ private static ThreadLocal<String> tenant = new ThreadLocal<String>() {
+ protected String initialValue() {
+ return Constants.DEFAULT_TENANT;
+ }
+
+ ;
+ };
+ private static boolean instantiated = false;
+ /**
+ * The Change notifier.
+ */
+ ConfigurationChangeNotifier changeNotifier;
+
+ /**
+ * Instantiates a new Configuration.
+ *
+ * @throws Exception the exception
+ */
+ public ConfigurationImpl() throws Exception {
+ if (instantiated || !CliConfigurationImpl.class.isAssignableFrom(this.getClass())) {
+ throw new RuntimeException("Illegal access to configuration.");
+ }
+ Map<String, AggregateConfiguration> moduleConfigStore = new HashMap<>();
+ List<URL> classpathResources = ConfigurationUtils.getAllClassPathResources();
+ Predicate<URL> predicate = ConfigurationUtils::isConfig;
+ for (URL url : classpathResources) {
+ if (predicate.test(url)) {
+ String moduleName = ConfigurationUtils.getConfigurationRepositoryKey(url);
+ AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
+ if (moduleConfig == null) {
+ moduleConfig = new AggregateConfiguration();
+ moduleConfigStore.put(moduleName, moduleConfig);
+ }
+ moduleConfig.addConfig(url);
+ } else {
+ NonConfigResource.add(url);
+ }
+ }
+ String configLocation = System.getProperty("config.location");
+ if (configLocation != null && configLocation.trim().length() > 0) {
+ File root = new File(configLocation);
+ Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
+ Predicate<File> filePredicate = ConfigurationUtils::isConfig;
+ for (File file : filesystemResources) {
+ if (filePredicate.test(file)) {
+ String moduleName = ConfigurationUtils.getConfigurationRepositoryKey(file);
+ AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
+ if (moduleConfig == null) {
+ moduleConfig = new AggregateConfiguration();
+ moduleConfigStore.put(moduleName, moduleConfig);
+ }
+ moduleConfig.addConfig(file);
+ } else {
+ NonConfigResource.add(file);
+ }
+ }
+ }
+ String tenantConfigLocation = System.getProperty("tenant.config.location");
+ if (tenantConfigLocation != null && tenantConfigLocation.trim().length() > 0) {
+ File root = new File(tenantConfigLocation);
+ Collection<File> tenantsRoot = ConfigurationUtils.getAllFiles(root, false, true);
+ Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
+ Predicate<File> filePredicate = ConfigurationUtils::isConfig;
+ for (File file : filesystemResources) {
+ if (filePredicate.test(file)) {
+ String moduleName = ConfigurationUtils.getNamespace(file);
+ for (File tenanatFileRoot : tenantsRoot) {
+ if (file.getAbsolutePath().startsWith(tenanatFileRoot.getAbsolutePath())) {
+ moduleName = ConfigurationUtils.getConfigurationRepositoryKey(
+ (tenanatFileRoot.getName().toUpperCase() + Constants.TENANT_NAMESPACE_SAPERATOR
+ + moduleName).split(Constants.TENANT_NAMESPACE_SAPERATOR));
+ }
+ }
+ AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
+ if (moduleConfig == null) {
+ moduleConfig = new AggregateConfiguration();
+ moduleConfigStore.put(moduleName, moduleConfig);
+ }
+ moduleConfig.addConfig(file);
+ }
+ }
+ }
+ populateFinalConfigurationIncrementally(moduleConfigStore);
+ ConfigurationRepository.lookup().initTenantsAndNamespaces();
+ String nodeConfigLocation = System.getProperty("node.config.location");
+ if (nodeConfigLocation != null && nodeConfigLocation.trim().length() > 0) {
+ File root = new File(nodeConfigLocation);
+ Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
+ Predicate<File> filePredicate = ConfigurationUtils::isConfig;
+ for (File file : filesystemResources) {
+ if (filePredicate.test(file)) {
+ ConfigurationRepository.lookup().populateOverrideConfigurtaion(
+ ConfigurationUtils.getConfigurationRepositoryKey(ConfigurationUtils.getNamespace(file)
+ .split(Constants.TENANT_NAMESPACE_SAPERATOR)), file);
+ }
+ }
+ }
+ instantiated = true;
+ changeNotifier = new ConfigurationChangeNotifier(moduleConfigStore);
+ }
+
+ @Override
+ public void addConfigurationChangeListener(String tenant, String namespace, String key,
+ ConfigurationChangeListener myself) {
+ tenant = ConfigurationRepository.lookup().isValidTenant(tenant) ? tenant.toUpperCase()
+ : Constants.DEFAULT_TENANT;
+ namespace =
+ ConfigurationRepository.lookup().isValidNamespace(namespace) ? namespace.toUpperCase()
+ : Constants.DEFAULT_NAMESPACE;
+ if (key == null || key.trim().length() == 0) {
+ throw new IllegalArgumentException("Key can't be null.");
+ }
+ if (myself == null) {
+ throw new IllegalArgumentException("ConfigurationChangeListener instance is null.");
+ }
+ try {
+ changeNotifier.notifyChangesTowards(tenant, namespace, key, myself);
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ private void populateFinalConfigurationIncrementally(Map<String, AggregateConfiguration> configs)
+ throws Exception {
+ boolean isDbAccessible = false;
+ if (configs.get(
+ Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELEMETER + Constants.DB_NAMESPACE)
+ != null) {
+ ConfigurationRepository.lookup().populateConfigurtaion(
+ Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELEMETER + Constants.DB_NAMESPACE,
+ configs.remove(
+ Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELEMETER + Constants.DB_NAMESPACE)
+ .getFinalConfiguration());
+ isDbAccessible = ConfigurationUtils.executeDdlSql(ConfigurationRepository.lookup()
+ .getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE)
+ .getString("createtablecql"));
+ if (isDbAccessible) {
+ ConfigurationUtils.executeDdlSql(ConfigurationRepository.lookup()
+ .getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE)
+ .getString("createmonitoringtablecql"));
+ }
+ }
+
+ Set<String> modules = configs.keySet();
+ for (String module : modules) {
+ if (isDbAccessible) {
+ DatabaseConfiguration config =
+ ConfigurationUtils.getDbConfigurationBuilder(module).getConfiguration();
+ Configuration currentConfig = configs.get(module).getFinalConfiguration();
+ Iterator<String> keys = currentConfig.getKeys();
+ while (keys.hasNext()) {
+ String currentKey = keys.next();
+ if (!(Constants.MODE_KEY.equals(currentKey)
+ || Constants.NAMESPACE_KEY.equals(currentKey)
+ || Constants.LOAD_ORDER_KEY.equals(currentKey))) {
+ if (!config.containsKey(currentKey)) {
+ Object propValue = currentConfig.getProperty(currentKey);
+ if (propValue instanceof Collection) {
+ config.addProperty(currentKey, propValue.toString());
+ } else {
+ config.addProperty(currentKey, propValue);
+ }
+ }
+ }
+ }
+ } else {
+ ConfigurationRepository.lookup()
+ .populateConfigurtaion(module, configs.get(module).getFinalConfiguration());
+ }
+ }
+ }
+
+ @Override
+ public <T> T get(String tenant, String namespace, String key, Class<T> clazz, Hint... hints) {
+
+ String[] tenantNamespaceArrayy = null;
+ if (tenant == null && namespace != null
+ && (tenantNamespaceArrayy = namespace.split(Constants.TENANT_NAMESPACE_SAPERATOR)).length
+ > 1) {
+ tenant = tenantNamespaceArrayy[0];
+ namespace = tenantNamespaceArrayy[1];
+ }
+
+ tenant = ConfigurationRepository.lookup().isValidTenant(tenant) ? tenant.toUpperCase()
+ : Constants.DEFAULT_TENANT;
+ namespace =
+ ConfigurationRepository.lookup().isValidNamespace(namespace) ? namespace.toUpperCase()
+ : Constants.DEFAULT_NAMESPACE;
+ T returnValue = null;
+ returnValue = (T) getInternal(tenant, namespace, key,
+ clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
+ hints == null || hints.length == 0 ? new Hint[]{Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC}
+ : hints);
+ if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
+ && !Constants.DEFAULT_TENANT.equals(tenant)) {
+ returnValue = (T) getInternal(Constants.DEFAULT_TENANT, namespace, key,
+ clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
+ hints == null || hints.length == 0 ? new Hint[]{Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC}
+ : hints);
+ }
+ if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
+ && !Constants.DEFAULT_NAMESPACE.equals(namespace)) {
+ returnValue = (T) getInternal(tenant, Constants.DEFAULT_NAMESPACE, key,
+ clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
+ hints == null || hints.length == 0 ? new Hint[]{Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC}
+ : hints);
+ }
+ if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
+ && !Constants.DEFAULT_NAMESPACE.equals(namespace)
+ && !Constants.DEFAULT_TENANT.equals(tenant)) {
+ returnValue = (T) getInternal(Constants.DEFAULT_TENANT, Constants.DEFAULT_NAMESPACE, key,
+ clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
+ hints == null || hints.length == 0 ? new Hint[]{Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC}
+ : hints);
+ }
+ if (returnValue == null && clazz.isPrimitive()) {
+ return (T) ConfigurationUtils.getDefaultFor(clazz);
+ } else {
+ return returnValue;
+ }
+ }
+
+
+ /**
+ * Gets internal.
+ *
+ * @param <T> the type parameter
+ * @param tenant the tenant
+ * @param namespace the namespace
+ * @param key the key
+ * @param clazz the clazz
+ * @param hints the hints
+ * @return the internal
+ */
+ protected <T> T getInternal(String tenant, String namespace, String key, Class<T> clazz,
+ Hint... hints) {
+ int processingHints = Hint.DEFAULT.value();
+ if (hints != null) {
+ for (Hint hint : hints) {
+ processingHints = processingHints | hint.value();
+ }
+ }
+
+ if (tenant == null || tenant.trim().length() == 0) {
+ tenant = this.tenant.get();
+ } else {
+ tenant = tenant.toUpperCase();
+ }
+ if (namespace == null || namespace.trim().length() == 0) {
+ namespace = Constants.DEFAULT_NAMESPACE;
+ } else {
+ namespace = namespace.toUpperCase();
+ }
+ if (key == null || key.trim().length() == 0) {
+ if (!clazz.isAnnotationPresent(Config.class)) {
+ throw new IllegalArgumentException("Key can't be null.");
+ }
+ }
+ if (clazz == null) {
+ throw new IllegalArgumentException("clazz is null.");
+ }
+ if (clazz.isPrimitive()) {
+ clazz = getWrapperClass(clazz);
+ }
+ try {
+ if (ConfigurationUtils.isWrapperClass(clazz) || clazz.isPrimitive()) {
+ Object obj =
+ ConfigurationUtils
+ .getProperty(ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace),
+ key, processingHints);
+ if (obj != null) {
+ if (ConfigurationUtils.isCollection(obj.toString())) {
+ obj = ConfigurationUtils.getCollectionString(obj.toString());
+ }
+ String value = obj.toString().split(",")[0];
+ value = ConfigurationUtils.processVariablesIfPresent(tenant, namespace, value);
+ return (T) getValue(value, clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
+ processingHints);
+ } else {
+ return null;
+ }
+ } else if (clazz.isArray()
+ && (clazz.getComponentType().isPrimitive() || ConfigurationUtils.isWrapperClass(clazz.getComponentType()))) {
+ Object obj =
+ ConfigurationUtils
+ .getProperty(ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace),
+ key, processingHints);
+ if (obj != null) {
+ Class componentClass = clazz.getComponentType();
+ if (clazz.getComponentType().isPrimitive()) {
+ componentClass = getWrapperClass(clazz.getComponentType());
+ }
+ String collString = ConfigurationUtils.getCollectionString(obj.toString());
+ ArrayList<String> tempCollection = new ArrayList<>();
+ for (String itemValue : collString.split(",")) {
+ tempCollection
+ .add(ConfigurationUtils.processVariablesIfPresent(tenant, namespace, itemValue));
+ }
+ Collection<T> collection = convert(
+ ConfigurationUtils.getCollectionString(Arrays.toString(tempCollection.toArray())),
+ componentClass, processingHints);
+ if (clazz.getComponentType().isPrimitive()) {
+ return (T) ConfigurationUtils.getPrimitiveArray(collection, clazz.getComponentType());
+ } else {
+ return (T) collection
+ .toArray(getZeroLengthArrayFor(getWrapperClass(clazz.getComponentType())));
+ }
+ } else {
+ return null;
+ }
+ } else if (clazz.isAnnotationPresent(Config.class)) {
+ return read(tenant, namespace, clazz,
+ (key == null || key.trim().length() == 0) ? "" : (key + "."), hints);
+ } else {
+ throw new IllegalArgumentException(
+ "Only pimitive classes, wrapper classes, corresponding array classes and any "
+ + "class decorated with @org.openecomp.config.api.Config are allowed as argument.");
+ }
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ return null;
+ }
+
+
+ private <T> T read(String tenant, String namespace, Class<T> clazz, String keyPrefix,
+ Hint... hints) throws Exception {
+ Config confAnnot =
+ clazz.getAnnotation(Config.class);
+ if (confAnnot != null && confAnnot.key().length()>0 && !keyPrefix.endsWith(".")) {
+ keyPrefix += (confAnnot.key() + ".");
+ }
+ Constructor<T> constructor = clazz.getDeclaredConstructor();
+ constructor.setAccessible(true);
+ T objToReturn = constructor.newInstance();
+ for (Field field : clazz.getDeclaredFields()) {
+ field.setAccessible(true);
+ Config fieldConfAnnot =
+ field.getAnnotation(Config.class);
+ if (fieldConfAnnot != null) {
+ if (field.getType().isPrimitive() || ConfigurationUtils.isWrapperClass(field.getType())
+ || (field.getType().isArray() && (field.getType().getComponentType().isPrimitive()
+ || ConfigurationUtils.isWrapperClass(field.getType().getComponentType())))
+ || field.getType().getAnnotation(Config.class) != null) {
+ field.set(objToReturn,
+ get(tenant, namespace, keyPrefix + fieldConfAnnot.key(), field.getType(), hints));
+ } else if (Collection.class.isAssignableFrom(field.getType())) {
+ Object obj = get(tenant, namespace, keyPrefix + fieldConfAnnot.key(),
+ ConfigurationUtils.getArrayClass(ConfigurationUtils.getCollectionGenericType(field)),
+ hints);
+ if (obj != null) {
+ List list = Arrays.asList((Object[]) obj);
+ Class clazzToInstantiate = null;
+ if (field.getType().isInterface()) {
+ clazzToInstantiate =
+ ConfigurationUtils.getConcreteCollection(field.getType()).getClass();
+ } else if (Modifier.isAbstract(field.getType().getModifiers())) {
+ clazzToInstantiate =
+ ConfigurationUtils.getCompatibleCollectionForAbstractDef(field.getType())
+ .getClass();
+ } else {
+ clazzToInstantiate = field.getType();
+ }
+ Constructor construct =
+ getConstructorWithArguments(clazzToInstantiate, Collection.class);
+ if (construct != null) {
+ construct.setAccessible(true);
+ field.set(objToReturn, construct.newInstance(list));
+ } else if ((construct =
+ getConstructorWithArguments(clazzToInstantiate, Integer.class, Boolean.class,
+ Collection.class)) != null) {
+ construct.setAccessible(true);
+ field.set(objToReturn, construct.newInstance(list.size(), true, list));
+ }
+ }
+ }else if (Map.class.isAssignableFrom(field.getType())){
+ field.set(objToReturn, generateMap(tenant, namespace, keyPrefix+fieldConfAnnot.key()));
+ }
+ }
+ }
+ return objToReturn;
+ }
+
+ private Constructor getConstructorWithArguments(Class clazz, Class... classes) {
+ try {
+ return clazz.getDeclaredConstructor(classes);
+ } catch (Exception exception) {
+ return null;
+ }
+ }
+
+ private Class getWrapperClass(Class clazz) {
+ if (byte.class == clazz) {
+ return Byte.class;
+ } else if (short.class == clazz) {
+ return Short.class;
+ } else if (int.class == clazz) {
+ return Integer.class;
+ } else if (long.class == clazz) {
+ return Long.class;
+ } else if (float.class == clazz) {
+ return Float.class;
+ } else if (double.class == clazz) {
+ return Double.class;
+ } else if (char.class == clazz) {
+ return Character.class;
+ } else if (boolean.class == clazz) {
+ return Boolean.class;
+ }
+ return clazz;
+ }
+
+ private <T> T getValue(Object obj, Class<T> clazz, int processingHint) {
+ if (obj == null || obj.toString().trim().length() == 0) {
+ return null;
+ } else {
+ obj = obj.toString().trim();
+ }
+ if (String.class.equals(clazz)) {
+ if (obj.toString().startsWith("@") && ConfigurationUtils.isExternalLookup(processingHint)) {
+ String contents = ConfigurationUtils
+ .getFileContents(NonConfigResource.locate(obj.toString().substring(1).trim()));
+ if (contents == null) {
+ contents = ConfigurationUtils.getFileContents(obj.toString().substring(1).trim());
+ }
+ if (contents != null) {
+ obj = contents;
+ }
+ }
+ return (T) obj.toString();
+ } else if (Number.class.isAssignableFrom(clazz)) {
+ Double doubleValue = Double.valueOf(obj.toString());
+ switch (clazz.getName()) {
+ case "java.lang.Byte":
+ Byte byteVal = doubleValue.byteValue();
+ return (T) byteVal;
+ case "java.lang.Short":
+ Short shortVal = doubleValue.shortValue();
+ return (T) shortVal;
+ case "java.lang.Integer":
+ Integer intVal = doubleValue.intValue();
+ return (T) intVal;
+ case "java.lang.Long":
+ Long longVal = doubleValue.longValue();
+ return (T) longVal;
+ case "java.lang.Float":
+ Float floatVal = doubleValue.floatValue();
+ return (T) floatVal;
+ case "java.lang.Double":
+ Double doubleVal = doubleValue.doubleValue();
+ return (T) doubleVal;
+ default:
+ }
+ } else if (Boolean.class.equals(clazz)) {
+ return (T) Boolean.valueOf(obj.toString());
+ } else if (Character.class.equals(clazz)) {
+ return (T) Character.valueOf(obj.toString().charAt(0));
+ }
+ return null;
+ }
+
+ private <T> T[] getZeroLengthArrayFor(Class<T> clazz) {
+ Object obj = null;
+ if (clazz == int.class) {
+ obj = new int[]{};
+ } else if (clazz == byte.class) {
+ obj = new byte[]{};
+ } else if (clazz == short.class) {
+ obj = new short[]{};
+ } else if (clazz == long.class) {
+ obj = new long[]{};
+ } else if (clazz == float.class) {
+ obj = new float[]{};
+ } else if (clazz == double.class) {
+ obj = new double[]{};
+ } else if (clazz == boolean.class) {
+ obj = new boolean[]{};
+ } else if (clazz == char.class) {
+ obj = new char[]{};
+ } else if (clazz == Byte.class) {
+ obj = new Byte[]{};
+ } else if (clazz == Short.class) {
+ obj = new Short[]{};
+ } else if (clazz == Integer.class) {
+ obj = new Integer[]{};
+ } else if (clazz == Long.class) {
+ obj = new Long[]{};
+ } else if (clazz == Float.class) {
+ obj = new Float[]{};
+ } else if (clazz == Double.class) {
+ obj = new Double[]{};
+ } else if (clazz == Boolean.class) {
+ obj = new Boolean[]{};
+ } else if (clazz == Character.class) {
+ obj = new Character[]{};
+ } else if (clazz == String.class) {
+ obj = new String[]{};
+ }
+ return (T[]) obj;
+ }
+
+ private <T> Collection<T> convert(String commaSaperatedValues, Class<T> clazz,
+ int processingHints) {
+ ArrayList<T> collection = new ArrayList<>();
+ for (String value : commaSaperatedValues.split(",")) {
+ try {
+ T type1 = getValue(value, clazz, processingHints);
+ if (type1 != null) {
+ collection.add(type1);
+ }
+ } catch (RuntimeException re) {
+ // do nothing
+ }
+ }
+ return collection;
+ }
+
+ /**
+ * Shutdown.
+ */
+ public void shutdown() {
+ if (changeNotifier != null) {
+ try {
+ changeNotifier.shutdown();
+ ConfigurationDataSource.lookup().close();
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void removeConfigurationChangeListener(String tenant, String namespace, String key,
+ ConfigurationChangeListener myself) {
+ tenant = ConfigurationRepository.lookup().isValidTenant(tenant) ? tenant.toUpperCase()
+ : Constants.DEFAULT_TENANT;
+ namespace =
+ ConfigurationRepository.lookup().isValidNamespace(namespace) ? namespace.toUpperCase()
+ : Constants.DEFAULT_NAMESPACE;
+ if (key == null || key.trim().length() == 0) {
+ throw new IllegalArgumentException("Key can't be null.");
+ }
+ try {
+ changeNotifier.stopNotificationTowards(tenant, namespace, key, myself);
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ @Override
+ public <T> Map<String, T> populateMap(String tenantId, String namespace, String key, Class<T> clazz){
+ if (tenantId==null || tenantId.trim().length()==0){
+ tenantId = this.tenant.get();
+ }else{
+ tenantId = tenantId.toUpperCase();
+ }
+ if (namespace==null || namespace.trim().length()==0){
+ namespace = Constants.DEFAULT_NAMESPACE;
+ }else{
+ namespace = namespace.toUpperCase();
+ }
+ Map<String, T> map = new HashMap<>();
+ Iterator<String> keys ;
+ try {
+ if (ConfigurationRepository.lookup().isDBAccessible()){
+ keys = ConfigurationUtils.executeSelectSql(ConfigurationRepository.lookup().getConfigurationFor(
+ Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE).getString("fetchkeysql"), new String[]{tenantId+ Constants.KEY_ELEMENTS_DELEMETER+namespace}).iterator();
+ }else{
+ keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys(key);
+ }
+ while(keys.hasNext()){
+ String k = keys.next();
+ if (k.startsWith(key+".")){
+ k = k.substring(key.length()+1);
+ String subkey = k.substring(0, k.indexOf("."));
+ if (!map.containsKey(subkey)){
+ map.put(subkey, get(tenantId, namespace, key+"."+subkey, clazz));
+ }
+ }
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return map;
+ }
+
+ @Override
+ public Map generateMap(String tenantId, String namespace, String key){
+ if (tenantId==null || tenantId.trim().length()==0){
+ tenantId = this.tenant.get();
+ }else{
+ tenantId = tenantId.toUpperCase();
+ }
+ if (namespace==null || namespace.trim().length()==0){
+ namespace = Constants.DEFAULT_NAMESPACE;
+ }else{
+ namespace = namespace.toUpperCase();
+ }
+ Map map, parentMap = new HashMap<>();
+ Iterator<String> keys ;
+ try {
+ if (ConfigurationRepository.lookup().isDBAccessible()){
+ keys = ConfigurationUtils.executeSelectSql(ConfigurationRepository.lookup().getConfigurationFor(
+ Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE).getString("fetchkeysql"), new String[]{tenantId+ Constants.KEY_ELEMENTS_DELEMETER+namespace}).iterator();
+ }else{
+ if (key==null || key.trim().length()==0){
+ keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys();
+ }else{
+ keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys(key);
+ }
+ }
+ while(keys.hasNext()){
+ map = parentMap;
+ String k = keys.next();
+
+ if (key!=null && key.trim().length()!=0 && !k.startsWith(key+".")){
+ continue;
+ }
+ String value = getAsString(tenantId, namespace, k);
+ if (key!=null && key.trim().length()!=0 && k.startsWith(key+".")){
+ k = k.substring(key.trim().length()+1);
+ }
+
+ while(k.contains(".")){
+ if (k.contains(".")){
+ String subkey = k.substring(0, k.indexOf("."));
+ k = k.substring(k.indexOf(".")+1);
+ if (!map.containsKey(subkey)){
+ map.put(subkey, map=new HashMap<>());
+ }else{
+ map = (Map)map.get(subkey);
+ }
+ }
+ }
+ map.put(k, value);
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return parentMap;
+ }
+
+
+
+
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationRepository.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationRepository.java
new file mode 100644
index 0000000000..1c3bc1313f
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ConfigurationRepository.java
@@ -0,0 +1,415 @@
+package org.onap.config.impl;
+
+import org.apache.commons.configuration2.CombinedConfiguration;
+import org.apache.commons.configuration2.CompositeConfiguration;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.FileBasedConfiguration;
+import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
+import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.onap.config.ConfigurationUtils;
+import org.onap.config.Constants;
+
+import java.io.File;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The type Configuration repository.
+ */
+public final class ConfigurationRepository {
+
+ /**
+ * The Repo.
+ */
+ static ConfigurationRepository repo;
+ private static Set<String> validCallers = Collections.unmodifiableSet(new HashSet<>(Arrays
+ .asList(ConfigurationChangeNotifier.NotificationData.class.getName(),
+ ConfigurationUtils.class.getName(), CliConfigurationImpl.class.getName(),
+ ConfigurationChangeNotifier.class.getName(), ConfigurationDataSource.class.getName(),
+ ConfigurationImpl.class.getName())));
+
+ static {
+ repo = new ConfigurationRepository();
+ }
+
+ private boolean dbAccessible = true;
+ private Set<String> tenants = new HashSet<>();
+ private Set<String> namespaces = new HashSet<>();
+ private LinkedHashMap<String, ConfigurationHolder> store =
+ new LinkedHashMap<String, ConfigurationHolder>(16, 0.75f, true) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ try {
+ return size() > getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE)
+ .getInt("config.size.max");
+ } catch (Exception exception) {
+ return false;
+ }
+ }
+ };
+
+ private ConfigurationRepository() {
+ if (repo != null) {
+ throw new RuntimeException("Illegal access to configuration.");
+ }
+ tenants.add(Constants.DEFAULT_TENANT);
+ namespaces.add(Constants.DEFAULT_NAMESPACE);
+ }
+
+ /**
+ * Lookup configuration repository.
+ *
+ * @return the configuration repository
+ */
+ public static ConfigurationRepository lookup() {
+ if (validCallers.contains(Thread.currentThread().getStackTrace()[2].getClassName())) {
+ return repo;
+ }
+ return null;
+ }
+
+ /**
+ * Gets tenants.
+ *
+ * @return the tenants
+ */
+ public Set<String> getTenants() {
+ return tenants;
+ }
+
+ /**
+ * Gets namespaces.
+ *
+ * @return the namespaces
+ */
+ public Set<String> getNamespaces() {
+ return namespaces;
+ }
+
+ private void populateTenantsNamespace(String key, boolean sourcedFromDb) {
+ String[] array = key.split(Constants.KEY_ELEMENTS_DELEMETER);
+ if (!array[1].equalsIgnoreCase(Constants.DB_NAMESPACE)) {
+ if (!sourcedFromDb) {
+ dbAccessible = false;
+ }
+ tenants.add(array[0]);
+ namespaces.add(array[1]);
+ }
+ }
+
+ /**
+ * Init tenants and namespaces.
+ */
+ public void initTenantsAndNamespaces() {
+ try {
+ Collection<String> collection = ConfigurationUtils.executeSelectSql(
+ getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE)
+ .getString("fetchnamescql"), new String[]{});
+ Iterator<String> iterator = collection.iterator();
+ while (iterator.hasNext()) {
+ populateTenantsNamespace(iterator.next(), true);
+ }
+ } catch (Exception exception) {
+ //Log this later
+ }
+ }
+
+ /**
+ * Is valid tenant boolean.
+ *
+ * @param tenant the tenant
+ * @return the boolean
+ */
+ public boolean isValidTenant(String tenant) {
+ return tenant == null ? false : tenants.contains(tenant.toUpperCase());
+ }
+
+ /**
+ * Is valid namespace boolean.
+ *
+ * @param namespace the namespace
+ * @return the boolean
+ */
+ public boolean isValidNamespace(String namespace) {
+ return namespace == null ? false : namespaces.contains(namespace.toUpperCase());
+ }
+
+ /**
+ * Gets configuration for.
+ *
+ * @param tenant the tenant
+ * @param namespace the namespace
+ * @return the configuration for
+ * @throws Exception the exception
+ */
+ public Configuration getConfigurationFor(String tenant, String namespace) throws Exception {
+ ConfigurationHolder config;
+ String module = tenant + Constants.KEY_ELEMENTS_DELEMETER + namespace;
+ config = store.get(module);
+ if (config == null) {
+ config = new ConfigurationHolder(ConfigurationUtils
+ .getDbConfigurationBuilder(tenant + Constants.KEY_ELEMENTS_DELEMETER + namespace));
+ store.put(module, config);
+ }
+ return config.getConfiguration(tenant + Constants.KEY_ELEMENTS_DELEMETER + namespace);
+ }
+
+ /**
+ * Populate configurtaion.
+ *
+ * @param key the key
+ * @param builder the builder
+ */
+ public void populateConfigurtaion(String key, Configuration builder) {
+ store.put(key, new ConfigurationHolder(builder));
+ populateTenantsNamespace(key, false);
+ }
+
+ /**
+ * Populate configurtaion.
+ *
+ * @param key the key
+ * @param builder the builder
+ * @throws Exception the exception
+ */
+ public void populateConfigurtaion(String key, BasicConfigurationBuilder builder)
+ throws Exception {
+ store.put(key, new ConfigurationHolder(builder));
+ }
+
+ /**
+ * Populate override configurtaion.
+ *
+ * @param key the key
+ * @param file the file
+ * @throws Exception the exception
+ */
+ public void populateOverrideConfigurtaion(String key, File file) throws Exception {
+ ConfigurationHolder holder = store.get(key);
+ if (holder == null) {
+ if (dbAccessible) {
+ holder = new ConfigurationHolder(ConfigurationUtils.getDbConfigurationBuilder(key));
+ } else {
+ holder = new ConfigurationHolder(new CombinedConfiguration());
+ }
+ store.put(key, holder);
+ }
+ holder.addOverrideConfiguration(file.getAbsolutePath(),
+ ConfigurationUtils.getConfigurationBuilder(file, true));
+ populateTenantsNamespace(key, true);
+ }
+
+ /**
+ * Refresh override configurtaion for.
+ *
+ * @param key the key
+ * @param index the index
+ * @throws Exception the exception
+ */
+ public void refreshOverrideConfigurtaionFor(String key, int index) throws Exception {
+ ConfigurationHolder holder = store.get(key);
+ if (holder != null) {
+ holder.refreshOverrideConfiguration(index);
+ }
+ }
+
+ /**
+ * Remove override configurtaion.
+ *
+ * @param file the file
+ * @throws Exception the exception
+ */
+ public void removeOverrideConfigurtaion(File file) throws Exception {
+ Iterator<String> iterator = new ArrayList(store.keySet()).iterator();
+ while (iterator.hasNext()) {
+ ConfigurationHolder holder = store.get(iterator.next());
+ if (holder.containsOverrideConfiguration(file.getAbsolutePath())) {
+ holder.removeOverrideConfiguration(file.getAbsolutePath());
+ }
+ }
+
+ }
+
+ private class ConfigurationHolder {
+
+ /**
+ * The Builder.
+ */
+ BasicConfigurationBuilder<Configuration> builder;
+ /**
+ * The Last configuration build time.
+ */
+ Timestamp lastConfigurationBuildTime;
+ /**
+ * The Config.
+ */
+ Configuration config;
+ /**
+ * The Composite.
+ */
+ Configuration composite;
+ /**
+ * The Last config change timestamp.
+ */
+ Timestamp lastConfigChangeTimestamp;
+ private Map<String, FileBasedConfigurationBuilder<FileBasedConfiguration>>
+ overrideConfiguration = new LinkedHashMap<>();
+
+
+ /**
+ * Instantiates a new Configuration holder.
+ *
+ * @param builder the builder
+ */
+ public ConfigurationHolder(BasicConfigurationBuilder builder) {
+ this.builder = builder;
+ }
+
+ /**
+ * Instantiates a new Configuration holder.
+ *
+ * @param builder the builder
+ */
+ public ConfigurationHolder(Configuration builder) {
+ this.config = builder;
+ }
+
+ /**
+ * Refresh override configuration.
+ *
+ * @param index the index
+ */
+ public void refreshOverrideConfiguration(int index) {
+ int count = -1;
+ for (FileBasedConfigurationBuilder overrides : overrideConfiguration.values()) {
+ try {
+ if (++count == index) {
+ overrides.save();
+ overrides.resetResult();
+ }
+ } catch (ConfigurationException exception) {
+ //do nothing
+ }
+ }
+ }
+
+ /**
+ * Add override configuration.
+ *
+ * @param path the path
+ * @param builder the builder
+ */
+ public void addOverrideConfiguration(String path,
+ BasicConfigurationBuilder<FileBasedConfiguration> builder) {
+ overrideConfiguration.put(path.toUpperCase(), (FileBasedConfigurationBuilder) builder);
+ getEffectiveConfiguration(config, overrideConfiguration.values());
+ }
+
+ /**
+ * Remove override configuration.
+ *
+ * @param path the path
+ */
+ public void removeOverrideConfiguration(String path) {
+ overrideConfiguration.remove(path.toUpperCase());
+ getEffectiveConfiguration(config, overrideConfiguration.values());
+ }
+
+ /**
+ * Contains override configuration boolean.
+ *
+ * @param path the path
+ * @return the boolean
+ */
+ public boolean containsOverrideConfiguration(String path) {
+ return overrideConfiguration.containsKey(path.toUpperCase());
+ }
+
+ /**
+ * Gets configuration.
+ *
+ * @param namespace the namespace
+ * @return the configuration
+ * @throws Exception the exception
+ */
+ public Configuration getConfiguration(String namespace) throws Exception {
+ if (config == null) {
+ config = builder.getConfiguration();
+ lastConfigurationBuildTime = new Timestamp(System.currentTimeMillis());
+ } else if (lastConfigurationBuildTime != null
+ && System.currentTimeMillis() - lastConfigurationBuildTime.getTime()
+ > getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE)
+ .getInt("config.refresh.interval")) {
+ Timestamp temp = getLastUpdateTimestampFor(namespace);
+ if ((temp != null)
+ && (lastConfigChangeTimestamp == null
+ || temp.getTime() > lastConfigChangeTimestamp.getTime())) {
+ builder.resetResult();
+ config = builder.getConfiguration();
+ lastConfigChangeTimestamp = temp;
+ getEffectiveConfiguration(config, overrideConfiguration.values());
+ }
+ lastConfigurationBuildTime = new Timestamp(System.currentTimeMillis());
+ }
+ if (composite == null && overrideConfiguration.size() != 0) {
+ composite = getEffectiveConfiguration(config, overrideConfiguration.values());
+ }
+ return overrideConfiguration.size() == 0 ? config : composite;
+ }
+
+ private Configuration getEffectiveConfiguration(Configuration configuration,
+ Collection<FileBasedConfigurationBuilder<FileBasedConfiguration>> list) {
+ try {
+ CompositeConfiguration cc = new CompositeConfiguration();
+ for (FileBasedConfigurationBuilder<FileBasedConfiguration> b : list) {
+ cc.addConfiguration(b.getConfiguration());
+ }
+ cc.addConfiguration(configuration);
+ composite = cc;
+ return composite;
+ } catch (Exception exception) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets last update timestamp for.
+ *
+ * @param namespace the namespace
+ * @return the last update timestamp for
+ */
+ public Timestamp getLastUpdateTimestampFor(String namespace) {
+ Timestamp timestamp = null;
+
+ try {
+ Collection<String> collection = ConfigurationUtils.executeSelectSql(
+ getConfigurationFor(Constants.DEFAULT_TENANT, Constants.DB_NAMESPACE)
+ .getString("fetchlastchangecql"), new String[]{namespace});
+ if (!collection.isEmpty()) {
+ timestamp = new Timestamp(Long.valueOf(((ArrayList) collection).get(0).toString()));
+ }
+ } catch (Exception exception) {
+ //Log this later
+ }
+
+ return timestamp;
+ }
+
+
+ }
+
+ public boolean isDBAccessible(){
+ return dbAccessible;
+ }
+
+
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ContextListener.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ContextListener.java
new file mode 100644
index 0000000000..cf87db936d
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/ContextListener.java
@@ -0,0 +1,28 @@
+package org.onap.config.impl;
+
+import static org.onap.config.Constants.MBEAN_NAME;
+
+import java.lang.management.ManagementFactory;
+import javax.management.ObjectName;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import org.onap.config.api.ConfigurationManager;
+
+@WebListener
+public class ContextListener implements ServletContextListener {
+
+ @Override
+ public void contextDestroyed(ServletContextEvent arg0) {
+ try {
+ ManagementFactory.getPlatformMBeanServer().unregisterMBean(new ObjectName(MBEAN_NAME));
+ } catch (Exception exception) {
+ exception.printStackTrace();
+ }
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent arg0) {
+ ConfigurationManager.lookup();
+ }
+}
diff --git a/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/YamlConfiguration.java b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/YamlConfiguration.java
new file mode 100644
index 0000000000..dc6785fd73
--- /dev/null
+++ b/common/onap-common-configuration-management/onap-configuration-management-core/src/main/java/org/onap/config/impl/YamlConfiguration.java
@@ -0,0 +1,18 @@
+package org.onap.config.impl;
+
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.virtlink.commons.configuration2.jackson.JacksonConfiguration;
+import org.apache.commons.configuration2.HierarchicalConfiguration;
+import org.apache.commons.configuration2.tree.ImmutableNode;
+
+public class YamlConfiguration extends JacksonConfiguration {
+
+ protected YamlConfiguration(HierarchicalConfiguration<ImmutableNode> config) {
+ super(new YAMLFactory(), config);
+ }
+
+ public YamlConfiguration() {
+ super(new YAMLFactory());
+ }
+
+}