Removed support of dynamic configuration
[sdc.git] / common / onap-common-configuration-management / onap-configuration-management-core / src / main / java / org / onap / config / ConfigurationUtils.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;
18
19 import static java.util.Optional.ofNullable;
20 import static org.onap.config.api.Hint.EXTERNAL_LOOKUP;
21 import static org.onap.config.api.Hint.LATEST_LOOKUP;
22 import static org.onap.config.api.Hint.NODE_SPECIFIC;
23
24 import com.virtlink.commons.configuration2.jackson.JsonConfiguration;
25 import java.io.File;
26 import java.lang.reflect.Field;
27 import java.lang.reflect.ParameterizedType;
28 import java.lang.reflect.Type;
29 import java.net.URL;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.util.ArrayDeque;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Deque;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Iterator;
41 import java.util.LinkedHashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Optional;
45 import java.util.Queue;
46 import java.util.Set;
47 import java.util.SortedSet;
48 import java.util.TreeSet;
49 import java.util.concurrent.BlockingQueue;
50 import java.util.concurrent.ConcurrentLinkedQueue;
51 import java.util.concurrent.LinkedBlockingQueue;
52 import java.util.concurrent.LinkedTransferQueue;
53 import java.util.concurrent.TransferQueue;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56 import java.util.stream.Collectors;
57 import java.util.stream.Stream;
58 import net.sf.corn.cps.CPScanner;
59 import net.sf.corn.cps.ResourceFilter;
60 import org.apache.commons.configuration2.CompositeConfiguration;
61 import org.apache.commons.configuration2.Configuration;
62 import org.apache.commons.configuration2.FileBasedConfiguration;
63 import org.apache.commons.configuration2.PropertiesConfiguration;
64 import org.apache.commons.configuration2.XMLConfiguration;
65 import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
66 import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
67 import org.apache.commons.configuration2.builder.fluent.Configurations;
68 import org.apache.commons.configuration2.builder.fluent.Parameters;
69 import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
70 import org.apache.commons.configuration2.ex.ConfigurationException;
71 import org.apache.commons.io.IOUtils;
72 import org.onap.config.api.Config;
73 import org.onap.config.api.ConfigurationManager;
74 import org.onap.config.impl.AgglomerateConfiguration;
75 import org.onap.config.impl.ConfigurationRepository;
76 import org.onap.config.impl.YamlConfiguration;
77 import org.onap.config.type.ConfigurationMode;
78 import org.onap.config.type.ConfigurationType;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
81
82 public class ConfigurationUtils {
83
84     private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationUtils.class);
85     private static final String CONFIGURATION_TYPE_NOT_SUPPORTED = "Configuration type not supported:";
86     private static final Map<Class, Class> ARRAY_CLASS_MAP;
87
88     static {
89         Map<Class, Class> arrayTypes = new HashMap<>();
90         arrayTypes.put(Byte.class, Byte[].class);
91         arrayTypes.put(Short.class, Short[].class);
92         arrayTypes.put(Integer.class, Integer[].class);
93         arrayTypes.put(Long.class, Long[].class);
94         arrayTypes.put(Float.class, Float[].class);
95         arrayTypes.put(Double.class, Double[].class);
96         arrayTypes.put(Boolean.class, Boolean[].class);
97         arrayTypes.put(Character.class, Character[].class);
98         arrayTypes.put(String.class, String[].class);
99         ARRAY_CLASS_MAP = Collections.unmodifiableMap(arrayTypes);
100     }
101
102     private ConfigurationUtils() {
103         // prevent instantiation
104     }
105
106     public static Collection<File> getAllFiles(File file, boolean recursive, boolean onlyDirectory) {
107         ArrayList<File> collection = new ArrayList<>();
108         if (file.isDirectory() && file.exists()) {
109             File[] files = file.listFiles();
110             for (File innerFile : files) {
111                 if (innerFile.isFile() && !onlyDirectory) {
112                     collection.add(innerFile);
113                 } else if (innerFile.isDirectory()) {
114                     collection.add(innerFile);
115                     if (recursive) {
116                         collection.addAll(getAllFiles(innerFile, recursive, onlyDirectory));
117                     }
118                 }
119             }
120         }
121         return collection;
122     }
123
124     public static String getCommaSeparatedList(String[] list) {
125         return getCommaSeparatedList(list == null ? Collections.emptyList() : Arrays.asList(list));
126     }
127
128     public static String getCommaSeparatedList(List list) {
129         return ((Stream<String>) list.stream().filter(o -> o != null && !o.toString().trim().isEmpty())
130                                          .map(o -> o.toString().trim())).collect(Collectors.joining(","));
131     }
132
133     public static boolean isConfig(URL url) {
134         return isConfig(url.getFile());
135     }
136
137     public static boolean isConfig(String file) {
138         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
139         file = file.substring(file.lastIndexOf('/') + 1);
140         return file.matches(
141                 "CONFIG(-\\w*){0,1}(-" + "(" + ConfigurationMode.OVERRIDE + "|" + ConfigurationMode.MERGE + "|"
142                         + ConfigurationMode.UNION + ")){0,1}" + "\\.(" + ConfigurationType.PROPERTIES.name() + "|"
143                         + ConfigurationType.XML.name() + "|" + ConfigurationType.JSON.name() + "|"
144                         + ConfigurationType.YAML.name() + ")$") || file.matches(
145                 "CONFIG(.)*\\.(" + ConfigurationType.PROPERTIES.name() + "|" + ConfigurationType.XML.name() + "|"
146                         + ConfigurationType.JSON.name() + "|" + ConfigurationType.YAML.name() + ")$");
147     }
148
149     public static boolean isConfig(File file) {
150         return file != null && file.exists() && isConfig(file.getName());
151     }
152
153     private static Optional<String> getNamespace(Configuration config) {
154         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.NAMESPACE_KEY)))
155                        .map(String::toUpperCase);
156     }
157
158     public static ConfigurationMode getMergeStrategy(URL url) {
159         Optional<ConfigurationMode> configurationMode =
160                 getConfiguration(url).flatMap(ConfigurationUtils::getMergeStrategy)
161                         .flatMap(ConfigurationUtils::convertConfigurationMode);
162         return configurationMode.orElseGet(() -> getMergeStrategy(url.getFile().toUpperCase()));
163     }
164
165     public static Optional<FileBasedConfiguration> getConfiguration(URL url) {
166         FileBasedConfiguration builder = null;
167         try {
168             ConfigurationType configType = ConfigurationUtils.getConfigType(url);
169             switch (configType) {
170                 case PROPERTIES:
171                     builder = new Configurations().fileBased(PropertiesConfiguration.class, url);
172                     break;
173                 case XML:
174                     builder = new Configurations().fileBased(XMLConfiguration.class, url);
175                     break;
176                 case JSON:
177                     builder = new Configurations().fileBased(JsonConfiguration.class, url);
178                     break;
179                 case YAML:
180                     builder = new Configurations().fileBased(YamlConfiguration.class, url);
181                     break;
182                 default:
183                     throw new ConfigurationException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
184             }
185         } catch (ConfigurationException exception) {
186             exception.printStackTrace();
187         }
188         return ofNullable(builder);
189     }
190
191     public static ConfigurationMode getMergeStrategy(String file) {
192         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
193         file = file.substring(file.lastIndexOf('/') + 1);
194         Pattern pattern = Pattern.compile(
195                 "CONFIG(-\\w*){0,1}(-" + "(" + ConfigurationMode.OVERRIDE + "|" + ConfigurationMode.MERGE + "|"
196                         + ConfigurationMode.UNION + ")){0,1}" + "\\.(" + ConfigurationType.PROPERTIES.name() + "|"
197                         + ConfigurationType.XML.name() + "|" + ConfigurationType.JSON.name() + "|"
198                         + ConfigurationType.YAML.name() + ")$");
199         Matcher matcher = pattern.matcher(file);
200         boolean b1 = matcher.matches();
201         if (b1) {
202             for (int i = 1; i <= matcher.groupCount(); i++) {
203                 String modeName = matcher.group(i);
204                 if (modeName != null) {
205                     modeName = modeName.substring(1);
206                 }
207                 try {
208                     return Enum.valueOf(ConfigurationMode.class, modeName);
209                 } catch (Exception exception) {
210                     //do nothing
211                 }
212             }
213         }
214
215         return null;
216     }
217
218     public static ConfigurationType getConfigType(URL url) {
219         return Enum.valueOf(ConfigurationType.class,
220                 url.getFile().substring(url.getFile().lastIndexOf('.') + 1).toUpperCase());
221     }
222
223     private static Optional<ConfigurationMode> convertConfigurationMode(String configMode) {
224         ConfigurationMode configurationMode = null;
225         try {
226             configurationMode = ConfigurationMode.valueOf(configMode);
227         } catch (Exception exception) {
228             LOGGER.error("Could not find convert {} into configuration mode", configMode);
229         }
230         return Optional.ofNullable(configurationMode);
231     }
232
233     private static Optional<String> getMergeStrategy(Configuration config) {
234         return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.MODE_KEY)))
235                        .map(String::toUpperCase);
236     }
237
238     public static ConfigurationMode getMergeStrategy(File file) {
239         Optional<ConfigurationMode> configurationMode =
240                 getConfiguration(file).flatMap(ConfigurationUtils::getMergeStrategy)
241                         .flatMap(ConfigurationUtils::convertConfigurationMode);
242         return configurationMode.orElseGet(() -> getMergeStrategy(file.getName().toUpperCase()));
243     }
244
245     public static Optional<FileBasedConfiguration> getConfiguration(File file) {
246         FileBasedConfiguration builder = null;
247         try {
248             ConfigurationType configType = ConfigurationUtils.getConfigType(file);
249             switch (configType) {
250                 case PROPERTIES:
251                     builder = new Configurations().fileBased(PropertiesConfiguration.class, file);
252                     break;
253                 case XML:
254                     builder = new Configurations().fileBased(XMLConfiguration.class, file);
255                     break;
256                 case JSON:
257                     builder = new Configurations().fileBased(JsonConfiguration.class, file);
258                     break;
259                 case YAML:
260                     builder = new Configurations().fileBased(YamlConfiguration.class, file);
261                     break;
262                 default:
263                     throw new ConfigurationException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
264             }
265         } catch (ConfigurationException exception) {
266             exception.printStackTrace();
267         }
268         return ofNullable(builder);
269     }
270
271     public static ConfigurationType getConfigType(File file) {
272         return Enum.valueOf(ConfigurationType.class,
273                 file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf('.') + 1).toUpperCase());
274     }
275
276     public static Class getCollectionGenericType(Field field) {
277         Type type = field.getGenericType();
278
279         if (type instanceof ParameterizedType) {
280
281             ParameterizedType paramType = (ParameterizedType) type;
282             Type[] arr = paramType.getActualTypeArguments();
283             if (arr.length > 0) {
284                 Class<?> clazz = (Class<?>) arr[0];
285                 if (isWrapperClass(clazz)) {
286                     return clazz;
287                 } else {
288                     throw new RuntimeException("Collection of type " + clazz.getName() + " not supported.");
289                 }
290             }
291         }
292
293         return String[].class;
294     }
295
296     public static boolean isWrapperClass(Class clazz) {
297         return clazz == String.class || clazz == Boolean.class || clazz == Character.class || Number.class
298                                                                                                       .isAssignableFrom(
299                                                                                                               clazz);
300     }
301
302     public static Class getArrayClass(Class clazz) {
303         return ARRAY_CLASS_MAP.getOrDefault(clazz, null);
304     }
305
306     public static List<URL> getAllClassPathResources() {
307         return CPScanner.scanResources(new ResourceFilter());
308     }
309
310     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(URL url) {
311         ConfigurationType configType = ConfigurationUtils.getConfigType(url);
312         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder =
313                 getFileBasedConfigurationReloadingFileBasedConfigurationBuilder(configType);
314         builder.configure(
315                 new Parameters().fileBased().setURL(url).setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
316         return builder;
317     }
318
319     private static ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> getFileBasedConfigurationReloadingFileBasedConfigurationBuilder(
320             ConfigurationType configType) {
321
322         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
323         switch (configType) {
324             case PROPERTIES:
325                 builder = new ReloadingFileBasedConfigurationBuilder<>(PropertiesConfiguration.class);
326                 break;
327             case XML:
328                 builder = new ReloadingFileBasedConfigurationBuilder<>(XMLConfiguration.class);
329                 break;
330             case JSON:
331                 builder = new ReloadingFileBasedConfigurationBuilder<>(JsonConfiguration.class);
332                 break;
333             case YAML:
334                 builder = new ReloadingFileBasedConfigurationBuilder<>(YamlConfiguration.class);
335                 break;
336             default:
337                 throw new IllegalArgumentException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
338         }
339         return builder;
340     }
341
342     public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(File file,
343             boolean autoSave) {
344         ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
345         ConfigurationType configType = ConfigurationUtils.getConfigType(file);
346         builder = getFileBasedConfigurationReloadingFileBasedConfigurationBuilder(configType);
347         builder.configure(new Parameters().fileBased().setFile(file)
348                                   .setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
349         builder.setAutoSave(autoSave);
350         return builder;
351     }
352
353     public static <T> T read(Configuration config, Class<T> clazz, String keyPrefix) throws Exception {
354         Config confAnnotation = clazz.getAnnotation(Config.class);
355         if (confAnnotation != null) {
356             keyPrefix += (confAnnotation.key() + ".");
357         }
358         T objToReturn = clazz.newInstance();
359         for (Field field : clazz.getDeclaredFields()) {
360             Config fieldAnnotation = field.getAnnotation(Config.class);
361             if (fieldAnnotation != null) {
362                 field.setAccessible(true);
363                 field.set(objToReturn, config.getProperty(keyPrefix + fieldAnnotation.key()));
364             } else if (field.getType().getAnnotation(Config.class) != null) {
365                 field.set(objToReturn, read(config, field.getType(), keyPrefix));
366             }
367         }
368         return objToReturn;
369     }
370
371     public static Object getPrimitiveArray(Collection collection, Class clazz) {
372         if (clazz == int.class) {
373             int[] array = new int[collection.size()];
374             Object[] objArray = collection.toArray();
375             for (int i = 0; i < collection.size(); i++) {
376                 array[i] = (int) objArray[i];
377             }
378             return array;
379         }
380         if (clazz == byte.class) {
381             byte[] array = new byte[collection.size()];
382             Object[] objArray = collection.toArray();
383             for (int i = 0; i < collection.size(); i++) {
384                 array[i] = (byte) objArray[i];
385             }
386             return array;
387         }
388         if (clazz == short.class) {
389             short[] array = new short[collection.size()];
390             Object[] objArray = collection.toArray();
391             for (int i = 0; i < collection.size(); i++) {
392                 array[i] = (short) objArray[i];
393             }
394             return array;
395         }
396         if (clazz == long.class) {
397             long[] array = new long[collection.size()];
398             Object[] objArray = collection.toArray();
399             for (int i = 0; i < collection.size(); i++) {
400                 array[i] = (long) objArray[i];
401             }
402             return array;
403         }
404         if (clazz == float.class) {
405             float[] array = new float[collection.size()];
406             Object[] objArray = collection.toArray();
407             for (int i = 0; i < collection.size(); i++) {
408                 array[i] = (float) objArray[i];
409             }
410             return array;
411         }
412         if (clazz == double.class) {
413             double[] array = new double[collection.size()];
414             Object[] objArray = collection.toArray();
415             for (int i = 0; i < collection.size(); i++) {
416                 array[i] = (double) objArray[i];
417             }
418             return array;
419         }
420         if (clazz == boolean.class) {
421             boolean[] array = new boolean[collection.size()];
422             Object[] objArray = collection.toArray();
423             for (int i = 0; i < collection.size(); i++) {
424                 array[i] = (boolean) objArray[i];
425             }
426             return array;
427         }
428         return null;
429     }
430
431     public static String getCollectionString(String input) {
432         Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
433         Matcher matcher = pattern.matcher(input);
434         if (matcher.matches()) {
435             input = matcher.group(1);
436         }
437         return input;
438     }
439
440     public static String processVariablesIfPresent(String tenant, String namespace, String data) {
441         Pattern pattern = Pattern.compile("^.*\\$\\{(.*)\\}.*");
442         Matcher matcher = pattern.matcher(data);
443         if (matcher.matches()) {
444             String key = matcher.group(1);
445             if (key.toUpperCase().startsWith("ENV:")) {
446                 String envValue = System.getenv(key.substring(4));
447                 return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "\\}",
448                         envValue == null ? "" : envValue.replace("\\", "\\\\")));
449             } else if (key.toUpperCase().startsWith("SYS:")) {
450                 String sysValue = System.getProperty(key.substring(4));
451                 return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "\\}",
452                         sysValue == null ? "" : sysValue.replace("\\", "\\\\")));
453             } else {
454                 String propertyValue = ConfigurationUtils.getCollectionString(
455                         ConfigurationManager.lookup().getAsStringValues(tenant, namespace, key).toString());
456                 return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "\\}",
457                         propertyValue == null ? "" : propertyValue.replace("\\", "\\\\")));
458             }
459         } else {
460             return data;
461         }
462     }
463
464     public static String getFileContents(String path) {
465         try {
466             if (path != null) {
467                 return IOUtils.toString(new URL(path));
468             }
469         } catch (Exception exception) {
470             exception.printStackTrace();
471         }
472         return null;
473     }
474
475     public static String getFileContents(Path path) {
476         try {
477             if (path != null) {
478                 return new String(Files.readAllBytes(path));
479             }
480         } catch (Exception exception) {
481             exception.printStackTrace();
482         }
483         return null;
484     }
485
486     public static Object getDefaultFor(Class clazz) {
487         if (byte.class == clazz) {
488             return new Byte("0");
489         } else if (short.class == clazz) {
490             return new Short("0");
491         } else if (int.class == clazz) {
492             return new Integer("0");
493         } else if (float.class == clazz) {
494             return new Float("0");
495         } else if (long.class == clazz) {
496             return new Long("0");
497         } else if (double.class == clazz) {
498             return new Double("0");
499         } else if (boolean.class == clazz) {
500             return Boolean.FALSE;
501         }
502         return (char) 0;
503     }
504
505     public static Collection getCompatibleCollectionForAbstractDef(Class clazz) {
506         if (BlockingQueue.class.isAssignableFrom(clazz)) {
507             return getConcreteCollection(BlockingQueue.class);
508         }
509         if (TransferQueue.class.isAssignableFrom(clazz)) {
510             return getConcreteCollection(TransferQueue.class);
511         }
512         if (Deque.class.isAssignableFrom(clazz)) {
513             return getConcreteCollection(Deque.class);
514         }
515         if (Queue.class.isAssignableFrom(clazz)) {
516             return getConcreteCollection(Queue.class);
517         }
518         if (SortedSet.class.isAssignableFrom(clazz)) {
519             return getConcreteCollection(SortedSet.class);
520         }
521         if (Set.class.isAssignableFrom(clazz)) {
522             return getConcreteCollection(Set.class);
523         }
524         if (List.class.isAssignableFrom(clazz)) {
525             return getConcreteCollection(List.class);
526         }
527         return null;
528     }
529
530     public static Collection getConcreteCollection(Class clazz) {
531         switch (clazz.getName()) {
532             case "java.util.Collection":
533             case "java.util.List":
534                 return new ArrayList<>();
535             case "java.util.Set":
536                 return new HashSet<>();
537             case "java.util.SortedSet":
538                 return new TreeSet<>();
539             case "java.util.Queue":
540                 return new ConcurrentLinkedQueue<>();
541             case "java.util.Deque":
542                 return new ArrayDeque<>();
543             case "java.util.concurrent.TransferQueue":
544                 return new LinkedTransferQueue<>();
545             case "java.util.concurrent.BlockingQueue":
546                 return new LinkedBlockingQueue<>();
547             default:
548                 return null;
549         }
550     }
551
552     public static String getConfigurationRepositoryKey(File file) {
553         return getConfigurationRepositoryKey(
554                 ConfigurationUtils.getNamespace(file).split(Constants.TENANT_NAMESPACE_SEPARATOR));
555     }
556
557     public static String getConfigurationRepositoryKey(String[] array) {
558         Deque<String> stack = new ArrayDeque<>();
559         stack.push(Constants.DEFAULT_TENANT);
560         for (String element : array) {
561             stack.push(element);
562         }
563         String toReturn = stack.pop();
564         return stack.pop() + Constants.KEY_ELEMENTS_DELIMITER + toReturn;
565     }
566
567     public static String getNamespace(File file) {
568         Optional<String> namespace =
569                 getConfiguration(file).flatMap(ConfigurationUtils::getNamespace).map(String::toUpperCase);
570         return namespace.orElseGet(() -> getNamespace(file.getName().toUpperCase()));
571     }
572
573     public static String getNamespace(String file) {
574         file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
575         file = file.substring(file.lastIndexOf('/') + 1);
576         Pattern pattern = Pattern.compile(
577                 "CONFIG(-\\w*){0,1}(-" + "(" + ConfigurationMode.OVERRIDE + "|" + ConfigurationMode.MERGE + "|"
578                         + ConfigurationMode.UNION + ")){0,1}" + "\\.(" + ConfigurationType.PROPERTIES.name() + "|"
579                         + ConfigurationType.XML.name() + "|" + ConfigurationType.JSON.name() + "|"
580                         + ConfigurationType.YAML.name() + ")$");
581         Matcher matcher = pattern.matcher(file);
582         boolean b1 = matcher.matches();
583         if (b1) {
584             if (matcher.group(1) != null) {
585                 String moduleName = matcher.group(1).substring(1);
586                 return moduleName.equalsIgnoreCase(ConfigurationMode.OVERRIDE.name()) || moduleName.equalsIgnoreCase(
587                         ConfigurationMode.UNION.name()) || moduleName.equalsIgnoreCase(ConfigurationMode.MERGE.name())
588                                ? Constants.DEFAULT_NAMESPACE : moduleName;
589             } else {
590                 return Constants.DEFAULT_NAMESPACE;
591             }
592         } else if (isConfig(file)) {
593             return Constants.DEFAULT_NAMESPACE;
594         }
595
596         return null;
597     }
598
599     public static String getConfigurationRepositoryKey(URL url) {
600         return getConfigurationRepositoryKey(
601                 ConfigurationUtils.getNamespace(url).split(Constants.TENANT_NAMESPACE_SEPARATOR));
602     }
603
604     public static String getNamespace(URL url) {
605
606         Optional<String> namespace =
607                 getConfiguration(url).flatMap(ConfigurationUtils::getNamespace).map(String::toUpperCase);
608
609         return namespace.orElseGet(() -> getNamespace(url.getFile().toUpperCase()));
610     }
611
612     public static LinkedHashMap toMap(Configuration config) {
613         Iterator<String> iterator = config.getKeys();
614         LinkedHashMap<String, String> map = new LinkedHashMap<>();
615         while (iterator.hasNext()) {
616             String key = iterator.next();
617             if (!(key.equals(Constants.MODE_KEY) || key.equals(Constants.NAMESPACE_KEY) || key.equals(
618                     Constants.LOAD_ORDER_KEY))) {
619                 map.put(key, config.getProperty(key).toString());
620             }
621         }
622
623         return map;
624     }
625
626     public static Map diff(LinkedHashMap orig, LinkedHashMap latest) {
627         orig = new LinkedHashMap<>(orig);
628         latest = new LinkedHashMap<>(latest);
629         List<String> set = new ArrayList(orig.keySet());
630         for (String key : set) {
631             if (latest.remove(key, orig.get(key))) {
632                 orig.remove(key);
633             }
634         }
635         Set<String> keys = latest.keySet();
636         for (String key : keys) {
637             orig.remove(key);
638         }
639         set = new ArrayList(orig.keySet());
640         for (String key : set) {
641             latest.put(key, "");
642         }
643         return new HashMap<>(latest);
644     }
645
646     public static boolean isArray(String tenant, String namespace, String key, int processingHints) throws Exception {
647         Object obj = ConfigurationUtils
648                              .getProperty(ConfigurationRepository.lookup().getConfigurationFor(tenant, namespace), key,
649                                      processingHints);
650         return (obj != null) && ConfigurationUtils.isCollection(obj.toString());
651     }
652
653     public static Object getProperty(Configuration config, String key, int processingHints) {
654         if (!isDirectLookup(processingHints)) {
655             if (config instanceof AgglomerateConfiguration) {
656                 return ((AgglomerateConfiguration) config).getPropertyValue(key);
657             } else if (config instanceof CompositeConfiguration) {
658                 CompositeConfiguration conf = (CompositeConfiguration) config;
659                 for (int i = 0; i < conf.getNumberOfConfigurations(); i++) {
660                     if (conf.getConfiguration(i) instanceof AgglomerateConfiguration) {
661                         return ((AgglomerateConfiguration) conf.getConfiguration(i)).getPropertyValue(key);
662                     } else if (isNodeSpecific(processingHints)) {
663                         Object obj = conf.getConfiguration(i).getProperty(key);
664                         if (obj != null) {
665                             return obj;
666                         }
667                     }
668                 }
669             }
670         }
671         return config.getProperty(key);
672     }
673
674     public static boolean isCollection(String input) {
675         Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
676         Matcher matcher = pattern.matcher(input);
677         return matcher.matches();
678     }
679
680     public static boolean isDirectLookup(int hints) {
681         return (hints & LATEST_LOOKUP.value()) == LATEST_LOOKUP.value();
682     }
683
684     public static boolean isNodeSpecific(int hints) {
685         return (hints & NODE_SPECIFIC.value()) == NODE_SPECIFIC.value();
686     }
687
688     public static boolean isExternalLookup(int hints) {
689         return (hints & EXTERNAL_LOOKUP.value()) == EXTERNAL_LOOKUP.value();
690     }
691
692     public static boolean isZeroLengthArray(Class clazz, Object obj) {
693         if (clazz.isArray() && clazz.getComponentType().isPrimitive()) {
694             if (clazz.getComponentType() == int.class) {
695                 return ((int[]) obj).length == 0;
696             } else if (clazz.getComponentType() == byte.class) {
697                 return ((byte[]) obj).length == 0;
698             } else if (clazz.getComponentType() == short.class) {
699                 return ((short[]) obj).length == 0;
700             } else if (clazz.getComponentType() == float.class) {
701                 return ((float[]) obj).length == 0;
702             } else if (clazz.getComponentType() == boolean.class) {
703                 return ((boolean[]) obj).length == 0;
704             } else if (clazz.getComponentType() == double.class) {
705                 return ((double[]) obj).length == 0;
706             } else if (clazz.getComponentType() == long.class) {
707                 return ((long[]) obj).length == 0;
708             } else {
709                 return ((Object[]) obj).length == 0;
710             }
711         }
712
713         return false;
714     }
715
716     public static boolean isBlank(String value) {
717         return value == null || value.trim().length() == 0;
718     }
719 }