001 package org.kuali.common.util.properties;
002
003 import java.util.List;
004 import java.util.Properties;
005
006 import org.kuali.common.util.Assert;
007 import org.kuali.common.util.PropertyUtils;
008 import org.kuali.common.util.cache.Cache;
009 import org.kuali.common.util.cache.SimpleCache;
010 import org.kuali.common.util.property.processor.NoOpProcessor;
011 import org.kuali.common.util.property.processor.OverridingProcessor;
012 import org.kuali.common.util.property.processor.PropertyProcessor;
013 import org.kuali.common.util.resolver.PropertiesValueResolver;
014 import org.kuali.common.util.resolver.ValueResolver;
015 import org.slf4j.Logger;
016 import org.slf4j.LoggerFactory;
017
018 public final class DefaultPropertiesService implements PropertiesService {
019
020 private static final Logger logger = LoggerFactory.getLogger(DefaultPropertiesService.class);
021
022 private static final Cache<String, Properties> CACHE = new SimpleCache<String, Properties>();
023 private static final PropertyProcessor DEFAULT_POST_PROCESSOR = NoOpProcessor.INSTANCE;
024
025 private final Properties overrides;
026 private final PropertyProcessor postProcessor;
027
028 public DefaultPropertiesService(Properties overrides) {
029 this(overrides, DEFAULT_POST_PROCESSOR);
030 }
031
032 public DefaultPropertiesService(Properties overrides, PropertyProcessor postProcessor) {
033 Assert.noNulls(overrides, postProcessor);
034 this.overrides = PropertyUtils.toImmutable(overrides);
035 this.postProcessor = postProcessor;
036 }
037
038 @Override
039 public Properties getProperties(List<Location> locations) {
040
041 // Allocate a new properties object
042 Properties properties = new Properties();
043
044 // Cycle through our list of locations
045 for (Location location : locations) {
046
047 // Override anything we've loaded with properties from overrides
048 Properties combined = PropertyUtils.combine(properties, overrides);
049
050 // Use the combined properties to resolve values
051 ValueResolver resolver = new PropertiesValueResolver(combined);
052
053 // Resolve the location using the resolver
054 String resolvedLocation = resolver.resolve(location.getValue());
055
056 // Setup a loader capable of correctly handling things
057 // It might be perfectly acceptable for the location to not even exist
058 // The location might point to the default location for user specified overrides and the user hasn't provided any (for example)
059 // The loader is allowed to ignore missing locations, emit a log message about missing locations, or throw an exception
060 PropertiesLoader loader = new CachingLoader(location, resolvedLocation, CACHE);
061
062 // This may return an empty properties object depending on the configuration of the corresponding Location object
063 Properties loaded = loader.load();
064
065 // Any freshly loaded properties "win" over previous properties
066 new OverridingProcessor(loaded).process(properties);
067 }
068
069 // Do any post processing as needed
070 postProcessor.process(properties);
071
072 // This is expensive, only do this in debug mode
073 if (logger.isDebugEnabled()) {
074 logger.debug("Displaying {} property values:\n\n{}", properties.size(), PropertyUtils.toString(properties));
075 }
076
077 // Return what we've found
078 return properties;
079 }
080
081 public void clearCache() {
082 CACHE.clear();
083 }
084
085 public Properties getOverrides() {
086 return overrides;
087 }
088
089 public PropertyProcessor getPostProcessor() {
090 return postProcessor;
091 }
092
093 }