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