Removed support of dynamic configuration
[sdc.git] / common / onap-common-configuration-management / onap-configuration-management-core / src / main / java / org / onap / config / impl / ConfigurationImpl.java
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.onap.config.impl;
18
19 import java.io.File;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Modifier;
23 import java.net.URL;
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;
30 import java.util.Map;
31 import java.util.Set;
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.Hint;
38
39 public class ConfigurationImpl implements org.onap.config.api.Configuration {
40
41     private static final String KEY_CANNOT_BE_NULL = "Key can't be null.";
42     private static final ThreadLocal<String> tenant = ThreadLocal.withInitial(() -> Constants.DEFAULT_TENANT);
43
44     private static boolean instantiated = false;
45
46     public ConfigurationImpl() throws Exception {
47         if (instantiated || !CliConfigurationImpl.class.isAssignableFrom(this.getClass())) {
48             throw new RuntimeException("Illegal access to configuration.");
49         }
50         Map<String, AggregateConfiguration> moduleConfigStore = new HashMap<>();
51         List<URL> classpathResources = ConfigurationUtils.getAllClassPathResources();
52         Predicate<URL> predicate = ConfigurationUtils::isConfig;
53         for (URL url : classpathResources) {
54             if (predicate.test(url)) {
55                 String moduleName = ConfigurationUtils.getConfigurationRepositoryKey(url);
56                 AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
57                 if (moduleConfig == null) {
58                     moduleConfig = new AggregateConfiguration();
59                     moduleConfigStore.put(moduleName, moduleConfig);
60                 }
61                 moduleConfig.addConfig(url);
62             } else {
63                 NonConfigResource.add(url);
64             }
65         }
66         String configLocation = System.getProperty("config.location");
67         if (configLocation != null && configLocation.trim().length() > 0) {
68             File root = new File(configLocation);
69             Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
70             Predicate<File> filePredicate = ConfigurationUtils::isConfig;
71             for (File file : filesystemResources) {
72                 if (filePredicate.test(file)) {
73                     String moduleName = ConfigurationUtils.getConfigurationRepositoryKey(file);
74                     AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
75                     if (moduleConfig == null) {
76                         moduleConfig = new AggregateConfiguration();
77                         moduleConfigStore.put(moduleName, moduleConfig);
78                     }
79                     moduleConfig.addConfig(file);
80                 } else {
81                     NonConfigResource.add(file);
82                 }
83             }
84         }
85         String tenantConfigLocation = System.getProperty("tenant.config.location");
86         if (tenantConfigLocation != null && tenantConfigLocation.trim().length() > 0) {
87             File root = new File(tenantConfigLocation);
88             Collection<File> tenantsRoot = ConfigurationUtils.getAllFiles(root, false, true);
89             Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
90             Predicate<File> filePredicate = ConfigurationUtils::isConfig;
91             for (File file : filesystemResources) {
92                 if (filePredicate.test(file)) {
93                     String moduleName = ConfigurationUtils.getNamespace(file);
94                     for (File tenantFileRoot : tenantsRoot) {
95                         if (file.getAbsolutePath().startsWith(tenantFileRoot.getAbsolutePath())) {
96                             moduleName = ConfigurationUtils.getConfigurationRepositoryKey(
97                                     (tenantFileRoot.getName().toUpperCase() + Constants.TENANT_NAMESPACE_SEPARATOR
98                                              + moduleName).split(Constants.TENANT_NAMESPACE_SEPARATOR));
99                         }
100                     }
101                     AggregateConfiguration moduleConfig = moduleConfigStore.get(moduleName);
102                     if (moduleConfig == null) {
103                         moduleConfig = new AggregateConfiguration();
104                         moduleConfigStore.put(moduleName, moduleConfig);
105                     }
106                     moduleConfig.addConfig(file);
107                 }
108             }
109         }
110         populateFinalConfigurationIncrementally(moduleConfigStore);
111         String nodeConfigLocation = System.getProperty("node.config.location");
112         if (nodeConfigLocation != null && nodeConfigLocation.trim().length() > 0) {
113             File root = new File(nodeConfigLocation);
114             Collection<File> filesystemResources = ConfigurationUtils.getAllFiles(root, true, false);
115             Predicate<File> filePredicate = ConfigurationUtils::isConfig;
116             for (File file : filesystemResources) {
117                 if (filePredicate.test(file)) {
118                     ConfigurationRepository.lookup().populateOverrideConfiguration(
119                             ConfigurationUtils.getConfigurationRepositoryKey(
120                                     ConfigurationUtils.getNamespace(file).split(Constants.TENANT_NAMESPACE_SEPARATOR)),
121                             file);
122                 }
123             }
124         }
125         instantiated = true;
126     }
127
128     private void populateFinalConfigurationIncrementally(Map<String, AggregateConfiguration> configs) {
129
130         if (configs.get(Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELIMITER + Constants.DB_NAMESPACE) != null) {
131             ConfigurationRepository.lookup().populateConfiguration(
132                     Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELIMITER + Constants.DB_NAMESPACE,
133                     configs.remove(Constants.DEFAULT_TENANT + Constants.KEY_ELEMENTS_DELIMITER + Constants.DB_NAMESPACE)
134                             .getFinalConfiguration());
135         }
136
137         Set<String> modules = configs.keySet();
138         for (String module : modules) {
139             ConfigurationRepository.lookup().populateConfiguration(module, configs.get(module).getFinalConfiguration());
140         }
141     }
142
143     @Override
144     public <T> T get(String tenant, String namespace, String key, Class<T> clazz, Hint... hints) {
145
146         String[] tenantNamespaceArray;
147         if (tenant == null && namespace != null) {
148             tenantNamespaceArray = namespace.split(Constants.TENANT_NAMESPACE_SEPARATOR);
149             if (tenantNamespaceArray.length > 1) {
150                 tenant = tenantNamespaceArray[0];
151                 namespace = tenantNamespaceArray[1];
152             }
153         }
154
155         tenant = ConfigurationRepository.lookup().isValidTenant(tenant) ? tenant.toUpperCase()
156                          : Constants.DEFAULT_TENANT;
157         namespace = ConfigurationRepository.lookup().isValidNamespace(namespace) ? namespace.toUpperCase()
158                             : Constants.DEFAULT_NAMESPACE;
159         T returnValue;
160         returnValue = (T) getInternal(tenant, namespace, key, clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
161                 hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
162         if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
163                     && !Constants.DEFAULT_TENANT.equals(tenant)) {
164             returnValue = (T) getInternal(Constants.DEFAULT_TENANT, namespace, key,
165                     clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
166                     hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
167         }
168         if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
169                     && !Constants.DEFAULT_NAMESPACE.equals(namespace)) {
170             returnValue = (T) getInternal(tenant, Constants.DEFAULT_NAMESPACE, key,
171                     clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
172                     hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
173         }
174         if ((returnValue == null || ConfigurationUtils.isZeroLengthArray(clazz, returnValue))
175                     && !Constants.DEFAULT_NAMESPACE.equals(namespace) && !Constants.DEFAULT_TENANT.equals(tenant)) {
176             returnValue = (T) getInternal(Constants.DEFAULT_TENANT, Constants.DEFAULT_NAMESPACE, key,
177                     clazz.isPrimitive() ? getWrapperClass(clazz) : clazz,
178                     hints == null || hints.length == 0 ? new Hint[] {Hint.EXTERNAL_LOOKUP, Hint.NODE_SPECIFIC} : hints);
179         }
180         if (returnValue == null && clazz.isPrimitive()) {
181             return (T) ConfigurationUtils.getDefaultFor(clazz);
182         } else {
183             return returnValue;
184         }
185     }
186
187     @Override
188     public <T> Map<String, T> populateMap(String tenantId, String namespace, String key, Class<T> clazz) {
189         if (tenantId == null || tenantId.trim().length() == 0) {
190             tenantId = tenant.get();
191         } else {
192             tenantId = tenantId.toUpperCase();
193         }
194         if (namespace == null || namespace.trim().length() == 0) {
195             namespace = Constants.DEFAULT_NAMESPACE;
196         } else {
197             namespace = namespace.toUpperCase();
198         }
199         Map<String, T> map = new HashMap<>();
200         Iterator<String> keys;
201         try {
202             keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys(key);
203             while (keys.hasNext()) {
204                 String k = keys.next();
205                 if (k.startsWith(key + ".")) {
206                     k = k.substring(key.length() + 1);
207                     String subkey = k.substring(0, k.indexOf("."));
208                     if (!map.containsKey(subkey)) {
209                         map.put(subkey, get(tenantId, namespace, key + "." + subkey, clazz));
210                     }
211                 }
212             }
213         } catch (Exception e) {
214             e.printStackTrace();
215         }
216         return map;
217     }
218
219     @Override
220     public Map generateMap(String tenantId, String namespace, String key) {
221         if (tenantId == null || tenantId.trim().length() == 0) {
222             tenantId = tenant.get();
223         } else {
224             tenantId = tenantId.toUpperCase();
225         }
226         if (namespace == null || namespace.trim().length() == 0) {
227             namespace = Constants.DEFAULT_NAMESPACE;
228         } else {
229             namespace = namespace.toUpperCase();
230         }
231         Map map;
232         Map parentMap = new HashMap<>();
233         Iterator<String> keys;
234         try {
235             if (key == null || key.trim().length() == 0) {
236                 keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys();
237             } else {
238                 keys = ConfigurationRepository.lookup().getConfigurationFor(tenantId, namespace).getKeys(key);
239             }
240             while (keys.hasNext()) {
241                 map = parentMap;
242                 String k = keys.next();
243
244                 if (key != null && key.trim().length() != 0 && !k.startsWith(key + ".")) {
245                     continue;
246                 }
247                 String value = getAsString(tenantId, namespace, k);
248                 if (key != null && key.trim().length() != 0 && k.startsWith(key + ".")) {
249                     k = k.substring(key.trim().length() + 1);
250                 }
251
252                 while (k.contains(".")) {
253                     if (k.contains(".")) {
254                         String subkey = k.substring(0, k.indexOf("."));
255                         k = k.substring(k.indexOf(".") + 1);
256                         if (!map.containsKey(subkey)) {
257                             map.put(subkey, map = new HashMap<>());
258                         } else {
259                             map = (Map) map.get(subkey);
260                         }
261                     }
262                 }
263                 map.put(k, value);
264             }
265         } catch (Exception e) {
266             e.printStackTrace();
267         }
268         return parentMap;
269     }
270
271     protected <T> T getInternal(String tenant, String namespace, String key, Class<T> clazz, Hint... hints) {
272         int processingHints = Hint.DEFAULT.value();
273         if (hints != null) {
274             for (Hint hint : hints) {
275                 processingHints = processingHints | hint.value();
276             }
277         }
278
279         if (tenant == null || tenant.trim().length() == 0) {
280             tenant = ConfigurationImpl.tenant.get();
281         } else {
282             tenant = tenant.toUpperCase();
283         }
284         if (namespace == null || namespace.trim().length() == 0) {
285             namespace = Constants.DEFAULT_NAMESPACE;
286         } else {
287             namespace = namespace.toUpperCase();
288         }
289         if ((key == null || key.trim().length() == 0) && !clazz.isAnnotationPresent(Config.class)) {
290             throw new IllegalArgumentException(KEY_CANNOT_BE_NULL);
291         }
292         if (clazz == null) {
293             throw new IllegalArgumentException("clazz is null.");
294         }
295         if (clazz.isPrimitive()) {
296             clazz = getWrapperClass(clazz);
297         }
298         try {
299             if (ConfigurationUtils.isWrapperClass(clazz) || clazz.isPrimitive()) {
300                 Object obj = ConfigurationUtils.getProperty(
301                         ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace), key, processingHints);
302                 if (obj != null) {
303                     if (ConfigurationUtils.isCollection(obj.toString())) {
304                         obj = ConfigurationUtils.getCollectionString(obj.toString());
305                     }
306                     String value = obj.toString().split(",")[0];
307                     value = ConfigurationUtils.processVariablesIfPresent(tenant, namespace, value);
308                     return (T) getValue(value, clazz.isPrimitive() ? getWrapperClass(clazz) : clazz, processingHints);
309                 } else {
310                     return null;
311                 }
312             } else if (clazz.isArray() && (clazz.getComponentType().isPrimitive() || ConfigurationUtils.isWrapperClass(
313                     clazz.getComponentType()))) {
314                 Object obj = ConfigurationUtils.getProperty(
315                         ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace), key, processingHints);
316                 if (obj != null) {
317                     Class componentClass = clazz.getComponentType();
318                     if (clazz.getComponentType().isPrimitive()) {
319                         componentClass = getWrapperClass(clazz.getComponentType());
320                     }
321                     String collString = ConfigurationUtils.getCollectionString(obj.toString());
322                     ArrayList<String> tempCollection = new ArrayList<>();
323                     for (String itemValue : collString.split(",")) {
324                         tempCollection.add(ConfigurationUtils.processVariablesIfPresent(tenant, namespace, itemValue));
325                     }
326                     Collection<T> collection =
327                             convert(ConfigurationUtils.getCollectionString(Arrays.toString(tempCollection.toArray())),
328                                     componentClass, processingHints);
329                     if (clazz.getComponentType().isPrimitive()) {
330                         return (T) ConfigurationUtils.getPrimitiveArray(collection, clazz.getComponentType());
331                     } else {
332                         return (T) collection.toArray(getZeroLengthArrayFor(getWrapperClass(clazz.getComponentType())));
333                     }
334                 } else {
335                     return null;
336                 }
337             } else if (clazz.isAnnotationPresent(Config.class)) {
338                 return read(tenant, namespace, clazz, (key == null || key.trim().length() == 0) ? "" : (key + "."),
339                         hints);
340             } else {
341                 throw new IllegalArgumentException(
342                         "Only primitive classes, wrapper classes, corresponding array classes and any "
343                                 + "class decorated with @org.openecomp.config.api.Config are allowed as argument.");
344             }
345         } catch (Exception exception) {
346             exception.printStackTrace();
347         }
348         return null;
349     }
350
351     private <T> T read(String tenant, String namespace, Class<T> clazz, String keyPrefix, Hint... hints)
352             throws Exception {
353         Config confAnnotation = clazz.getAnnotation(Config.class);
354         if (confAnnotation != null && confAnnotation.key().length() > 0 && !keyPrefix.endsWith(".")) {
355             keyPrefix += (confAnnotation.key() + ".");
356         }
357         Constructor<T> constructor = clazz.getDeclaredConstructor();
358         constructor.setAccessible(true);
359         T objToReturn = constructor.newInstance();
360         for (Field field : clazz.getDeclaredFields()) {
361             field.setAccessible(true);
362             Config fieldConfAnnotation = field.getAnnotation(Config.class);
363             if (fieldConfAnnotation != null) {
364                 if (field.getType().isPrimitive() || ConfigurationUtils.isWrapperClass(field.getType()) || (
365                         field.getType().isArray() && (field.getType().getComponentType().isPrimitive()
366                                                               || ConfigurationUtils.isWrapperClass(
367                                 field.getType().getComponentType())))
368                             || field.getType().getAnnotation(Config.class) != null) {
369                     field.set(objToReturn,
370                             get(tenant, namespace, keyPrefix + fieldConfAnnotation.key(), field.getType(), hints));
371                 } else if (Collection.class.isAssignableFrom(field.getType())) {
372                     Object obj = get(tenant, namespace, keyPrefix + fieldConfAnnotation.key(),
373                             ConfigurationUtils.getArrayClass(ConfigurationUtils.getCollectionGenericType(field)),
374                             hints);
375                     if (obj != null) {
376                         List list = Arrays.asList((Object[]) obj);
377                         Class clazzToInstantiate;
378                         if (field.getType().isInterface()) {
379                             clazzToInstantiate = ConfigurationUtils.getConcreteCollection(field.getType()).getClass();
380                         } else if (Modifier.isAbstract(field.getType().getModifiers())) {
381                             clazzToInstantiate =
382                                     ConfigurationUtils.getCompatibleCollectionForAbstractDef(field.getType())
383                                             .getClass();
384                         } else {
385                             clazzToInstantiate = field.getType();
386                         }
387                         Constructor construct = getConstructorWithArguments(clazzToInstantiate, Collection.class);
388                         if (construct != null) {
389                             construct.setAccessible(true);
390                             field.set(objToReturn, construct.newInstance(list));
391                         } else if ((construct = getConstructorWithArguments(clazzToInstantiate, Integer.class,
392                                 Boolean.class, Collection.class)) != null) {
393                             construct.setAccessible(true);
394                             field.set(objToReturn, construct.newInstance(list.size(), true, list));
395                         }
396                     }
397                 } else if (Map.class.isAssignableFrom(field.getType())) {
398                     field.set(objToReturn, generateMap(tenant, namespace, keyPrefix + fieldConfAnnotation.key()));
399                 }
400             }
401         }
402         return objToReturn;
403     }
404
405     private Constructor getConstructorWithArguments(Class clazz, Class... classes) {
406         try {
407             return clazz.getDeclaredConstructor(classes);
408         } catch (Exception exception) {
409             return null;
410         }
411     }
412
413     private Class getWrapperClass(Class clazz) {
414         if (byte.class == clazz) {
415             return Byte.class;
416         } else if (short.class == clazz) {
417             return Short.class;
418         } else if (int.class == clazz) {
419             return Integer.class;
420         } else if (long.class == clazz) {
421             return Long.class;
422         } else if (float.class == clazz) {
423             return Float.class;
424         } else if (double.class == clazz) {
425             return Double.class;
426         } else if (char.class == clazz) {
427             return Character.class;
428         } else if (boolean.class == clazz) {
429             return Boolean.class;
430         }
431         return clazz;
432     }
433
434     private <T> T getValue(Object obj, Class<T> clazz, int processingHint) {
435         if (obj == null || obj.toString().trim().length() == 0) {
436             return null;
437         } else {
438             obj = obj.toString().trim();
439         }
440         if (String.class.equals(clazz)) {
441             if (obj.toString().startsWith("@") && ConfigurationUtils.isExternalLookup(processingHint)) {
442                 String contents = ConfigurationUtils.getFileContents(
443                         NonConfigResource.locate(obj.toString().substring(1).trim()));
444                 if (contents == null) {
445                     contents = ConfigurationUtils.getFileContents(obj.toString().substring(1).trim());
446                 }
447                 if (contents != null) {
448                     obj = contents;
449                 }
450             }
451             return (T) obj.toString();
452         } else if (Number.class.isAssignableFrom(clazz)) {
453             Double doubleValue = Double.valueOf(obj.toString());
454             switch (clazz.getName()) {
455                 case "java.lang.Byte":
456                     Byte byteVal = doubleValue.byteValue();
457                     return (T) byteVal;
458                 case "java.lang.Short":
459                     Short shortVal = doubleValue.shortValue();
460                     return (T) shortVal;
461                 case "java.lang.Integer":
462                     Integer intVal = doubleValue.intValue();
463                     return (T) intVal;
464                 case "java.lang.Long":
465                     Long longVal = doubleValue.longValue();
466                     return (T) longVal;
467                 case "java.lang.Float":
468                     Float floatVal = doubleValue.floatValue();
469                     return (T) floatVal;
470                 case "java.lang.Double":
471                     return (T) doubleValue;
472                 default:
473             }
474         } else if (Boolean.class.equals(clazz)) {
475             return (T) Boolean.valueOf(obj.toString());
476         } else if (Character.class.equals(clazz)) {
477             return (T) Character.valueOf(obj.toString().charAt(0));
478         }
479         return null;
480     }
481
482     private <T> T[] getZeroLengthArrayFor(Class<T> clazz) {
483         Object obj = null;
484         if (clazz == int.class) {
485             obj = new int[] {};
486         } else if (clazz == byte.class) {
487             obj = new byte[] {};
488         } else if (clazz == short.class) {
489             obj = new short[] {};
490         } else if (clazz == long.class) {
491             obj = new long[] {};
492         } else if (clazz == float.class) {
493             obj = new float[] {};
494         } else if (clazz == double.class) {
495             obj = new double[] {};
496         } else if (clazz == boolean.class) {
497             obj = new boolean[] {};
498         } else if (clazz == char.class) {
499             obj = new char[] {};
500         } else if (clazz == Byte.class) {
501             obj = new Byte[] {};
502         } else if (clazz == Short.class) {
503             obj = new Short[] {};
504         } else if (clazz == Integer.class) {
505             obj = new Integer[] {};
506         } else if (clazz == Long.class) {
507             obj = new Long[] {};
508         } else if (clazz == Float.class) {
509             obj = new Float[] {};
510         } else if (clazz == Double.class) {
511             obj = new Double[] {};
512         } else if (clazz == Boolean.class) {
513             obj = new Boolean[] {};
514         } else if (clazz == Character.class) {
515             obj = new Character[] {};
516         } else if (clazz == String.class) {
517             obj = new String[] {};
518         }
519         return (T[]) obj;
520     }
521
522     private <T> Collection<T> convert(String commaSaperatedValues, Class<T> clazz, int processingHints) {
523         ArrayList<T> collection = new ArrayList<>();
524         for (String value : commaSaperatedValues.split(",")) {
525             try {
526                 T type1 = getValue(value, clazz, processingHints);
527                 if (type1 != null) {
528                     collection.add(type1);
529                 }
530             } catch (RuntimeException re) {
531                 // do nothing
532             }
533         }
534         return collection;
535     }
536 }