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