View Javadoc
1   /**
2    * Copyright 2010-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util.properties;
17  
18  import java.util.List;
19  import java.util.Properties;
20  
21  import org.kuali.common.util.Assert;
22  import org.kuali.common.util.PropertyUtils;
23  import org.kuali.common.util.cache.Cache;
24  import org.kuali.common.util.cache.SimpleCache;
25  import org.kuali.common.util.property.processor.NoOpProcessor;
26  import org.kuali.common.util.property.processor.OverridingProcessor;
27  import org.kuali.common.util.property.processor.PropertyProcessor;
28  import org.kuali.common.util.resolver.PropertiesValueResolver;
29  import org.kuali.common.util.resolver.ValueResolver;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  public class DefaultPropertiesService implements PropertiesService {
34  
35  	private static final Logger logger = LoggerFactory.getLogger(DefaultPropertiesService.class);
36  
37  	private static final Cache<String, Properties> CACHE = new SimpleCache<String, Properties>();
38  	private static final PropertyProcessor DEFAULT_POST_PROCESSOR = NoOpProcessor.INSTANCE;
39  
40  	private final Properties overrides;
41  	private final PropertyProcessor postProcessor;
42  
43  	public DefaultPropertiesService(Properties overrides) {
44  		this(overrides, DEFAULT_POST_PROCESSOR);
45  	}
46  
47  	public DefaultPropertiesService(Properties overrides, PropertyProcessor postProcessor) {
48  		Assert.noNulls(overrides, postProcessor);
49  		this.overrides = PropertyUtils.toImmutable(overrides);
50  		this.postProcessor = postProcessor;
51  	}
52  
53  	protected PropertiesLoader getLoader(Location location, Cache<String, Properties> cache, Properties combined) {
54  		return new CachingLoader(location, CACHE);
55  	}
56  
57  	@Override
58  	public Properties getProperties(List<Location> locations) {
59  
60  		// Allocate a new properties object
61  		Properties properties = new Properties();
62  
63  		// Cycle through our list of locations
64  		for (Location location : locations) {
65  
66  			// Override anything we've loaded with properties from overrides
67  			Properties combined = PropertyUtils.combine(properties, overrides);
68  
69  			// Use the combined properties to resolve values
70  			ValueResolver resolver = new PropertiesValueResolver(combined);
71  
72  			// Resolve the location using the resolver
73  			String resolvedLocation = resolver.resolve(location.getValue());
74  
75  			// If the resolved location is different from the original location, create a new location object
76  			Location actualLocation = getLocation(location, location.getValue(), resolvedLocation);
77  
78  			// Setup a loader capable of correctly handling things
79  			// It might be perfectly acceptable for the location to not even exist
80  			// The location might point to the default location for user specified overrides and the user hasn't provided any (for example)
81  			// The loader is allowed to ignore missing locations, emit a log message about missing locations, or throw an exception
82  			PropertiesLoader loader = getLoader(actualLocation, CACHE, combined);
83  
84  			// This may return an empty properties object depending on the configuration of the corresponding Location object
85  			Properties loaded = loader.load();
86  
87  			// Any freshly loaded properties "win" over previous properties
88  			new OverridingProcessor(loaded).process(properties);
89  		}
90  
91  		// Do any post processing as needed
92  		postProcessor.process(properties);
93  
94  		// This is expensive, only do this in debug mode
95  		if (logger.isDebugEnabled()) {
96  			logger.debug("Displaying {} property values:\n\n{}", properties.size(), PropertyUtils.toString(properties));
97  		}
98  
99  		// Return what we've found
100 		return properties;
101 	}
102 
103 	private Location getLocation(Location location, String originalLocation, String resolvedLocation) {
104 		if (originalLocation.equals(resolvedLocation)) {
105 			return location;
106 		} else {
107 			return Location.builder(location, resolvedLocation).build();
108 		}
109 	}
110 
111 	public void clearCache() {
112 		CACHE.clear();
113 	}
114 
115 	public Properties getOverrides() {
116 		return overrides;
117 	}
118 
119 	public PropertyProcessor getPostProcessor() {
120 		return postProcessor;
121 	}
122 
123 }