1 package org.kuali.spring.util;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.InputStreamReader;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Properties;
11
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14 import org.springframework.core.io.Resource;
15 import org.springframework.core.io.support.PropertiesLoaderSupport;
16 import org.springframework.util.CollectionUtils;
17 import org.springframework.util.DefaultPropertiesPersister;
18 import org.springframework.util.ObjectUtils;
19 import org.springframework.util.PropertiesPersister;
20
21 public class PropertiesLoader {
22 private static final Logger logger = LoggerFactory.getLogger(PropertiesLoader.class);
23
24 public static final String DEFAULT_ENVIRONMENT_PROPERTY_PREFIX = "env.";
25 public static final boolean DEFAULT_IS_USE_ENVIRONMENT_PROPERTY_PREFIX = false;
26 public static final boolean DEFAULT_IS_LOCAL_OVERRIDE = false;
27 public static final boolean DEFAULT_IS_IGNORE_RESOURCE_NOT_FOUND = false;
28 public static final boolean DEFAULT_IS_SEARCH_SYSTEM_ENVIRONMENT = true;
29 public static final SystemPropertiesMode DEFAULT_SYSTEM_PROPERTIES_MODE = SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_OVERRIDE;
30
31
32 String environmentPropertyPrefix = DEFAULT_ENVIRONMENT_PROPERTY_PREFIX;
33 boolean useEnvironmentPropertyPrefix = DEFAULT_IS_USE_ENVIRONMENT_PROPERTY_PREFIX;
34 SystemPropertiesMode systemPropertiesMode = DEFAULT_SYSTEM_PROPERTIES_MODE;
35 boolean localOverride = DEFAULT_IS_LOCAL_OVERRIDE;
36 boolean ignoreResourceNotFound = DEFAULT_IS_IGNORE_RESOURCE_NOT_FOUND;
37 boolean searchSystemEnvironment = DEFAULT_IS_SEARCH_SYSTEM_ENVIRONMENT;
38
39
40 String fileEncoding;
41 Properties[] localProperties;
42 Resource[] locations;
43
44
45 PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
46 PropertyLogger plogger = new PropertyLogger();
47
48
49 Properties systemProperties;
50 Properties environmentProperties;
51 Properties resourceProperties;
52 Properties mergedLocalProperties;
53 Properties properties;
54
55
56
57
58 protected Properties getEnvironmentProperties(String prefix) {
59 Map<String, String> environmentMap = SystemUtils.getEnvironmentIgnoreExceptions();
60 Properties envProps = new Properties();
61 for (Map.Entry<String, String> entry : environmentMap.entrySet()) {
62 String key = (prefix == null) ? entry.getKey() : prefix + entry.getKey();
63 envProps.setProperty(key, entry.getValue());
64 }
65 return envProps;
66 }
67
68
69
70
71
72
73
74
75
76
77 protected Properties getProperties(Resource location, InputStream is) throws IOException {
78 Properties properties = new Properties();
79
80 if (isXMLFile(location)) {
81 getPropertiesPersister().loadFromXml(properties, is);
82 return properties;
83 }
84
85 if (getFileEncoding() != null) {
86
87 getPropertiesPersister().load(properties, new InputStreamReader(is, getFileEncoding()));
88 } else {
89
90 getPropertiesPersister().load(properties, is);
91 }
92 return properties;
93 }
94
95
96
97
98
99
100
101
102
103 protected Properties loadProperties(Resource location) throws IOException {
104
105 if (!location.exists()) {
106 return handleResourceNotFound(location);
107 }
108
109
110 logger.info("Loading properties from {}", location);
111 InputStream is = null;
112 try {
113 is = location.getInputStream();
114 return getProperties(location, is);
115 } catch (IOException e) {
116 throw e;
117 } finally {
118 nullSafeClose(is);
119 }
120 }
121
122
123
124
125
126
127
128 protected boolean isXMLFile(Resource location) {
129 String filename = null;
130 try {
131 filename = location.getFilename();
132 } catch (IllegalStateException ex) {
133
134 return false;
135 }
136
137 if (filename == null) {
138 return false;
139 }
140
141 if (filename.endsWith(PropertiesLoaderSupport.XML_FILE_EXTENSION)) {
142 return true;
143 } else {
144 return false;
145 }
146 }
147
148
149
150
151
152
153
154 protected Properties handleResourceNotFound(Resource location) {
155 if (isIgnoreResourceNotFound()) {
156 logger.info("Ignoring properties from {}. Resource not found", location);
157 return new Properties();
158 } else {
159 throw new PropertiesLoadException("Resource not found: " + location);
160 }
161 }
162
163
164
165
166
167
168
169 protected void nullSafeClose(InputStream is) throws IOException {
170 if (is == null) {
171 return;
172 }
173 is.close();
174 }
175
176
177
178
179 protected Properties mergeLocalProperties() {
180 if (getLocalProperties() == null || getLocalProperties().length == 0) {
181
182 return new Properties();
183 }
184
185 Properties result = new Properties();
186 for (Properties localProp : getLocalProperties()) {
187 CollectionUtils.mergePropertiesIntoMap(localProp, result);
188 }
189 return result;
190 }
191
192
193
194
195 protected Properties loadEnvironmentProperties() {
196 if (isUseEnvironmentPropertyPrefix()) {
197 return getEnvironmentProperties(getEnvironmentPropertyPrefix());
198 } else {
199 return getEnvironmentProperties(null);
200 }
201 }
202
203
204
205
206 protected Properties loadSystemProperties() {
207 return SystemUtils.getSystemPropertiesIgnoreExceptions();
208 }
209
210
211
212
213
214 public Properties mergeProperties(Properties local, Properties resource, Properties sys, Properties env) {
215
216 Properties result = new Properties();
217
218
219 PropertiesMergeContext context = new PropertiesMergeContext(result, local, PropertySource.LOCAL);
220 mergeProperties(context);
221
222
223
224 boolean resourcePropertyWins = !isLocalOverride();
225 context = new PropertiesMergeContext(result, resource, resourcePropertyWins, PropertySource.RESOURCE);
226 mergeProperties(context);
227
228
229 if (!getSystemPropertiesMode().equals(SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_NEVER)) {
230 boolean override = getSystemPropertiesMode().equals(SystemPropertiesMode.SYSTEM_PROPERTIES_MODE_OVERRIDE);
231 context = new PropertiesMergeContext(result, sys, override, PropertySource.SYSTEM);
232 mergeProperties(context);
233 }
234
235
236 if (isSearchSystemEnvironment()) {
237 context = new PropertiesMergeContext(result, env, false, PropertySource.ENVIRONMENT);
238 mergeProperties(context);
239 }
240
241
242 return result;
243 }
244
245
246
247
248 public Properties loadProperties() {
249 try {
250
251 Properties local = mergeLocalProperties();
252 Properties resource = loadResourceProperties();
253 Properties sys = loadSystemProperties();
254 Properties env = loadEnvironmentProperties();
255
256
257 setMergedLocalProperties(local);
258 setResourceProperties(resource);
259 setSystemProperties(sys);
260 setEnvironmentProperties(env);
261
262
263 return mergeProperties(local, resource, sys, env);
264 } catch (IOException e) {
265 throw new PropertiesLoadException("Unexpected error loading properties", e);
266 }
267 }
268
269
270
271
272
273
274
275 public PropertyMergeResult mergeProperty(PropertiesMergeContext context, String key) {
276 Properties newProps = context.getNewProperties();
277 Properties currentProps = context.getCurrentProperties();
278 boolean override = context.isOverride();
279
280
281 String newValue = newProps.getProperty(key);
282
283
284 String currentValue = currentProps.getProperty(key);
285
286
287 if (newValue == null) {
288 PropertyMergeType type = PropertyMergeType.NULL_NEW_VALUE;
289 return new PropertyMergeResult(context, key, currentValue, newValue, type);
290 }
291
292
293 if (currentValue == null) {
294 currentProps.setProperty(key, newValue);
295 PropertyMergeType type = PropertyMergeType.ADD;
296 return new PropertyMergeResult(context, key, currentValue, newValue, type);
297 }
298
299
300 if (ObjectUtils.nullSafeEquals(newValue, currentValue)) {
301 PropertyMergeType type = PropertyMergeType.IDENTICAL_VALUES;
302 return new PropertyMergeResult(context, key, currentValue, newValue, type);
303 }
304
305 if (override) {
306
307 currentProps.setProperty(key, newValue);
308 PropertyMergeType type = PropertyMergeType.OVERRIDE;
309 return new PropertyMergeResult(context, key, currentValue, newValue, type);
310 } else {
311
312 PropertyMergeType type = PropertyMergeType.EXISTING_PROPERTY_WINS;
313 return new PropertyMergeResult(context, key, currentValue, newValue, type);
314 }
315 }
316
317 public void mergeProperties(PropertiesMergeContext context) {
318 List<String> keys = new ArrayList<String>(context.getNewProperties().stringPropertyNames());
319 if (context.isSort()) {
320 Collections.sort(keys);
321 }
322 for (String key : keys) {
323 logResult(mergeProperty(context, key));
324 }
325 }
326
327 protected void logResult(PropertyMergeResult result) {
328 PropertySource source = result.getContext().getSource();
329 String key = result.getKey();
330 String newValue = result.getNewValue();
331 String currentValue = result.getOldValue();
332
333 switch (result.getType()) {
334 case ADD:
335 logger.debug("Add " + source + " property {}=[{}]", key, plogger.getLogValue(key, newValue));
336 return;
337 case OVERRIDE:
338 logger.info(source + " property override for '" + key + "' [{}]->[{}]",
339 plogger.getLogValue(key, currentValue), plogger.getLogValue(key, newValue));
340 return;
341 case EXISTING_PROPERTY_WINS:
342 logger.debug("The existing value for '" + key + "' is not being overridden by the " + source
343 + " value. Existing:[{}] New:[{}]", plogger.getLogValue(key, currentValue),
344 plogger.getLogValue(key, newValue));
345 return;
346 default:
347 logger.trace("Merge property result: {} for {}", result.getType(), key);
348 return;
349
350 }
351
352 }
353
354 public Properties loadResourceProperties() throws IOException {
355 if (getLocations() == null || getLocations().length == 0) {
356 logger.info("No resource property locations to load from");
357 return new Properties();
358 }
359 Properties result = new Properties();
360 for (Resource location : getLocations()) {
361 Properties newProps = loadProperties(location);
362 PropertySource source = PropertySource.RESOURCE;
363
364 boolean override = true;
365 PropertiesMergeContext context = new PropertiesMergeContext(result, newProps, override, source);
366 mergeProperties(context);
367 }
368 return result;
369 }
370
371 public boolean isIgnoreResourceNotFound() {
372 return ignoreResourceNotFound;
373 }
374
375 public String getFileEncoding() {
376 return fileEncoding;
377 }
378
379 public Properties[] getLocalProperties() {
380 return localProperties;
381 }
382
383 public void setLocalProperties(Properties[] localProperties) {
384 this.localProperties = localProperties;
385 }
386
387 public Resource[] getLocations() {
388 return locations;
389 }
390
391 public boolean isLocalOverride() {
392 return localOverride;
393 }
394
395 public PropertiesPersister getPropertiesPersister() {
396 return propertiesPersister;
397 }
398
399 public String getEnvironmentPropertyPrefix() {
400 return environmentPropertyPrefix;
401 }
402
403 public void setEnvironmentPropertyPrefix(String environmentPropertyPrefix) {
404 this.environmentPropertyPrefix = environmentPropertyPrefix;
405 }
406
407 public Logger getLogger() {
408 return logger;
409 }
410
411 public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
412 this.propertiesPersister = propertiesPersister;
413 }
414
415 public void setLocations(Resource[] locations) {
416 this.locations = locations;
417 }
418
419 public void setLocalOverride(boolean localOverride) {
420 this.localOverride = localOverride;
421 }
422
423 public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
424 this.ignoreResourceNotFound = ignoreResourceNotFound;
425 }
426
427 public void setFileEncoding(String fileEncoding) {
428 this.fileEncoding = fileEncoding;
429 }
430
431 public SystemPropertiesMode getSystemPropertiesMode() {
432 return systemPropertiesMode;
433 }
434
435 public void setSystemPropertiesMode(SystemPropertiesMode systemPropertiesMode) {
436 this.systemPropertiesMode = systemPropertiesMode;
437 }
438
439 public boolean isSearchSystemEnvironment() {
440 return searchSystemEnvironment;
441 }
442
443 public void setSearchSystemEnvironment(boolean searchSystemEnvironment) {
444 this.searchSystemEnvironment = searchSystemEnvironment;
445 }
446
447 public void setResourceProperties(Properties resourceProperties) {
448 this.resourceProperties = resourceProperties;
449 }
450
451 public void setSystemProperties(Properties systemProperties) {
452 this.systemProperties = systemProperties;
453 }
454
455 public void setEnvironmentProperties(Properties environmentProperties) {
456 this.environmentProperties = environmentProperties;
457 }
458
459 public void setMergedLocalProperties(Properties mergedLocalProperties) {
460 this.mergedLocalProperties = mergedLocalProperties;
461 }
462
463 public boolean isUseEnvironmentPropertyPrefix() {
464 return useEnvironmentPropertyPrefix;
465 }
466
467 public void setUseEnvironmentPropertyPrefix(boolean useEnvironmentPropertyPrefix) {
468 this.useEnvironmentPropertyPrefix = useEnvironmentPropertyPrefix;
469 }
470
471 public PropertyLogger getPlogger() {
472 return plogger;
473 }
474
475 public void setPlogger(PropertyLogger propertiesLogger) {
476 this.plogger = propertiesLogger;
477 }
478
479 public Properties getProperties() {
480 return properties;
481 }
482
483 public void setProperties(Properties properties) {
484 this.properties = properties;
485 }
486
487 public Properties getSystemProperties() {
488 return systemProperties;
489 }
490
491 public Properties getEnvironmentProperties() {
492 return environmentProperties;
493 }
494
495 public Properties getResourceProperties() {
496 return resourceProperties;
497 }
498
499 public Properties getMergedLocalProperties() {
500 return mergedLocalProperties;
501 }
502
503 }