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