001package org.kuali.common.util.spring.env;
002
003import static com.google.common.base.Preconditions.checkNotNull;
004
005import java.util.Map;
006import java.util.Properties;
007import java.util.Set;
008
009import org.kuali.common.util.PropertyUtils;
010import org.kuali.common.util.log.LoggerUtils;
011import org.slf4j.Logger;
012import org.springframework.core.env.MapPropertySource;
013
014import com.google.common.collect.ImmutableSet;
015import com.google.common.collect.Maps;
016
017/**
018 * <p>
019 * When {@code getProperty(name)} is called, {@code name} is checked. If no value is found, {@code name} is then converted into several aliases representing a few different ways it
020 * might be represented as an environment variable. All of the aliases are then also checked.
021 * </p>
022 * 
023 * <pre>
024 *   foo.barBaz -> env.FOO_BAR_BAZ
025 *                 FOO_BAR_BAZ
026 *                 env.foo_bar_baz
027 *                 foo_bar_baz
028 * </pre>
029 */
030public class SysEnvPropertySource extends MapPropertySource {
031
032        private static final Map<String, ImmutableSet<String>> ALIAS_CACHE = Maps.newConcurrentMap();
033        private static final String GLOBAL_PROPERTIES_PROPERTY_SOURCE_NAME = "systemPropertiesAndEnvironmentVariables";
034        private static final Logger logger = LoggerUtils.make();
035
036        public SysEnvPropertySource() {
037                this(GLOBAL_PROPERTIES_PROPERTY_SOURCE_NAME, PropertyUtils.getGlobalProperties());
038        }
039
040        public SysEnvPropertySource(String name, Properties source) {
041                this(name, convert(source));
042        }
043
044        public SysEnvPropertySource(String name, Map<String, Object> source) {
045                super(name, source);
046        }
047
048        /**
049         * {@inheritDoc}
050         */
051        @Override
052        public Object getProperty(String name) {
053                checkNotNull(name, "'name' cannot be null");
054                Object value = super.getProperty(name);
055                if (value != null) {
056                        return value;
057                } else {
058                        Set<String> aliases = getAliases(name);
059                        return getProperty(aliases, name);
060                }
061        }
062
063        protected Object getProperty(Set<String> aliases, String original) {
064                for (String alias : aliases) {
065                        Object value = super.getProperty(alias);
066                        if (value != null) {
067                                logger.debug(String.format("PropertySource [%s] does not contain '%s', but found equivalent '%s'", this.getName(), original, alias));
068                                return value;
069                        }
070                }
071                return null;
072        }
073
074        /**
075         * <pre>
076         *   foo.barBaz -> env.FOO_BAR_BAZ
077         *                 FOO_BAR_BAZ
078         *                 env.foo_bar_baz
079         *                 foo_bar_baz
080         * </pre>
081         */
082        protected ImmutableSet<String> getAliases(String name) {
083                ImmutableSet<String> aliases = ALIAS_CACHE.get(name);
084                if (aliases == null) {
085                        // foo.barBaz -> env.FOO_BAR_BAZ
086                        String env1 = EnvUtils.getEnvironmentVariableKey(name);
087                        // foo.barBaz -> FOO_BAR_BAZ
088                        String env2 = EnvUtils.toUnderscore(name).toUpperCase();
089                        // foo.barBaz -> env.foo_bar_baz
090                        String env3 = env1.toLowerCase();
091                        // foo.barBaz -> foo_bar_baz
092                        String env4 = env2.toLowerCase();
093                        aliases = ImmutableSet.of(env1, env2, env3, env4);
094                        ALIAS_CACHE.put(name, aliases);
095                }
096                return aliases;
097        }
098
099        protected static Map<String, Object> convert(Properties properties) {
100                Map<String, Object> map = Maps.newHashMap();
101                for (String key : properties.stringPropertyNames()) {
102                        map.put(key, properties.getProperty(key));
103                }
104                return map;
105        }
106
107        public void clearAliasCache() {
108                ALIAS_CACHE.clear();
109        }
110
111}