001/**
002 * Copyright 2010-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.common.util.spring.env;
017
018import static com.google.common.base.Preconditions.checkNotNull;
019
020import java.util.Map;
021import java.util.Properties;
022import java.util.Set;
023
024import org.kuali.common.util.PropertyUtils;
025import org.kuali.common.util.log.LoggerUtils;
026import org.slf4j.Logger;
027import org.springframework.core.env.MapPropertySource;
028
029import com.google.common.collect.ImmutableSet;
030import com.google.common.collect.Maps;
031
032/**
033 * <p>
034 * 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
035 * might be represented as an environment variable. All of the aliases are then also checked.
036 * </p>
037 * 
038 * <pre>
039 *   foo.barBaz -> env.FOO_BAR_BAZ
040 *                 FOO_BAR_BAZ
041 *                 env.foo_bar_baz
042 *                 foo_bar_baz
043 * </pre>
044 */
045public class SysEnvPropertySource extends MapPropertySource {
046
047        private static final Map<String, ImmutableSet<String>> ALIAS_CACHE = Maps.newConcurrentMap();
048        private static final String GLOBAL_PROPERTIES_PROPERTY_SOURCE_NAME = "systemPropertiesAndEnvironmentVariables";
049        private static final Logger logger = LoggerUtils.make();
050
051        public SysEnvPropertySource() {
052                this(GLOBAL_PROPERTIES_PROPERTY_SOURCE_NAME, PropertyUtils.getGlobalProperties());
053        }
054
055        public SysEnvPropertySource(String name, Properties source) {
056                this(name, convert(source));
057        }
058
059        public SysEnvPropertySource(String name, Map<String, Object> source) {
060                super(name, source);
061        }
062
063        /**
064         * {@inheritDoc}
065         */
066        @Override
067        public Object getProperty(String name) {
068                checkNotNull(name, "'name' cannot be null");
069                Object value = super.getProperty(name);
070                if (value != null) {
071                        return value;
072                } else {
073                        Set<String> aliases = getAliases(name);
074                        return getProperty(aliases, name);
075                }
076        }
077
078        protected Object getProperty(Set<String> aliases, String original) {
079                for (String alias : aliases) {
080                        Object value = super.getProperty(alias);
081                        if (value != null) {
082                                logger.debug(String.format("PropertySource [%s] does not contain '%s', but found equivalent '%s'", this.getName(), original, alias));
083                                return value;
084                        }
085                }
086                return null;
087        }
088
089        /**
090         * <pre>
091         *   foo.barBaz -> env.FOO_BAR_BAZ
092         *                 FOO_BAR_BAZ
093         *                 env.foo_bar_baz
094         *                 foo_bar_baz
095         * </pre>
096         */
097        protected ImmutableSet<String> getAliases(String name) {
098                ImmutableSet<String> aliases = ALIAS_CACHE.get(name);
099                if (aliases == null) {
100                        // foo.barBaz -> env.FOO_BAR_BAZ
101                        String env1 = EnvUtils.getEnvironmentVariableKey(name);
102                        // foo.barBaz -> FOO_BAR_BAZ
103                        String env2 = EnvUtils.toUnderscore(name).toUpperCase();
104                        // foo.barBaz -> env.foo_bar_baz
105                        String env3 = env1.toLowerCase();
106                        // foo.barBaz -> foo_bar_baz
107                        String env4 = env2.toLowerCase();
108                        aliases = ImmutableSet.of(env1, env2, env3, env4);
109                        ALIAS_CACHE.put(name, aliases);
110                }
111                return aliases;
112        }
113
114        protected static Map<String, Object> convert(Properties properties) {
115                Map<String, Object> map = Maps.newHashMap();
116                for (String key : properties.stringPropertyNames()) {
117                        map.put(key, properties.getProperty(key));
118                }
119                return map;
120        }
121
122        public void clearAliasCache() {
123                ALIAS_CACHE.clear();
124        }
125
126}