/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.tamaya.core.propertysource;
import org.apache.tamaya.spi.PropertyValue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* This {@link org.apache.tamaya.spi.PropertySource} manages the system properties. You can disable this feature by
* setting {@code tamaya.envprops.disable} or {@code tamaya.defaults.disable}.
*/
public class SystemPropertySource extends BasePropertySource {
/**
* default ordinal for {@link org.apache.tamaya.core.propertysource.SystemPropertySource}
*/
public static final int DEFAULT_ORDINAL = 1000;
private volatile Map<String, PropertyValue> cachedProperties;
/**
* previous System.getProperties().hashCode()
* so we can check if we need to reload
*/
private volatile int previousHash;
/**
* Prefix that allows system properties to virtually be mapped on specified sub section.
*/
private String prefix;
/**
* If true, this property source does not return any properties. This is useful since this
* property source is applied by default, but can be switched off by setting the
* {@code tamaya.envprops.disable} system/environment property to {@code true}.
*/
private boolean disabled = false;
/**
* Creates a new instance. Also initializes the {@code prefix} and {@code disabled} properties
* from the system-/ environment properties:
* <pre>
* tamaya.envprops.prefix
* tamaya.envprops.disable
* </pre>
*/
public SystemPropertySource(){
initFromSystemProperties();
if(!disabled){
cachedProperties = Collections.unmodifiableMap(loadProperties());
}
}
/**
* Initializes the {@code prefix} and {@code disabled} properties from the system-/
* environment properties:
* <pre>
* tamaya.envprops.prefix
* tamaya.envprops.disable
* </pre>
*/
private void initFromSystemProperties() {
String value = System.getProperty("tamaya.sysprops.prefix");
if(value==null){
prefix = System.getenv("tamaya.sysprops.prefix");
}
value = System.getProperty("tamaya.sysprops.disable");
if(value==null){
value = System.getenv("tamaya.sysprops.disable");
}
if(value==null){
value = System.getProperty("tamaya.defaults.disable");
}
if(value==null){
value = System.getenv("tamaya.defaults.disable");
}
if(value!=null && !value.isEmpty()) {
this.disabled = Boolean.parseBoolean(value);
}
}
/**
* Creates a new instance using a fixed ordinal value.
* @param ordinal the ordinal number.
*/
public SystemPropertySource(int ordinal){
this(null, ordinal);
}
/**
* Creates a new instance.
* @param prefix the prefix to be used, or null.
* @param ordinal the ordinal to be used.
*/
public SystemPropertySource(String prefix, int ordinal){
this.prefix = prefix;
setOrdinal(ordinal);
}
/**
* Creates a new instance.
* @param prefix the prefix to be used, or null.
*/
public SystemPropertySource(String prefix){
this.prefix = prefix;
}
@Override
public int getDefaultOrdinal() {
return DEFAULT_ORDINAL;
}
private Map<String,PropertyValue> loadProperties() {
Properties sysProps = System.getProperties();
previousHash = System.getProperties().hashCode();
final String prefix = this.prefix;
Map<String,PropertyValue> values = new HashMap<>();
for (Map.Entry<Object,Object> entry : sysProps.entrySet()) {
if(prefix==null) {
values.put(entry.getKey().toString(), PropertyValue.of(entry.getKey().toString(), entry.getValue().toString(), getName()));
}else {
values.put(prefix + entry.getKey(), PropertyValue.of(prefix + entry.getKey(), entry.getValue().toString(), getName()));
}
}
return values;
}
@Override
public String getName() {
if(disabled){
return "system-properties(disabled)";
}
return "system-properties";
}
@Override
public PropertyValue get(String key) {
if(disabled){
return null;
}
String prefix = this.prefix;
if(prefix==null) {
return PropertyValue.of(key, System.getProperty(key), getName());
}
return PropertyValue.of(key, System.getProperty(key.substring(prefix.length())), getName());
}
@Override
public Map<String, PropertyValue> getProperties() {
if(disabled){
return Collections.emptyMap();
}
// only need to reload and fill our map if something has changed
// synchronization was removed, Instance was marked as volatile. In the worst case it
// is reloaded twice, but the values will be the same.
if (previousHash != System.getProperties().hashCode()) {
this.cachedProperties = Collections.unmodifiableMap(loadProperties());
}
return this.cachedProperties;
}
@Override
public boolean isScannable() {
return true;
}
}