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