Remove catch throwable and error
[appc.git] / appc-common / src / main / java / org / openecomp / appc / configuration / DefaultConfiguration.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.openecomp.appc.configuration;
26
27
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.net.URL;
31 import java.security.CodeSource;
32 import java.security.ProtectionDomain;
33 import java.security.Provider;
34 import java.security.Provider.Service;
35 import java.security.Security;
36 import java.util.Map.Entry;
37 import java.util.Properties;
38 import java.util.jar.JarFile;
39 import java.util.jar.Manifest;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43 import org.openecomp.appc.encryption.EncryptionTool;
44 import org.openecomp.appc.util.UnmodifiableProperties;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * This class provides the implementation of the <code>Configuration</code> interface. It is created by the
50  * ConfigurationFactory and initialized with the configuration values for the process.
51  *
52  * @version $Id$
53  * @since Mar 18, 2014
54  */
55 public final class DefaultConfiguration implements Configuration, Cloneable {
56
57     private static final Logger logger = LoggerFactory.getLogger(DefaultConfiguration.class);
58
59     /**
60      * The framework configuration properties.
61      */
62     private Properties properties = new Properties();
63
64     /**
65      * Construct the configuration object.
66      */
67     public DefaultConfiguration() {
68         // do nothing
69     }
70
71     /**
72      * Clears all properties
73      */
74     public void clear() {
75         properties.clear();
76     }
77
78     /**
79      * @see java.lang.Object#clone()
80      */
81     @Override
82     protected Object clone() throws CloneNotSupportedException {
83         DefaultConfiguration clone = (DefaultConfiguration) super.clone();
84
85         clone.properties = new Properties(this.properties);
86         clone.properties.putAll(this.properties);
87
88         return clone;
89     }
90
91     /**
92      * Decrypts an encrypted value, if it is encrypted, and returns the clear text. Performs no operation on the string
93      * if it is not encrypted.
94      *
95      * @param value The value to (optionally) be decrypted
96      * @return The clear text
97      */
98     @SuppressWarnings("nls")
99     private static String decrypt(String value) {
100         if (value != null) {
101             if (value.startsWith(EncryptionTool.ENCRYPTED_VALUE_PREFIX)) {
102                 try {
103                     return EncryptionTool.getInstance().decrypt(value);
104                 } catch (Exception e) {
105                     StringBuilder out = new StringBuilder();
106                     for (Provider p : Security.getProviders()) {
107                         for (Service s : p.getServices()) {
108                             String algo = s.getAlgorithm();
109                             out.append(String.format("\n==Found Algorithm [ %s ] in provider [ %s ] and service [ %s ]",
110                                     algo, p.getName(), s.getClassName()));
111                         }
112                     }
113                     logger.debug(out.toString());
114                     logger.warn(String.format("Could not decrypt the configuration value [%s]", value), e);
115                 }
116             }
117         }
118         return value;
119     }
120
121     /**
122      * Decrypts all elements in the properties object
123      */
124     private void decryptAllProperties() {
125         if (properties != null) {
126             for (Entry<Object, Object> e : properties.entrySet()) {
127                 if (e.getValue() != null) {
128                     e.setValue(decrypt(e.getValue().toString()));
129                 }
130             }
131         }
132     }
133
134     /**
135      * @see java.lang.Object#equals(java.lang.Object)
136      */
137     @Override
138     public boolean equals(Object obj) {
139
140         if (obj == null) {
141             return false;
142         }
143
144         if (this.getClass() != obj.getClass()) {
145             return false;
146         }
147
148         DefaultConfiguration other = (DefaultConfiguration) obj;
149
150         return (this.properties.size() == other.properties.size())
151                 && (this.properties.entrySet().containsAll(other.properties.entrySet()))
152                 && (other.properties.entrySet().containsAll(this.properties.entrySet()));
153
154     }
155
156     /**
157      * This method will use the properties object to expand any variables that may be present in the template provided.
158      * Variables are represented by the string "${name}", where "name" is the name of a property defined in either the
159      * current configuration object, or system properties if undefined. If the value cannot be found, the variable is
160      * removed and an empty string is used to replace the variable.
161      *
162      * @param template The template to be expanded
163      * @return The expanded template where each variable is replaced with its value
164      */
165     @SuppressWarnings("nls")
166     private String expandVariables(String template) {
167         if (template == null) {
168             return null;
169         }
170
171         // Decrypt the template if needed
172         // template = decrypt(template); DH: Do not assign values to parameters, bad form! Also, Sonar complains
173         // bitterly
174
175         StringBuffer buffer = new StringBuffer(decrypt(template));
176         Pattern pattern = Pattern.compile("\\$\\{([^\\}]+)\\}");
177         Matcher matcher = pattern.matcher(buffer);
178         while (matcher.find()) {
179             String variable = matcher.group(1);
180             String value = properties.getProperty(variable);
181             if (value == null) {
182                 value = System.getProperty(variable);
183             }
184             if (value == null) {
185                 value = "";
186             }
187             buffer.replace(matcher.start(), matcher.end(), value);
188
189             matcher.reset();
190         }
191         return buffer.toString().trim();
192     }
193
194     /**
195      * This method is called to obtain a property expressed as a boolean value (true or false). The standard rules for
196      * Boolean.parseBoolean() are used.
197      *
198      * @param key The property key
199      * @return The value of the property expressed as a boolean, or false if it does not exist.
200      */
201     @SuppressWarnings("nls")
202     @Override
203     public boolean getBooleanProperty(String key) {
204         return Boolean.valueOf(getProperty(key, "false"));
205     }
206
207     /**
208      * This method is called to obtain a property expressed as a boolean value (true or false). The standard rules for
209      * Boolean.valueOf(String) are used.
210      *
211      * @param key          The property key
212      * @param defaultValue The default value to be returned if the property does not exist
213      * @return The value of the property expressed as a boolean, or false if it does not exist.
214      * @see org.openecomp.appc.configuration.Configuration#getBooleanProperty(java.lang.String, boolean)
215      */
216     @Override
217     public boolean getBooleanProperty(String key, boolean defaultValue) {
218         if (isPropertyDefined(key)) {
219             return getBooleanProperty(key);
220         }
221         return defaultValue;
222     }
223
224     /**
225      * Returns the indicated property value expressed as a floating point double-precision value (double).
226      *
227      * @param key The property to retrieve
228      * @return The value of the property, or 0.0 if not found
229      * @see org.openecomp.appc.configuration.Configuration#getDoubleProperty(java.lang.String)
230      */
231     @SuppressWarnings("nls")
232     @Override
233     public double getDoubleProperty(String key) {
234         try {
235             return Double.valueOf(getProperty(key, "0.0"));
236         } catch (NumberFormatException e) {
237             return 0.0;
238         }
239     }
240
241     /**
242      * This method is called to obtain a property as a string value
243      *
244      * @param key          The key of the property
245      * @param defaultValue The default value to be returned if the property does not exist
246      * @return The string value, or null if it does not exist.
247      * @see org.openecomp.appc.configuration.Configuration#getDoubleProperty(java.lang.String, double)
248      */
249     @Override
250     public double getDoubleProperty(String key, double defaultValue) {
251         if (isPropertyDefined(key)) {
252             return getDoubleProperty(key);
253         }
254         return defaultValue;
255     }
256
257     /**
258      * Returns the property indicated expressed as an integer. The standard rules for
259      * {@link Integer#parseInt(String, int)} using a radix of 10 are used.
260      *
261      * @param key The property name to retrieve.
262      * @return The value of the property, or 0 if it does not exist or is invalid.
263      * @see org.openecomp.appc.configuration.Configuration#getIntegerProperty(java.lang.String)
264      */
265     @SuppressWarnings("nls")
266     @Override
267     public int getIntegerProperty(String key) {
268         try {
269             return Integer.parseInt(getProperty(key, "0"), 10);
270         } catch (NumberFormatException e) {
271             return 0;
272         }
273     }
274
275     /**
276      * Returns the property indicated expressed as an integer. The standard rules for Integer.parseInt(String, int)
277      * using a radix of 10 are used.
278      *
279      * @param key          The property name to retrieve.
280      * @param defaultValue The default value to be returned if the property does not exist
281      * @return The value of the property, or 0 if it does not exist or is invalid.
282      * @see org.openecomp.appc.configuration.Configuration#getIntegerProperty(java.lang.String, int)
283      */
284     @Override
285     public int getIntegerProperty(String key, int defaultValue) {
286         if (isPropertyDefined(key)) {
287             return getIntegerProperty(key);
288         }
289         return defaultValue;
290     }
291
292     /**
293      * Returns the specified property as a long integer value, if it exists, or zero if it does not.
294      *
295      * @param key The key of the property desired.
296      * @return The value of the property expressed as an integer long value, or zero if the property does not exist or
297      * is not a valid integer long.
298      * @see org.openecomp.appc.configuration.Configuration#getLongProperty(java.lang.String)
299      */
300     @SuppressWarnings("nls")
301     @Override
302     public long getLongProperty(String key) {
303         try {
304             return Long.parseLong(getProperty(key, "0"), 10);
305         } catch (NumberFormatException e) {
306             return 0;
307         }
308     }
309
310     /**
311      * Returns the specified property as a long integer value, if it exists, or the default value if it does not exist
312      * or is invalid.
313      *
314      * @param key          The key of the property desired.
315      * @param defaultValue the value to be returned if the property is not valid or does not exist.
316      * @return The value of the property expressed as an integer long value, or the default value if the property does
317      * not exist or is not a valid integer long.
318      * @see org.openecomp.appc.configuration.Configuration#getLongProperty(java.lang.String, long)
319      */
320     @Override
321     public long getLongProperty(String key, long defaultValue) {
322         if (isPropertyDefined(key)) {
323             return getLongProperty(key);
324         }
325         return defaultValue;
326     }
327
328     /**
329      * This method can be called to retrieve a properties object that is immutable. Any attempt to modify the properties
330      * object returned will result in an exception. This allows a caller to view the current configuration as a set of
331      * properties.
332      *
333      * @return An unmodifiable properties object.
334      * @see org.openecomp.appc.configuration.Configuration#getProperties()
335      */
336     @Override
337     public Properties getProperties() {
338         return new UnmodifiableProperties(properties);
339     }
340
341     /**
342      * This method is called to obtain a property as a string value
343      *
344      * @param key The key of the property
345      * @return The string value, or null if it does not exist.
346      */
347     @Override
348     public String getProperty(String key) {
349         String value = properties.getProperty(key);
350         if (value == null) {
351             return null;
352         }
353         return expandVariables(value.trim());
354     }
355
356     /**
357      * This method is called to obtain a property as a string value
358      *
359      * @param key          The key of the property
360      * @param defaultValue The default value to be returned if the property does not exist
361      * @return The string value, or null if it does not exist.
362      * @see org.openecomp.appc.configuration.Configuration#getProperty(java.lang.String, java.lang.String)
363      */
364     @Override
365     public String getProperty(String key, String defaultValue) {
366         if (isPropertyDefined(key)) {
367             return getProperty(key);
368         }
369
370         if (defaultValue == null) {
371             return null;
372         }
373
374         return expandVariables(defaultValue.trim());
375     }
376
377     /**
378      * @see java.lang.Object#hashCode()
379      */
380     @Override
381     public int hashCode() {
382         return (properties == null ? 0 : properties.hashCode());
383     }
384
385     /**
386      * Returns true if the named property is defined, false otherwise.
387      *
388      * @param key The key of the property we are interested in
389      * @return True if the property exists.
390      */
391     @Override
392     public boolean isPropertyDefined(String key) {
393         return properties.containsKey(key);
394     }
395
396     /**
397      * Returns an indication of the validity of the boolean property. A boolean property is considered to be valid only
398      * if it has the value "true" or "false" (ignoring case).
399      *
400      * @param key The property to be checked
401      * @return True if the value is a boolean constant, or false if it does not exist or is not a correct string
402      * @see org.openecomp.appc.configuration.Configuration#isValidBoolean(java.lang.String)
403      */
404     @SuppressWarnings("nls")
405     @Override
406     public boolean isValidBoolean(String key) {
407         String value = getProperty(key);
408         if (value != null) {
409             value = value.toLowerCase();
410             return value.matches("true|false");
411         }
412         return false;
413     }
414
415     /**
416      * Returns an indication if the indicated property represents a valid double-precision floating point number.
417      *
418      * @param key The property to be examined
419      * @return True if the property is a valid representation of a double, or false if it does not exist or contains
420      * illegal characters.
421      * @see org.openecomp.appc.configuration.Configuration#isValidDouble(java.lang.String)
422      */
423     @Override
424     public boolean isValidDouble(String key) {
425         String value = getProperty(key);
426         if (value != null) {
427             try {
428                 Double.valueOf(value);
429                 return true;
430             } catch (NumberFormatException e) {
431                 return false;
432             }
433         }
434         return false;
435     }
436
437     /**
438      * Returns an indication if the property is a valid integer value or not.
439      *
440      * @param key The key of the property to check
441      * @return True if the value is a valid integer string, or false if it does not exist or contains illegal
442      * characters.
443      * @see org.openecomp.appc.configuration.Configuration#isValidInteger(java.lang.String)
444      */
445     @Override
446     public boolean isValidInteger(String key) {
447         String value = getProperty(key);
448         if (value != null) {
449             try {
450                 Integer.parseInt(value.trim(), 10);
451                 return true;
452             } catch (NumberFormatException e) {
453                 return false;
454             }
455         }
456         return false;
457     }
458
459     /**
460      * Determines is the specified property exists and is a valid representation of an integer long value.
461      *
462      * @param key The property to be checked
463      * @return True if the property is a valid representation of an integer long value, and false if it either does not
464      * exist or is not valid.
465      * @see org.openecomp.appc.configuration.Configuration#isValidLong(java.lang.String)
466      */
467     @Override
468     public boolean isValidLong(String key) {
469         String value = getProperty(key);
470         if (value != null) {
471             try {
472                 Long.parseLong(value.trim(), 10);
473                 return true;
474             } catch (NumberFormatException e) {
475                 return false;
476             }
477         }
478         return false;
479     }
480
481     /**
482      * This method allows an implementation to load configuration properties that may override default values.
483      *
484      * @param is An input stream that contains the properties to be loaded
485      */
486     public void setProperties(InputStream is) {
487         try {
488             properties.load(is);
489         } catch (IOException e) {
490             e.printStackTrace();
491         }
492     }
493
494     /**
495      * This method allows an implementation to load configuration properties that may override default values.
496      *
497      * @param props An optional Properties object to be merged into the configuration, replacing any same-named
498      *              properties.
499      * @see org.openecomp.appc.configuration.Configuration#setProperties(java.util.Properties)
500      */
501     @Override
502     public void setProperties(Properties props) {
503         properties.putAll(props);
504         decryptAllProperties();
505     }
506
507     /**
508      * This method allows a caller to insert a new property definition into the configuration object. This allows the
509      * application to adjust or add to the current configuration. If the property already exists, it is replaced with
510      * the new value.
511      *
512      * @param key   The key of the property to be defined
513      * @param value The value of the property to be defined
514      * @see org.openecomp.appc.configuration.Configuration#setProperty(java.lang.String, java.lang.String)
515      */
516     @Override
517     public void setProperty(String key, String value) {
518         properties.setProperty(key, decrypt(value));
519     }
520
521     /**
522      * @see java.lang.Object#toString()
523      */
524     @SuppressWarnings("nls")
525     @Override
526     public String toString() {
527         return String.format("Configuration: %d properties, keys:[%s]", properties.size(), properties.keySet()
528                 .toString());
529     }
530
531     /**
532      * This is a helper method to read the manifest of the jar file that this class was loaded from. Note that this will
533      * only work if the code is packaged in a jar file. If it is an open deployment, such as under eclipse, this will
534      * not work and there is code added to detect that case.
535      *
536      * @return The manifest object from the jar file, or null if the code is not packaged in a jar file.
537      */
538     @SuppressWarnings({
539             "unused", "nls"
540     })
541     private Manifest getManifest() {
542         ProtectionDomain domain = getClass().getProtectionDomain();
543         CodeSource source = domain.getCodeSource();
544         URL location = source.getLocation();
545         String path = location.getPath();
546         int index = path.indexOf('!');
547         if (index != -1) {
548             path = path.substring(0, index);
549         }
550         if (path.endsWith(".jar")) {
551             try (JarFile jar = new JarFile(location.getFile())) {
552                 return jar.getManifest();
553             } catch (IOException e) {
554                 logger.error("getManifest", e);
555             }
556         }
557
558         return null;
559     }
560 }