2 * Copyright © 2016-2018 European Support Limited
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.onap.config.impl;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Modifier;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.List;
32 import java.util.function.Predicate;
33 import org.onap.config.ConfigurationUtils;
34 import org.onap.config.Constants;
35 import org.onap.config.NonConfigResource;
36 import org.onap.config.api.Config;
37 import org.onap.config.api.ConfigurationChangeListener;
38 import org.onap.config.api.Hint;
40 public class ConfigurationImpl implements org.onap.config.api.Configuration {
42 private static final String KEY_CANNOT_BE_NULL = "Key can't be null.";
43 private static final ThreadLocal<String> tenant = ThreadLocal.withInitial(() -> Constants.DEFAULT_TENANT);
45 private static boolean instantiated = false;
47 ConfigurationChangeNotifier changeNotifier;
49 public ConfigurationImpl() throws Exception {
50 if (instantiated || !CliConfigurationImpl.class.isAssignableFrom(this.getClass())) {
51 throw new RuntimeException("Illegal access to configuration.");
53 Map<String, AggregateConfiguration> moduleConfigStore = new HashMap<>();
54 List<URL> classpathResources = ConfigurationUtils.getAllClassPathResources();
55 Predicate<URL> predicate = ConfigurationUtils::isConfig;
56 for (URL url : classpathResources) {
57 if (predicate.test(url)) {
58 String moduleName = ConfigurationUtils.getConfigurationRepositoryKey(url);
59 AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
60 if (moduleConfig == null) {
61 moduleConfig = new AggregateConfiguration();
62 moduleConfigStore.put(moduleName, moduleConfig);
64 moduleConfig.addConfig(url);
66 NonConfigResource.add(url);
69 String configLocation = System.getProperty("config.location");
70 if (configLocation != null && configLocation.trim().length() > 0) {
71 File root = new File(configLocation);
72 Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
73 Predicate<File> filePredicate = ConfigurationUtils::isConfig;
74 for (File file : filesystemResources) {
75 if (filePredicate.test(file)) {
76 String moduleName = ConfigurationUtils.getConfigurationRepositoryKey(file);
77 AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
78 if (moduleConfig == null) {
79 moduleConfig = new AggregateConfiguration();
80 moduleConfigStore.put(moduleName, moduleConfig);
82 moduleConfig.addConfig(file);
84 NonConfigResource.add(file);
88 String tenantConfigLocation = System.getProperty("tenant.config.location");
89 if (tenantConfigLocation != null && tenantConfigLocation.trim().length() > 0) {
90 File root = new File(tenantConfigLocation);
91 Collection<File> tenantsRoot = ConfigurationUtils.getAllFiles(root, false, true);
92 Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
93 Predicate<File> filePredicate = ConfigurationUtils::isConfig;
94 for (File file : filesystemResources) {
95 if (filePredicate.test(file)) {
96 String moduleName = ConfigurationUtils.getNamespace(file);
97 for (File tenantFileRoot : tenantsRoot) {
98 if (file.getAbsolutePath().startsWith(tenantFileRoot.getAbsolutePath())) {
99 moduleName = ConfigurationUtils.getConfigurationRepositoryKey(
100 (tenantFileRoot.getName().toUpperCase() + Constants.TENANT_NAMESPACE_SEPARATOR
101 + moduleName).split(Constants.TENANT_NAMESPACE_SEPARATOR));
104 AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
105 if (moduleConfig == null) {
106 moduleConfig = new AggregateConfiguration();
107 moduleConfigStore.put(moduleName, moduleConfig);
109 moduleConfig.addConfig(file);
113 populateFinalConfigurationIncrementally(moduleConfigStore);
114 String nodeConfigLocation = System.getProperty("node.config.location");
115 if (nodeConfigLocation != null && nodeConfigLocation.trim().length() > 0) {
116 File root = new File(nodeConfigLocation);
117 Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
118 Predicate<File> filePredicate = ConfigurationUtils::isConfig;
119 for (File file : filesystemResources) {
120 if (filePredicate.test(file)) {
121 ConfigurationRepository.lookup().populateOverrideConfiguration(
122 ConfigurationUtils.getConfigurationRepositoryKey(
123 ConfigurationUtils.getNamespace(file).split(Constants.TENANT_NAMESPACE_SEPARATOR)),
129 changeNotifier = new ConfigurationChangeNotifier(moduleConfigStore);
132 private void populateFinalConfigurationIncrementally(Map<String, AggregateConfiguration> configs) {
134 if (configs.get(Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELIMETER + Constants.DB_NAMESPACE) != null) {
135 ConfigurationRepository.lookup().populateConfiguration(
136 Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELIMETER + Constants.DB_NAMESPACE,
137 configs.remove(Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELIMETER + Constants.DB_NAMESPACE)
138 .getFinalConfiguration());
141 Set<String> modules = configs.keySet();
142 for (String module : modules) {
143 ConfigurationRepository.lookup().populateConfiguration(module, configs.get(module).getFinalConfiguration());
148 public <T> T get(String tenant, String namespace, String key, Class<T> clazz, Hint... hints) {
150 String[] tenantNamespaceArray;
151 if (tenant == null && namespace != null) {
152 tenantNamespaceArray = namespace.split(Constants.TENANT_NAMESPACE_SEPARATOR);
153 if (tenantNamespaceArray.length > 1) {
154 tenant = tenantNamespaceArray[0];
155 namespace = tenantNamespaceArray[1];
159 tenant = ConfigurationRepository.lookup().isValidTenant(tenant) ? tenant.toUpperCase()
160 : Constants.DEFAULT_TENANT;
161 namespace = ConfigurationRepository.lookup().isValidNamespace(namespace) ? namespace.toUpperCase()
162 : Constants.DEFAULT_NAMESPACE;
164 returnValue = (T) getInternal(tenant, namespace, key, clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
165 hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
166 if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
167 && !Constants.DEFAULT_TENANT.equals(tenant)) {
168 returnValue = (T) getInternal(Constants.DEFAULT_TENANT, namespace, key,
169 clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
170 hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
172 if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
173 && !Constants.DEFAULT_NAMESPACE.equals(namespace)) {
174 returnValue = (T) getInternal(tenant, Constants.DEFAULT_NAMESPACE, key,
175 clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
176 hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
178 if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
179 && !Constants.DEFAULT_NAMESPACE.equals(namespace) && !Constants.DEFAULT_TENANT.equals(tenant)) {
180 returnValue = (T) getInternal(Constants.DEFAULT_TENANT, Constants.DEFAULT_NAMESPACE, key,
181 clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
182 hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
184 if (returnValue == null && clazz.isPrimitive()) {
185 return (T) ConfigurationUtils.getDefaultFor(clazz);
192 public void addConfigurationChangeListener(String tenant, String namespace, String key,
193 ConfigurationChangeListener myself) {
194 tenant = ConfigurationRepository.lookup().isValidTenant(tenant) ? tenant.toUpperCase()
195 : Constants.DEFAULT_TENANT;
196 namespace = ConfigurationRepository.lookup().isValidNamespace(namespace) ? namespace.toUpperCase()
197 : Constants.DEFAULT_NAMESPACE;
198 if (key == null || key.trim().length() == 0) {
199 throw new IllegalArgumentException(KEY_CANNOT_BE_NULL);
201 if (myself == null) {
202 throw new IllegalArgumentException("ConfigurationChangeListener instance is null.");
205 changeNotifier.notifyChangesTowards(tenant, namespace, key, myself);
206 } catch (Exception exception) {
207 exception.printStackTrace();
212 public void removeConfigurationChangeListener(String tenant, String namespace, String key,
213 ConfigurationChangeListener myself) {
214 tenant = ConfigurationRepository.lookup().isValidTenant(tenant) ? tenant.toUpperCase()
215 : Constants.DEFAULT_TENANT;
216 namespace = ConfigurationRepository.lookup().isValidNamespace(namespace) ? namespace.toUpperCase()
217 : Constants.DEFAULT_NAMESPACE;
218 if (key == null || key.trim().length() == 0) {
219 throw new IllegalArgumentException(KEY_CANNOT_BE_NULL);
222 changeNotifier.stopNotificationTowards(tenant, namespace, key, myself);
223 } catch (Exception exception) {
224 exception.printStackTrace();
229 public <T> Map<String, T> populateMap(String tenantId, String namespace, String key, Class<T> clazz) {
230 if (tenantId == null || tenantId.trim().length() == 0) {
231 tenantId = tenant.get();
233 tenantId = tenantId.toUpperCase();
235 if (namespace == null || namespace.trim().length() == 0) {
236 namespace = Constants.DEFAULT_NAMESPACE;
238 namespace = namespace.toUpperCase();
240 Map<String, T> map = new HashMap<>();
241 Iterator<String> keys;
243 keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys(key);
244 while (keys.hasNext()) {
245 String k = keys.next();
246 if (k.startsWith(key + ".")) {
247 k = k.substring(key.length() + 1);
248 String subkey = k.substring(0, k.indexOf("."));
249 if (!map.containsKey(subkey)) {
250 map.put(subkey, get(tenantId, namespace, key + "." + subkey, clazz));
254 } catch (Exception e) {
261 public Map generateMap(String tenantId, String namespace, String key) {
262 if (tenantId == null || tenantId.trim().length() == 0) {
263 tenantId = tenant.get();
265 tenantId = tenantId.toUpperCase();
267 if (namespace == null || namespace.trim().length() == 0) {
268 namespace = Constants.DEFAULT_NAMESPACE;
270 namespace = namespace.toUpperCase();
273 Map parentMap = new HashMap<>();
274 Iterator<String> keys;
276 if (key == null || key.trim().length() == 0) {
277 keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys();
279 keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys(key);
281 while (keys.hasNext()) {
283 String k = keys.next();
285 if (key != null && key.trim().length() != 0 && !k.startsWith(key + ".")) {
288 String value = getAsString(tenantId, namespace, k);
289 if (key != null && key.trim().length() != 0 && k.startsWith(key + ".")) {
290 k = k.substring(key.trim().length() + 1);
293 while (k.contains(".")) {
294 if (k.contains(".")) {
295 String subkey = k.substring(0, k.indexOf("."));
296 k = k.substring(k.indexOf(".") + 1);
297 if (!map.containsKey(subkey)) {
298 map.put(subkey, map = new HashMap<>());
300 map = (Map) map.get(subkey);
306 } catch (Exception e) {
312 protected <T> T getInternal(String tenant, String namespace, String key, Class<T> clazz, Hint... hints) {
313 int processingHints = Hint.DEFAULT.value();
315 for (Hint hint : hints) {
316 processingHints = processingHints | hint.value();
320 if (tenant == null || tenant.trim().length() == 0) {
321 tenant = ConfigurationImpl.tenant.get();
323 tenant = tenant.toUpperCase();
325 if (namespace == null || namespace.trim().length() == 0) {
326 namespace = Constants.DEFAULT_NAMESPACE;
328 namespace = namespace.toUpperCase();
330 if ((key == null || key.trim().length() == 0) && !clazz.isAnnotationPresent(Config.class)) {
331 throw new IllegalArgumentException(KEY_CANNOT_BE_NULL);
334 throw new IllegalArgumentException("clazz is null.");
336 if (clazz.isPrimitive()) {
337 clazz = getWrapperClass(clazz);
340 if (ConfigurationUtils.isWrapperClass(clazz) || clazz.isPrimitive()) {
341 Object obj = ConfigurationUtils.getProperty(
342 ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace), key, processingHints);
344 if (ConfigurationUtils.isCollection(obj.toString())) {
345 obj = ConfigurationUtils.getCollectionString(obj.toString());
347 String value = obj.toString().split(",")[0];
348 value = ConfigurationUtils.processVariablesIfPresent(tenant, namespace, value);
349 return (T) getValue(value, clazz.isPrimitive() ? getWrapperClass(clazz) : clazz, processingHints);
353 } else if (clazz.isArray() && (clazz.getComponentType().isPrimitive() || ConfigurationUtils.isWrapperClass(
354 clazz.getComponentType()))) {
355 Object obj = ConfigurationUtils.getProperty(
356 ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace), key, processingHints);
358 Class componentClass = clazz.getComponentType();
359 if (clazz.getComponentType().isPrimitive()) {
360 componentClass = getWrapperClass(clazz.getComponentType());
362 String collString = ConfigurationUtils.getCollectionString(obj.toString());
363 ArrayList<String> tempCollection = new ArrayList<>();
364 for (String itemValue : collString.split(",")) {
365 tempCollection.add(ConfigurationUtils.processVariablesIfPresent(tenant, namespace, itemValue));
367 Collection<T> collection =
368 convert(ConfigurationUtils.getCollectionString(Arrays.toString(tempCollection.toArray())),
369 componentClass, processingHints);
370 if (clazz.getComponentType().isPrimitive()) {
371 return (T) ConfigurationUtils.getPrimitiveArray(collection, clazz.getComponentType());
373 return (T) collection.toArray(getZeroLengthArrayFor(getWrapperClass(clazz.getComponentType())));
378 } else if (clazz.isAnnotationPresent(Config.class)) {
379 return read(tenant, namespace, clazz, (key == null || key.trim().length() == 0) ? "" : (key + "."),
382 throw new IllegalArgumentException(
383 "Only primitive classes, wrapper classes, corresponding array classes and any "
384 + "class decorated with @org.openecomp.config.api.Config are allowed as argument.");
386 } catch (Exception exception) {
387 exception.printStackTrace();
392 private <T> T read(String tenant, String namespace, Class<T> clazz, String keyPrefix, Hint... hints)
394 Config confAnnotation = clazz.getAnnotation(Config.class);
395 if (confAnnotation != null && confAnnotation.key().length() > 0 && !keyPrefix.endsWith(".")) {
396 keyPrefix += (confAnnotation.key() + ".");
398 Constructor<T> constructor = clazz.getDeclaredConstructor();
399 constructor.setAccessible(true);
400 T objToReturn = constructor.newInstance();
401 for (Field field : clazz.getDeclaredFields()) {
402 field.setAccessible(true);
403 Config fieldConfAnnotation = field.getAnnotation(Config.class);
404 if (fieldConfAnnotation != null) {
405 if (field.getType().isPrimitive() || ConfigurationUtils.isWrapperClass(field.getType()) || (
406 field.getType().isArray() && (field.getType().getComponentType().isPrimitive()
407 || ConfigurationUtils.isWrapperClass(
408 field.getType().getComponentType())))
409 || field.getType().getAnnotation(Config.class) != null) {
410 field.set(objToReturn,
411 get(tenant, namespace, keyPrefix + fieldConfAnnotation.key(), field.getType(), hints));
412 } else if (Collection.class.isAssignableFrom(field.getType())) {
413 Object obj = get(tenant, namespace, keyPrefix + fieldConfAnnotation.key(),
414 ConfigurationUtils.getArrayClass(ConfigurationUtils.getCollectionGenericType(field)),
417 List list = Arrays.asList((Object[]) obj);
418 Class clazzToInstantiate;
419 if (field.getType().isInterface()) {
420 clazzToInstantiate = ConfigurationUtils.getConcreteCollection(field.getType()).getClass();
421 } else if (Modifier.isAbstract(field.getType().getModifiers())) {
423 ConfigurationUtils.getCompatibleCollectionForAbstractDef(field.getType())
426 clazzToInstantiate = field.getType();
428 Constructor construct = getConstructorWithArguments(clazzToInstantiate, Collection.class);
429 if (construct != null) {
430 construct.setAccessible(true);
431 field.set(objToReturn, construct.newInstance(list));
432 } else if ((construct = getConstructorWithArguments(clazzToInstantiate, Integer.class,
433 Boolean.class, Collection.class)) != null) {
434 construct.setAccessible(true);
435 field.set(objToReturn, construct.newInstance(list.size(), true, list));
438 } else if (Map.class.isAssignableFrom(field.getType())) {
439 field.set(objToReturn, generateMap(tenant, namespace, keyPrefix + fieldConfAnnotation.key()));
446 private Constructor getConstructorWithArguments(Class clazz, Class... classes) {
448 return clazz.getDeclaredConstructor(classes);
449 } catch (Exception exception) {
454 private Class getWrapperClass(Class clazz) {
455 if (byte.class == clazz) {
457 } else if (short.class == clazz) {
459 } else if (int.class == clazz) {
460 return Integer.class;
461 } else if (long.class == clazz) {
463 } else if (float.class == clazz) {
465 } else if (double.class == clazz) {
467 } else if (char.class == clazz) {
468 return Character.class;
469 } else if (boolean.class == clazz) {
470 return Boolean.class;
475 private <T> T getValue(Object obj, Class<T> clazz, int processingHint) {
476 if (obj == null || obj.toString().trim().length() == 0) {
479 obj = obj.toString().trim();
481 if (String.class.equals(clazz)) {
482 if (obj.toString().startsWith("@") && ConfigurationUtils.isExternalLookup(processingHint)) {
483 String contents = ConfigurationUtils.getFileContents(
484 NonConfigResource.locate(obj.toString().substring(1).trim()));
485 if (contents == null) {
486 contents = ConfigurationUtils.getFileContents(obj.toString().substring(1).trim());
488 if (contents != null) {
492 return (T) obj.toString();
493 } else if (Number.class.isAssignableFrom(clazz)) {
494 Double doubleValue = Double.valueOf(obj.toString());
495 switch (clazz.getName()) {
496 case "java.lang.Byte":
497 Byte byteVal = doubleValue.byteValue();
499 case "java.lang.Short":
500 Short shortVal = doubleValue.shortValue();
502 case "java.lang.Integer":
503 Integer intVal = doubleValue.intValue();
505 case "java.lang.Long":
506 Long longVal = doubleValue.longValue();
508 case "java.lang.Float":
509 Float floatVal = doubleValue.floatValue();
511 case "java.lang.Double":
512 return (T) doubleValue;
515 } else if (Boolean.class.equals(clazz)) {
516 return (T) Boolean.valueOf(obj.toString());
517 } else if (Character.class.equals(clazz)) {
518 return (T) Character.valueOf(obj.toString().charAt(0));
523 private <T> T[] getZeroLengthArrayFor(Class<T> clazz) {
525 if (clazz == int.class) {
527 } else if (clazz == byte.class) {
529 } else if (clazz == short.class) {
530 obj = new short[] {};
531 } else if (clazz == long.class) {
533 } else if (clazz == float.class) {
534 obj = new float[] {};
535 } else if (clazz == double.class) {
536 obj = new double[] {};
537 } else if (clazz == boolean.class) {
538 obj = new boolean[] {};
539 } else if (clazz == char.class) {
541 } else if (clazz == Byte.class) {
543 } else if (clazz == Short.class) {
544 obj = new Short[] {};
545 } else if (clazz == Integer.class) {
546 obj = new Integer[] {};
547 } else if (clazz == Long.class) {
549 } else if (clazz == Float.class) {
550 obj = new Float[] {};
551 } else if (clazz == Double.class) {
552 obj = new Double[] {};
553 } else if (clazz == Boolean.class) {
554 obj = new Boolean[] {};
555 } else if (clazz == Character.class) {
556 obj = new Character[] {};
557 } else if (clazz == String.class) {
558 obj = new String[] {};
563 private <T> Collection<T> convert(String commaSaperatedValues, Class<T> clazz, int processingHints) {
564 ArrayList<T> collection = new ArrayList<>();
565 for (String value : commaSaperatedValues.split(",")) {
567 T type1 = getValue(value, clazz, processingHints);
569 collection.add(type1);
571 } catch (RuntimeException re) {
578 public void shutdown() {
579 if (changeNotifier != null) {
581 changeNotifier.shutdown();
582 } catch (Exception exception) {
583 exception.printStackTrace();