View Javadoc

1   package org.kuali.ole.utility;
2   
3   import java.io.IOException;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Properties;
9   
10  import org.apache.commons.lang.StringUtils;
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  /**
15   * 
16   */
17  public class PropertyUtils {
18  	private static final Logger LOG = LoggerFactory.getLogger(PropertyUtils.class);
19  	public static final String ENVIRONMENT_PROPERTY_PREFIX = "env";
20  	public static final String CR = "\r";
21  	public static final String LF = "\n";
22  	private static final ResourceUtil resourceUtil = new ResourceUtil();
23  	private static final PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}");
24  
25  	protected PropertyUtils() {
26  		super();
27  	}
28  
29  	/**
30  	 * Return a list containing a pointer to the location of common properties + the explicit location they specified + the value of the system property (if the system property is
31  	 * set to a valid location)
32  	 */
33  	public static final List<String> getLocations(String location, String systemProperty, String externalConfigFile) {
34  		List<String> list = new ArrayList<String>();
35  		list.add(Constants.COMMON_PROPERTIES_LOCATION);
36  		list.add(location);
37  		if (resourceUtil.exists(externalConfigFile)) {
38  			list.add(externalConfigFile);
39  		} else {
40  			LOG.info("Skipping [" + externalConfigFile + "].  File does not exist");
41  		}
42  		if (validate(systemProperty)) {
43  			list.add(System.getProperty(systemProperty));
44  			LOG.info("System property " + systemProperty + "=" + System.getProperty(systemProperty));
45  		} else {
46  			LOG.info("System property [" + systemProperty + "] is not set");
47  		}
48  		return list;
49  	}
50  
51  	/**
52  	 * Return false if the system property is not set. Return true if the system property is set AND it points to a resource Spring resource loading can understand. Throw an
53  	 * <code>IllegalArgumentException</code> if the system property is set, but the location it points to does not exist.
54  	 */
55  	public static final boolean validate(String systemProperty) {
56  		String location = System.getProperty(systemProperty);
57  		if (StringUtils.isBlank(location)) {
58  			return false;
59  		}
60  		if (resourceUtil.exists(location)) {
61  			return true;
62  		} else {
63  			// Throw an exception here because they explicitly set a system property that points to an invalid location
64  			throw new IllegalArgumentException(location + " does not exist");
65  		}
66  	}
67  
68  	/**
69  	 * Load and resolve properties for an environment. Properties are loaded from the list of locations. If there are environment specific properties, the default values are
70  	 * overridden with environment specific values. In the properties object returned by this method, any placeholders present in the property values will be fully resolved.
71  	 */
72  	public static final Properties getAppEnvironmentProperties(List<String> locations) throws IOException {
73  		// Default values loaded from the locations provided
74  		Properties props = resourceUtil.getProperties(locations);
75  		// Override with environment variables
76  		props.putAll(getEnvironmentProperties());
77  		// Override with system properties
78  		props.putAll(System.getProperties());
79  		// Extract the environment we are running in
80  		String environment = props.getProperty(Constants.ENVIRONMENT_PROPERTY, Constants.DEFAULT_ENVIRONMENT);
81  		LOG.info(Constants.ENVIRONMENT_PROPERTY + "=" + environment);
82  		// Override default values with values that are specific to the environment we are running in
83  		overrideProperties(props, environment);
84  		// Resolve placeholders in the property values
85  		resolveProperties(props);
86  
87  		// Log information about the properties we've loaded
88  		if (LOG.isInfoEnabled()) {
89  			List<String> keys = new ArrayList<String>(props.stringPropertyNames());
90  			Collections.sort(keys);
91  			LOG.info("Displaying " + props.size() + " properties for environment '" + environment + "'");
92  			LOG.info("--------------------------------------------------------------------");
93  			for (String key : keys) {
94  				String value = props.getProperty(key);
95  				LOG.info(key + "=" + flatten(value));
96  			}
97  			LOG.info("--------------------------------------------------------------------");
98  		}
99  
100 		// Return what we've found
101 		return props;
102 	}
103 
104 	/**
105 	 * Return a properties object that aggregates the properties passed in with environment variables and system properties. Environment variables are included as properties
106 	 * prefixed with <code>env</code>. If there are duplicate property values, the system property value will always "win".
107 	 */
108 	public static final Properties getAllProperties(Properties properties) {
109 		Properties all = new Properties();
110 		all.putAll(properties);
111 		all.putAll(getEnvironmentProperties());
112 		all.putAll(System.getProperties());
113 		return all;
114 	}
115 
116 	/**
117 	 * Return the keys from the properties as a sorted list.
118 	 */
119 	public static final List<String> getSortedKeys(Properties properties) {
120 		List<String> keys = new ArrayList<String>(properties.stringPropertyNames());
121 		Collections.sort(keys);
122 		return keys;
123 	}
124 
125 	/**
126 	 * Resolve placeholders in the properties. Properties are always resolved using the properties passed in plus environment variables and system properties.
127 	 */
128 	public static final void resolveProperties(Properties props) {
129 		Properties resolverProperties = getAllProperties(props);
130 		List<String> keys = getSortedKeys(props);
131 		Properties resolvedProperties = new Properties();
132 		for (String key : keys) {
133 			String oldValue = props.getProperty(key);
134 			String newValue = helper.replacePlaceholders(oldValue, resolverProperties);
135 			addIfDifferent(key, oldValue, newValue, resolvedProperties);
136 		}
137 		if (resolvedProperties.size() > 0) {
138 			LOG.info("Resolved " + resolvedProperties.size() + " property values");
139 		}
140 		props.putAll(resolvedProperties);
141 	}
142 
143 	/**
144 	 * Override property values for a given environment. For example, if <code>batch.size=5</code> and <code>dev.batch.size=10</code> are both provided and environment is
145 	 * <code>dev</code>, <code>batch.size</code> will be updated to 10
146 	 */
147 	public static final void overrideProperties(Properties props, String environment) {
148 		if (StringUtils.isBlank(environment)) {
149 			return;
150 		}
151 		Properties all = getAllProperties(props);
152 		List<String> keys = getSortedKeys(all);
153 		Properties overridenProperties = new Properties();
154 		String prefix = environment + ".";
155 		for (String key : keys) {
156 			if (!key.startsWith(prefix)) {
157 				continue;
158 			}
159 			String existingKey = key.substring(prefix.length());
160 			String oldValue = all.getProperty(existingKey);
161 			String newValue = all.getProperty(key);
162 			addIfDifferent(existingKey, oldValue, newValue, overridenProperties);
163 		}
164 		if (overridenProperties.size() > 0) {
165 			LOG.info("Overrode " + overridenProperties.size() + " values for environment [" + environment + "]");
166 		}
167 		props.putAll(overridenProperties);
168 	}
169 
170 	/**
171 	 * If <code>newValue</code> is different than <code>oldValue</code>, add <code>key</code> to the properties using <code>newValue</code>
172 	 */
173 	protected static final void addIfDifferent(String key, String oldValue, String newValue, Properties props) {
174 		if (!newValue.equals(oldValue)) {
175 			props.setProperty(key, newValue);
176 			LOG.info("Updating " + key + " [" + flatten(oldValue) + "] -> [" + flatten(newValue) + "]");
177 		}
178 	}
179 
180 	/**
181 	 * Replace carriage returns with <code>CR</code>. Replace linefeeds with <code>LF</code>.
182 	 */
183 	public static final String flatten(String s) {
184 		if (s == null) {
185 			return s;
186 		} else {
187 			return s.replace(CR, "CR").replace(LF, "LF");
188 		}
189 	}
190 
191 	/**
192 	 * Return environment variables as properties where each property is prefixed with "env"
193 	 */
194 	public static final Properties getEnvironmentProperties() {
195 		Map<String, String> env = System.getenv();
196 		Properties props = new Properties();
197 		for (String key : env.keySet()) {
198 			String newKey = ENVIRONMENT_PROPERTY_PREFIX + "." + key;
199 			String value = env.get(key);
200 			props.setProperty(newKey, value);
201 		}
202 		return props;
203 	}
204 
205 }