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 }