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