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.
16 * Modifications Copyright (c) 2019 Samsung
20 package org.onap.config;
22 import static java.util.Optional.ofNullable;
23 import static org.onap.config.api.Hint.EXTERNAL_LOOKUP;
24 import static org.onap.config.api.Hint.LATEST_LOOKUP;
25 import static org.onap.config.api.Hint.NODE_SPECIFIC;
27 import com.virtlink.commons.configuration2.jackson.JsonConfiguration;
29 import java.lang.reflect.Field;
30 import java.lang.reflect.ParameterizedType;
31 import java.lang.reflect.Type;
32 import java.net.MalformedURLException;
34 import java.nio.charset.Charset;
35 import java.nio.file.Files;
36 import java.nio.file.Path;
37 import java.util.ArrayDeque;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.Deque;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.List;
47 import java.util.Objects;
48 import java.util.Optional;
49 import java.util.Queue;
51 import java.util.SortedSet;
52 import java.util.TreeSet;
53 import java.util.concurrent.BlockingQueue;
54 import java.util.concurrent.ConcurrentLinkedQueue;
55 import java.util.concurrent.LinkedBlockingQueue;
56 import java.util.concurrent.LinkedTransferQueue;
57 import java.util.concurrent.TransferQueue;
58 import java.util.regex.Matcher;
59 import java.util.regex.Pattern;
60 import java.util.stream.Collectors;
61 import net.sf.corn.cps.CPScanner;
62 import net.sf.corn.cps.ResourceFilter;
63 import org.apache.commons.configuration2.CompositeConfiguration;
64 import org.apache.commons.configuration2.Configuration;
65 import org.apache.commons.configuration2.FileBasedConfiguration;
66 import org.apache.commons.configuration2.PropertiesConfiguration;
67 import org.apache.commons.configuration2.XMLConfiguration;
68 import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
69 import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
70 import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
71 import org.apache.commons.configuration2.builder.fluent.Configurations;
72 import org.apache.commons.configuration2.builder.fluent.Parameters;
73 import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
74 import org.apache.commons.configuration2.ex.ConfigurationException;
75 import org.apache.commons.io.IOUtils;
76 import org.onap.config.api.Config;
77 import org.onap.config.api.ConfigurationManager;
78 import org.onap.config.impl.YamlConfiguration;
79 import org.onap.config.type.ConfigurationMode;
80 import org.onap.config.type.ConfigurationType;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
84 public class ConfigurationUtils {
86 private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationUtils.class);
88 private static final String CONFIGURATION_TYPE_NOT_SUPPORTED = "Configuration type not supported:";
90 private static final Map<Class, Class> ARRAY_CLASS_MAP;
92 private static final String CONFIG_REGEX_TPL_OPT_1 = "CONFIG(-\\w*){0,1}(-(%s|%s|%s)){0,1}\\.(%s|%s|%s|%s)$";
94 private static final String CONFIG_REGEX_TPL_OPT_2 = "CONFIG(.)*\\.(%s|%s|%s|%s)$";
97 Map<Class, Class> arrayTypes = new HashMap<>();
98 arrayTypes.put(Byte.class, Byte[].class);
99 arrayTypes.put(Short.class, Short[].class);
100 arrayTypes.put(Integer.class, Integer[].class);
101 arrayTypes.put(Long.class, Long[].class);
102 arrayTypes.put(Float.class, Float[].class);
103 arrayTypes.put(Double.class, Double[].class);
104 arrayTypes.put(Boolean.class, Boolean[].class);
105 arrayTypes.put(Character.class, Character[].class);
106 arrayTypes.put(String.class, String[].class);
107 ARRAY_CLASS_MAP = Collections.unmodifiableMap(arrayTypes);
110 private ConfigurationUtils() {
111 // prevent instantiation
114 public static Collection<File> getAllFiles(File file, boolean recursive, boolean onlyDirectory) {
116 ArrayList<File> collection = new ArrayList<>();
117 if (file.isDirectory() && file.exists()) {
118 File[] files = file.listFiles();
119 for (File innerFile : Objects.requireNonNull(files)) {
120 if (innerFile.isFile() && !onlyDirectory) {
121 collection.add(innerFile);
122 } else if (innerFile.isDirectory()) {
123 collection.add(innerFile);
125 collection.addAll(getAllFiles(innerFile, true, onlyDirectory));
133 public static String getCommaSeparatedList(String[] list) {
134 return (list == null) || (list.length == 0) ? "" : getCommaSeparatedList(Arrays.asList(list));
137 public static String getCommaSeparatedList(List<?> list) {
139 if ((list == null) || list.isEmpty()) {
143 return list.stream().filter(o -> o != null && !o.toString().trim().isEmpty())
144 .map(o -> o.toString().trim()).collect(Collectors.joining(","));
147 public static boolean isConfig(URL url) {
148 return isConfig(url.getFile());
151 public static boolean isConfig(String file) {
152 file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
153 file = file.substring(file.lastIndexOf('/') + 1);
156 CONFIG_REGEX_TPL_OPT_1,
157 ConfigurationMode.OVERRIDE,
158 ConfigurationMode.MERGE,
159 ConfigurationMode.UNION,
160 ConfigurationType.PROPERTIES.name(),
161 ConfigurationType.XML.name(),
162 ConfigurationType.JSON.name(),
163 ConfigurationType.YAML.name()
167 CONFIG_REGEX_TPL_OPT_2,
168 ConfigurationType.PROPERTIES.name(),
169 ConfigurationType.XML.name(),
170 ConfigurationType.JSON.name(),
171 ConfigurationType.YAML.name()
176 public static boolean isConfig(File file) {
177 return file != null && file.exists() && isConfig(file.getName());
180 private static Optional<String> readNamespace(Configuration config) {
181 return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.NAMESPACE_KEY)))
182 .map(String::toUpperCase);
185 private static Optional<String> readMergeStrategy(Configuration config) {
186 return ofNullable(config).flatMap(configuration -> ofNullable(configuration.getString(Constants.MODE_KEY)))
187 .map(String::toUpperCase);
190 public static ConfigurationMode getMergeStrategy(File file) {
191 Optional<ConfigurationMode> configurationMode =
192 getConfiguration(file).flatMap(ConfigurationUtils::readMergeStrategy)
193 .flatMap(ConfigurationUtils::convertConfigurationMode);
194 return configurationMode.orElseGet(() -> getMergeStrategy(file.getName().toUpperCase()));
197 public static ConfigurationMode getMergeStrategy(URL url) {
198 Optional<ConfigurationMode> configurationMode =
199 getConfiguration(url).flatMap(ConfigurationUtils::readMergeStrategy)
200 .flatMap(ConfigurationUtils::convertConfigurationMode);
201 return configurationMode.orElseGet(() -> getMergeStrategy(url.getFile().toUpperCase()));
204 public static ConfigurationMode getMergeStrategy(String file) {
206 file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
207 file = file.substring(file.lastIndexOf('/') + 1);
208 Pattern pattern = Pattern.compile(
210 CONFIG_REGEX_TPL_OPT_1,
211 ConfigurationMode.OVERRIDE,
212 ConfigurationMode.MERGE,
213 ConfigurationMode.UNION,
214 ConfigurationType.PROPERTIES.name(),
215 ConfigurationType.XML.name(),
216 ConfigurationType.JSON.name(),
217 ConfigurationType.YAML.name()
220 Matcher matcher = pattern.matcher(file);
221 boolean b1 = matcher.matches();
223 for (int i = 1; i <= matcher.groupCount(); i++) {
224 String modeName = matcher.group(i);
225 if (modeName != null) {
226 modeName = modeName.substring(1);
229 return Enum.valueOf(ConfigurationMode.class, modeName);
230 } catch (Exception exception) {
231 LOGGER.debug("Configuration mode for merge strategy '{}' not found", modeName, exception);
239 public static Optional<FileBasedConfiguration> getConfiguration(URL url) {
243 ConfigurationType configType = ConfigurationUtils.getConfigType(url);
244 switch (configType) {
246 return Optional.of(new Configurations().fileBased(PropertiesConfiguration.class, url));
248 return Optional.of(new Configurations().fileBased(XMLConfiguration.class, url));
250 return Optional.of(new Configurations().fileBased(JsonConfiguration.class, url));
252 return Optional.of(new Configurations().fileBased(YamlConfiguration.class, url));
254 throw new ConfigurationException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
256 } catch (ConfigurationException exception) {
257 LOGGER.error("Error reading configuration at {}.", url.toString(), exception);
260 return Optional.empty();
263 public static Optional<FileBasedConfiguration> getConfiguration(File file) {
266 return getConfiguration(file.getAbsoluteFile().toURI().toURL());
267 } catch (MalformedURLException e) {
268 throw new IllegalStateException("Malformed URL: " + file.getAbsolutePath());
272 public static ConfigurationType getConfigType(File file) {
273 Objects.requireNonNull(file, "File cannot be null");
274 return Enum.valueOf(ConfigurationType.class,
275 file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf('.') + 1).toUpperCase());
278 public static ConfigurationType getConfigType(URL url) {
279 Objects.requireNonNull(url, "URL cannot be null");
280 return Enum.valueOf(ConfigurationType.class,
281 url.getFile().substring(url.getFile().lastIndexOf('.') + 1).toUpperCase());
284 private static Optional<ConfigurationMode> convertConfigurationMode(String configMode) {
285 ConfigurationMode configurationMode = null;
287 configurationMode = ConfigurationMode.valueOf(configMode);
288 } catch (Exception exception) {
289 LOGGER.error("Could not find convert {} into configuration mode.", configMode, exception);
291 return Optional.ofNullable(configurationMode);
294 public static Class getCollectionGenericType(Field field) {
295 Type type = field.getGenericType();
297 if (type instanceof ParameterizedType) {
299 ParameterizedType paramType = (ParameterizedType) type;
300 Type[] arr = paramType.getActualTypeArguments();
301 if (arr.length > 0) {
302 Class<?> clazz = (Class<?>) arr[0];
303 if (isWrapperClass(clazz)) {
306 throw new IllegalArgumentException("Collection of type " + clazz.getName() + " not supported.");
311 return String[].class;
314 public static boolean isWrapperClass(Class clazz) {
315 return clazz == String.class || clazz == Boolean.class || clazz == Character.class
316 || Number.class.isAssignableFrom(clazz);
319 public static Class getArrayClass(Class clazz) {
320 return ARRAY_CLASS_MAP.getOrDefault(clazz, null);
323 public static List<URL> getAllClassPathResources() {
324 return CPScanner.scanResources(new ResourceFilter());
327 public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(File file) {
328 FileBasedConfigurationBuilder<FileBasedConfiguration> builder;
329 ConfigurationType configType = ConfigurationUtils.getConfigType(file);
330 builder = getFileBasedConfigurationBuilder(configType);
331 builder.configure(new Parameters().fileBased().setFile(file)
332 .setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
336 public static BasicConfigurationBuilder<FileBasedConfiguration> getConfigurationBuilder(URL url) {
337 ConfigurationType configType = ConfigurationUtils.getConfigType(url);
338 ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder =
339 getFileBasedConfigurationBuilder(configType);
341 new Parameters().fileBased().setURL(url).setListDelimiterHandler(new DefaultListDelimiterHandler(',')));
345 private static ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> getFileBasedConfigurationBuilder(
346 ConfigurationType configType) {
348 ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
349 switch (configType) {
351 builder = new ReloadingFileBasedConfigurationBuilder<>(PropertiesConfiguration.class);
354 builder = new ReloadingFileBasedConfigurationBuilder<>(XMLConfiguration.class);
357 builder = new ReloadingFileBasedConfigurationBuilder<>(JsonConfiguration.class);
360 builder = new ReloadingFileBasedConfigurationBuilder<>(YamlConfiguration.class);
363 throw new IllegalArgumentException(CONFIGURATION_TYPE_NOT_SUPPORTED + configType);
368 public static <T> T read(Configuration config, Class<T> clazz, String keyPrefix) throws Exception {
369 Config confAnnotation = clazz.getAnnotation(Config.class);
370 if (confAnnotation != null) {
371 keyPrefix += (confAnnotation.key() + ".");
373 T objToReturn = clazz.newInstance();
374 for (Field field : clazz.getDeclaredFields()) {
375 Config fieldAnnotation = field.getAnnotation(Config.class);
376 if (fieldAnnotation != null) {
377 field.setAccessible(true);
378 field.set(objToReturn, config.getProperty(keyPrefix + fieldAnnotation.key()));
379 } else if (field.getType().getAnnotation(Config.class) != null) {
380 field.set(objToReturn, read(config, field.getType(), keyPrefix));
386 public static Object getPrimitiveArray(Collection collection, Class clazz) {
387 if (clazz == int.class) {
388 int[] array = new int[collection.size()];
389 Object[] objArray = collection.toArray();
390 for (int i = 0; i < collection.size(); i++) {
391 array[i] = (int) objArray[i];
395 if (clazz == byte.class) {
396 byte[] array = new byte[collection.size()];
397 Object[] objArray = collection.toArray();
398 for (int i = 0; i < collection.size(); i++) {
399 array[i] = (byte) objArray[i];
403 if (clazz == short.class) {
404 short[] array = new short[collection.size()];
405 Object[] objArray = collection.toArray();
406 for (int i = 0; i < collection.size(); i++) {
407 array[i] = (short) objArray[i];
411 if (clazz == long.class) {
412 long[] array = new long[collection.size()];
413 Object[] objArray = collection.toArray();
414 for (int i = 0; i < collection.size(); i++) {
415 array[i] = (long) objArray[i];
419 if (clazz == float.class) {
420 float[] array = new float[collection.size()];
421 Object[] objArray = collection.toArray();
422 for (int i = 0; i < collection.size(); i++) {
423 array[i] = (float) objArray[i];
427 if (clazz == double.class) {
428 double[] array = new double[collection.size()];
429 Object[] objArray = collection.toArray();
430 for (int i = 0; i < collection.size(); i++) {
431 array[i] = (double) objArray[i];
435 if (clazz == boolean.class) {
436 boolean[] array = new boolean[collection.size()];
437 Object[] objArray = collection.toArray();
438 for (int i = 0; i < collection.size(); i++) {
439 array[i] = (boolean) objArray[i];
446 public static String getCollectionString(String input) {
447 Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
448 Matcher matcher = pattern.matcher(input);
449 if (matcher.matches()) {
450 input = matcher.group(1);
455 public static String processVariablesIfPresent(String tenant, String namespace, String data) {
456 Pattern pattern = Pattern.compile("^.*\\$\\{(.*)\\}.*");
457 Matcher matcher = pattern.matcher(data);
458 if (matcher.matches()) {
459 final int substringStartIndex = 4;
460 String key = matcher.group(1);
462 if (key.toUpperCase().startsWith("ENV:")) {
463 value = System.getenv(key.substring(substringStartIndex));
464 } else if (key.toUpperCase().startsWith("SYS:")) {
465 value = System.getProperty(key.substring(substringStartIndex));
467 value = ConfigurationUtils.getCollectionString(
468 ConfigurationManager.lookup().getAsStringValues(tenant, namespace, key).toString());
470 return processVariablesIfPresent(tenant, namespace, data.replaceAll("\\$\\{" + key + "}",
471 value == null ? "" : value.replace("\\", "\\\\")));
477 public static String getFileContents(String path) {
480 return IOUtils.toString(new URL(path), Charset.defaultCharset());
482 } catch (Exception exception) {
483 LOGGER.error("Error while getting '{}' content", path, exception);
488 public static String getFileContents(Path path) {
491 return new String(Files.readAllBytes(path));
493 } catch (Exception exception) {
494 LOGGER.error("Error while getting '{}' content", path.toString(), exception);
499 public static Object getDefaultFor(Class clazz) {
500 if (byte.class == clazz) {
501 return new Byte("0");
502 } else if (short.class == clazz) {
503 return new Short("0");
504 } else if (int.class == clazz) {
505 return new Integer("0");
506 } else if (float.class == clazz) {
507 return new Float("0");
508 } else if (long.class == clazz) {
509 return new Long("0");
510 } else if (double.class == clazz) {
511 return new Double("0");
512 } else if (boolean.class == clazz) {
513 return Boolean.FALSE;
518 public static Collection getCompatibleCollectionForAbstractDef(Class clazz) {
519 if (TransferQueue.class.isAssignableFrom(clazz)) {
520 return getConcreteCollection(TransferQueue.class);
522 if (BlockingQueue.class.isAssignableFrom(clazz)) {
523 return getConcreteCollection(BlockingQueue.class);
525 if (Deque.class.isAssignableFrom(clazz)) {
526 return getConcreteCollection(Deque.class);
528 if (Queue.class.isAssignableFrom(clazz)) {
529 return getConcreteCollection(Queue.class);
531 if (SortedSet.class.isAssignableFrom(clazz)) {
532 return getConcreteCollection(SortedSet.class);
534 if (Set.class.isAssignableFrom(clazz)) {
535 return getConcreteCollection(Set.class);
537 if (List.class.isAssignableFrom(clazz)) {
538 return getConcreteCollection(List.class);
540 throw new IllegalArgumentException("Only corresponding array classes and any are allowed as argument."
541 + "assignable from TransferQueue, BlockingQueue, Deque, Queue, SortedSet, Set, List class");
544 public static Collection getConcreteCollection(Class clazz) {
545 switch (clazz.getName()) {
546 case "java.util.Collection":
547 case "java.util.List":
548 return new ArrayList<>();
549 case "java.util.Set":
550 return new HashSet<>();
551 case "java.util.SortedSet":
552 return new TreeSet<>();
553 case "java.util.Queue":
554 return new ConcurrentLinkedQueue<>();
555 case "java.util.Deque":
556 return new ArrayDeque<>();
557 case "java.util.concurrent.TransferQueue":
558 return new LinkedTransferQueue<>();
559 case "java.util.concurrent.BlockingQueue":
560 return new LinkedBlockingQueue<>();
562 throw new IllegalArgumentException("Only corresponding array classes and any are allowed as argument."
563 + "assignable from TransferQueue, BlockingQueue, Deque, Queue, SortedSet, Set, List class");
567 public static String getConfigurationRepositoryKey(File file) {
568 return getConfigurationRepositoryKey(
569 ConfigurationUtils.getNamespace(file).split(Constants.TENANT_NAMESPACE_SEPARATOR));
572 public static String getConfigurationRepositoryKey(URL url) {
573 return getConfigurationRepositoryKey(
574 ConfigurationUtils.getNamespace(url).split(Constants.TENANT_NAMESPACE_SEPARATOR));
577 public static String getConfigurationRepositoryKey(String[] array) {
578 Deque<String> stack = new ArrayDeque<>();
579 stack.push(Constants.DEFAULT_TENANT);
580 for (String element : array) {
583 String toReturn = stack.pop();
584 return stack.pop() + Constants.KEY_ELEMENTS_DELIMITER + toReturn;
587 public static String getNamespace(File file) {
588 Optional<String> namespace =
589 getConfiguration(file).flatMap(ConfigurationUtils::readNamespace).map(String::toUpperCase);
590 return namespace.orElseGet(() -> getNamespace(file.getName().toUpperCase()));
593 public static String getNamespace(String file) {
594 file = file.toUpperCase().substring(file.lastIndexOf('!') + 1);
595 file = file.substring(file.lastIndexOf('/') + 1);
596 Pattern pattern = Pattern.compile(
598 CONFIG_REGEX_TPL_OPT_1,
599 ConfigurationMode.OVERRIDE,
600 ConfigurationMode.MERGE,
601 ConfigurationMode.UNION,
602 ConfigurationType.PROPERTIES.name(),
603 ConfigurationType.XML.name(),
604 ConfigurationType.JSON.name(),
605 ConfigurationType.YAML.name()
608 Matcher matcher = pattern.matcher(file);
609 boolean b1 = matcher.matches();
611 if (matcher.group(1) != null) {
612 String moduleName = matcher.group(1).substring(1);
613 return moduleName.equalsIgnoreCase(ConfigurationMode.OVERRIDE.name()) || moduleName.equalsIgnoreCase(
614 ConfigurationMode.UNION.name()) || moduleName.equalsIgnoreCase(ConfigurationMode.MERGE.name())
615 ? Constants.DEFAULT_NAMESPACE : moduleName;
617 return Constants.DEFAULT_NAMESPACE;
619 } else if (isConfig(file)) {
620 return Constants.DEFAULT_NAMESPACE;
626 public static String getNamespace(URL url) {
628 Optional<String> namespace =
629 getConfiguration(url).flatMap(ConfigurationUtils::readNamespace).map(String::toUpperCase);
631 return namespace.orElseGet(() -> getNamespace(url.getFile().toUpperCase()));
634 public static Object getProperty(Configuration config, String key, int processingHints) {
636 if (!isDirectLookup(processingHints) && (config instanceof CompositeConfiguration)) {
638 CompositeConfiguration conf = (CompositeConfiguration) config;
639 for (int i = 0; i < conf.getNumberOfConfigurations(); i++) {
641 if (isNodeSpecific(processingHints)) {
642 Object obj = conf.getConfiguration(i).getProperty(key);
650 return config.getProperty(key);
653 public static boolean isCollection(String input) {
654 Pattern pattern = Pattern.compile("^\\[(.*)\\]$");
655 Matcher matcher = pattern.matcher(input);
656 return matcher.matches();
659 public static boolean isDirectLookup(int hints) {
660 return (hints & LATEST_LOOKUP.value()) == LATEST_LOOKUP.value();
663 public static boolean isNodeSpecific(int hints) {
664 return (hints & NODE_SPECIFIC.value()) == NODE_SPECIFIC.value();
667 public static boolean isExternalLookup(int hints) {
668 return (hints & EXTERNAL_LOOKUP.value()) == EXTERNAL_LOOKUP.value();
671 public static boolean isZeroLengthArray(Class clazz, Object obj) {
672 if (clazz.isArray() && clazz.getComponentType().isPrimitive()) {
673 if (clazz.getComponentType() == int.class) {
674 return ((int[]) obj).length == 0;
675 } else if (clazz.getComponentType() == byte.class) {
676 return ((byte[]) obj).length == 0;
677 } else if (clazz.getComponentType() == short.class) {
678 return ((short[]) obj).length == 0;
679 } else if (clazz.getComponentType() == float.class) {
680 return ((float[]) obj).length == 0;
681 } else if (clazz.getComponentType() == boolean.class) {
682 return ((boolean[]) obj).length == 0;
683 } else if (clazz.getComponentType() == double.class) {
684 return ((double[]) obj).length == 0;
685 } else if (clazz.getComponentType() == long.class) {
686 return ((long[]) obj).length == 0;
688 return ((Object[]) obj).length == 0;
695 public static boolean isBlank(String value) {
696 return value == null || value.trim().isEmpty();