/*-
* ============LICENSE_START=======================================================
* openECOMP : APP-C
* ================================================================================
* Copyright (C) 2017 AT&T Intellectual Property. All rights
* reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END=========================================================
*/
package org.openecomp.appc.configuration;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.security.Provider;
import java.security.Provider.Service;
import java.security.Security;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openecomp.appc.encryption.EncryptionTool;
import org.openecomp.appc.util.UnmodifiableProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class provides the implementation of the Configuration
interface. It is created by the
* ConfigurationFactory and initialized with the configuration values for the process.
*
* @since Mar 18, 2014
* @version $Id$
*/
public final class DefaultConfiguration implements Configuration, Cloneable {
private static final Logger logger = LoggerFactory.getLogger(DefaultConfiguration.class);
/**
* The framework configuration properties.
*/
private Properties properties = new Properties();
/**
* Construct the configuration object.
*/
public DefaultConfiguration() {
}
/**
* Clears all properties
*/
public void clear() {
properties.clear();
}
/**
* @see java.lang.Object#clone()
*/
@Override
protected Object clone() throws CloneNotSupportedException {
DefaultConfiguration clone = (DefaultConfiguration) super.clone();
clone.properties = new Properties(this.properties);
clone.properties.putAll(this.properties);
return clone;
}
/**
* Decrypts an encrypted value, if it is encrypted, and returns the clear text. Performs no operation on the string
* if it is not encrypted.
*
* @param value
* The value to (optionally) be decrypted
* @return The clear text
*/
@SuppressWarnings("nls")
private static String decrypt(String value) {
if (value != null) {
if (value.startsWith(EncryptionTool.ENCRYPTED_VALUE_PREFIX)) {
try {
return EncryptionTool.getInstance().decrypt(value);
} catch (Throwable e) {
String out = "";
for (Provider p : Security.getProviders()) {
for (Service s : p.getServices()) {
String algo = s.getAlgorithm();
out +=
String.format("\n==Found Algorithm [ %s ] in provider [ %s ] and service [ %s ]", algo,
p.getName(), s.getClassName());
}
}
logger.debug(out);
logger.warn(String.format("Could not decrypt the configuration value [%s]", value), e);
}
}
}
return value;
}
/**
* Decrypts all elements in the properties object
*/
private void decryptAllProperties() {
if (properties != null) {
for (Entry e : properties.entrySet()) {
if (e.getValue() != null) {
e.setValue(decrypt(e.getValue().toString()));
}
}
}
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
DefaultConfiguration other = (DefaultConfiguration) obj;
if ((this.properties.size() == other.properties.size())
&& (this.properties.entrySet().containsAll(other.properties.entrySet()))
&& (other.properties.entrySet().containsAll(this.properties.entrySet()))) {
return true;
}
return false;
}
/**
* This method will use the properties object to expand any variables that may be present in the template provided.
* Variables are represented by the string "${name}", where "name" is the name of a property defined in either the
* current configuration object, or system properties if undefined. If the value cannot be found, the variable is
* removed and an empty string is used to replace the variable.
*
* @param template
* The template to be expanded
* @return The expanded template where each variable is replaced with its value
*/
@SuppressWarnings("nls")
private String expandVariables(String template) {
if (template == null) {
return template;
}
// Decrypt the template if needed
// template = decrypt(template); DH: Do not assign values to parameters, bad form! Also, Sonar complains
// bitterly
StringBuffer buffer = new StringBuffer(decrypt(template));
Pattern pattern = Pattern.compile("\\$\\{([^\\}]+)\\}");
Matcher matcher = pattern.matcher(buffer);
while (matcher.find()) {
String variable = matcher.group(1);
String value = properties.getProperty(variable);
if (value == null) {
value = System.getProperty(variable);
}
if (value == null) {
value = "";
}
buffer.replace(matcher.start(), matcher.end(), value);
matcher.reset();
}
return buffer.toString().trim();
}
/**
* This method is called to obtain a property expressed as a boolean value (true or false). The standard rules for
* Boolean.parseBoolean() are used.
*
* @param key
* The property key
* @return The value of the property expressed as a boolean, or false if it does not exist.
*/
@SuppressWarnings("nls")
@Override
public boolean getBooleanProperty(String key) {
return Boolean.valueOf(getProperty(key, "false")).booleanValue();
}
/**
* This method is called to obtain a property expressed as a boolean value (true or false). The standard rules for
* Boolean.valueOf(String) are used.
*
* @param key
* The property key
* @param defaultValue
* The default value to be returned if the property does not exist
* @return The value of the property expressed as a boolean, or false if it does not exist.
* @see org.openecomp.appc.configuration.Configuration#getBooleanProperty(java.lang.String, boolean)
*/
@Override
public boolean getBooleanProperty(String key, boolean defaultValue) {
if (isPropertyDefined(key)) {
return getBooleanProperty(key);
}
return defaultValue;
}
/**
* Returns the indicated property value expressed as a floating point double-precision value (double).
*
* @param key
* The property to retrieve
* @return The value of the property, or 0.0 if not found
* @see org.openecomp.appc.configuration.Configuration#getDoubleProperty(java.lang.String)
*/
@SuppressWarnings("nls")
@Override
public double getDoubleProperty(String key) {
try {
return Double.valueOf(getProperty(key, "0.0")).doubleValue();
} catch (NumberFormatException e) {
return 0.0;
}
}
/**
* This method is called to obtain a property as a string value
*
* @param key
* The key of the property
* @param defaultValue
* The default value to be returned if the property does not exist
* @return The string value, or null if it does not exist.
* @see org.openecomp.appc.configuration.Configuration#getDoubleProperty(java.lang.String, double)
*/
@Override
public double getDoubleProperty(String key, double defaultValue) {
if (isPropertyDefined(key)) {
return getDoubleProperty(key);
}
return defaultValue;
}
/**
* Returns the property indicated expressed as an integer. The standard rules for
* {@link Integer#parseInt(String, int)} using a radix of 10 are used.
*
* @param key
* The property name to retrieve.
* @returns The value of the property, or 0 if it does not exist or is invalid.
* @see org.openecomp.appc.configuration.Configuration#getIntegerProperty(java.lang.String)
*/
@SuppressWarnings("nls")
@Override
public int getIntegerProperty(String key) {
try {
return Integer.parseInt(getProperty(key, "0"), 10);
} catch (NumberFormatException e) {
return 0;
}
}
/**
* Returns the property indicated expressed as an integer. The standard rules for Integer.parseInt(String, int)
* using a radix of 10 are used.
*
* @param key
* The property name to retrieve.
* @param defaultValue
* The default value to be returned if the property does not exist
* @return The value of the property, or 0 if it does not exist or is invalid.
* @see org.openecomp.appc.configuration.Configuration#getIntegerProperty(java.lang.String, int)
*/
@Override
public int getIntegerProperty(String key, int defaultValue) {
if (isPropertyDefined(key)) {
return getIntegerProperty(key);
}
return defaultValue;
}
/**
* Returns the specified property as a long integer value, if it exists, or zero if it does not.
*
* @param key
* The key of the property desired.
* @return The value of the property expressed as an integer long value, or zero if the property does not exist or
* is not a valid integer long.
* @see org.openecomp.appc.configuration.Configuration#getLongProperty(java.lang.String)
*/
@SuppressWarnings("nls")
@Override
public long getLongProperty(String key) {
try {
return Long.parseLong(getProperty(key, "0"), 10);
} catch (NumberFormatException e) {
return 0;
}
}
/**
* Returns the specified property as a long integer value, if it exists, or the default value if it does not exist
* or is invalid.
*
* @param key
* The key of the property desired.
* @param defaultValue
* the value to be returned if the property is not valid or does not exist.
* @return The value of the property expressed as an integer long value, or the default value if the property does
* not exist or is not a valid integer long.
* @see org.openecomp.appc.configuration.Configuration#getLongProperty(java.lang.String, long)
*/
@Override
public long getLongProperty(String key, long defaultValue) {
if (isPropertyDefined(key)) {
return getLongProperty(key);
}
return defaultValue;
}
/**
* This method can be called to retrieve a properties object that is immutable. Any attempt to modify the properties
* object returned will result in an exception. This allows a caller to view the current configuration as a set of
* properties.
*
* @return An unmodifiable properties object.
* @see org.openecomp.appc.configuration.Configuration#getProperties()
*/
@Override
public Properties getProperties() {
return new UnmodifiableProperties(properties);
}
/**
* This method is called to obtain a property as a string value
*
* @param key
* The key of the property
* @return The string value, or null if it does not exist.
*/
@Override
public String getProperty(String key) {
String value = properties.getProperty(key);
if (value == null) {
return null;
}
return expandVariables(value.trim());
}
/**
* This method is called to obtain a property as a string value
*
* @param key
* The key of the property
* @param defaultValue
* The default value to be returned if the property does not exist
* @return The string value, or null if it does not exist.
* @see org.openecomp.appc.configuration.Configuration#getProperty(java.lang.String, java.lang.String)
*/
@Override
public String getProperty(String key, String defaultValue) {
if (isPropertyDefined(key)) {
return getProperty(key);
}
if (defaultValue == null) {
return null;
}
return expandVariables(defaultValue.trim());
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return (properties == null ? 0 : properties.hashCode());
}
/**
* Returns true if the named property is defined, false otherwise.
*
* @param key
* The key of the property we are interested in
* @return True if the property exists.
*/
@Override
public boolean isPropertyDefined(String key) {
return properties.containsKey(key);
}
/**
* Returns an indication of the validity of the boolean property. A boolean property is considered to be valid only
* if it has the value "true" or "false" (ignoring case).
*
* @param key
* The property to be checked
* @returns True if the value is a boolean constant, or false if it does not exist or is not a correct string
* @see org.openecomp.appc.configuration.Configuration#isValidBoolean(java.lang.String)
*/
@SuppressWarnings("nls")
@Override
public boolean isValidBoolean(String key) {
String value = getProperty(key);
if (value != null) {
value = value.toLowerCase();
return value.matches("true|false");
}
return false;
}
/**
* Returns an indication if the indicated property represents a valid double-precision floating point number.
*
* @param key
* The property to be examined
* @returns True if the property is a valid representation of a double, or false if it does not exist or contains
* illegal characters.
* @see org.openecomp.appc.configuration.Configuration#isValidDouble(java.lang.String)
*/
@Override
public boolean isValidDouble(String key) {
String value = getProperty(key);
if (value != null) {
try {
Double.valueOf(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}
return false;
}
/**
* Returns an indication if the property is a valid integer value or not.
*
* @param key
* The key of the property to check
* @returns True if the value is a valid integer string, or false if it does not exist or contains illegal
* characters.
* @see org.openecomp.appc.configuration.Configuration#isValidInteger(java.lang.String)
*/
@Override
public boolean isValidInteger(String key) {
String value = getProperty(key);
if (value != null) {
try {
Integer.parseInt(value.trim(), 10);
return true;
} catch (NumberFormatException e) {
return false;
}
}
return false;
}
/**
* Determines is the specified property exists and is a valid representation of an integer long value.
*
* @param key
* The property to be checked
* @return True if the property is a valid representation of an integer long value, and false if it either does not
* exist or is not valid.
* @see org.openecomp.appc.configuration.Configuration#isValidLong(java.lang.String)
*/
@Override
public boolean isValidLong(String key) {
String value = getProperty(key);
if (value != null) {
try {
Long.parseLong(value.trim(), 10);
return true;
} catch (NumberFormatException e) {
return false;
}
}
return false;
}
/**
* This method allows an implementation to load configuration properties that may override default values.
*
* @param is
* An input stream that contains the properties to be loaded
*/
public void setProperties(InputStream is) {
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* This method allows an implementation to load configuration properties that may override default values.
*
* @param props
* An optional Properties object to be merged into the configuration, replacing any same-named
* properties.
* @see org.openecomp.appc.configuration.Configuration#setProperties(java.util.Properties)
*/
@Override
public void setProperties(Properties props) {
properties.putAll(props);
decryptAllProperties();
}
/**
* This method allows a caller to insert a new property definition into the configuration object. This allows the
* application to adjust or add to the current configuration. If the property already exists, it is replaced with
* the new value.
*
* @param key
* The key of the property to be defined
* @param value
* The value of the property to be defined
* @see org.openecomp.appc.configuration.Configuration#setProperty(java.lang.String, java.lang.String)
*/
@Override
public void setProperty(String key, String value) {
properties.setProperty(key, decrypt(value));
}
/**
* @see java.lang.Object#toString()
*/
@SuppressWarnings("nls")
@Override
public String toString() {
return String.format("Configuration: %d properties, keys:[%s]", properties.size(), properties.keySet()
.toString());
}
/**
* This is a helper method to read the manifest of the jar file that this class was loaded from. Note that this will
* only work if the code is packaged in a jar file. If it is an open deployment, such as under eclipse, this will
* not work and there is code added to detect that case.
*
* @return The manifest object from the jar file, or null if the code is not packaged in a jar file.
*/
@SuppressWarnings({
"unused", "nls"
})
private Manifest getManifest() {
ProtectionDomain domain = getClass().getProtectionDomain();
CodeSource source = domain.getCodeSource();
URL location = source.getLocation();
String path = location.getPath();
int index = path.indexOf('!');
if (index != -1) {
path = path.substring(0, index);
}
if (path.endsWith(".jar")) {
try (JarFile jar = new JarFile(location.getFile())) {
return jar.getManifest();
} catch (IOException e) {
logger.error("getManifest", e);
}
}
return null;
}
}