001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util.properties; 017 018import java.util.List; 019import java.util.Properties; 020 021import org.kuali.common.util.Assert; 022import org.kuali.common.util.PropertyUtils; 023import org.kuali.common.util.cache.Cache; 024import org.kuali.common.util.cache.SimpleCache; 025import org.kuali.common.util.property.processor.NoOpProcessor; 026import org.kuali.common.util.property.processor.OverridingProcessor; 027import org.kuali.common.util.property.processor.PropertyProcessor; 028import org.kuali.common.util.resolver.PropertiesValueResolver; 029import org.kuali.common.util.resolver.ValueResolver; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033public class DefaultPropertiesService implements PropertiesService { 034 035 private static final Logger logger = LoggerFactory.getLogger(DefaultPropertiesService.class); 036 037 private static final Cache<String, Properties> CACHE = new SimpleCache<String, Properties>(); 038 private static final PropertyProcessor DEFAULT_POST_PROCESSOR = NoOpProcessor.INSTANCE; 039 040 private final Properties overrides; 041 private final PropertyProcessor postProcessor; 042 043 public DefaultPropertiesService(Properties overrides) { 044 this(overrides, DEFAULT_POST_PROCESSOR); 045 } 046 047 public DefaultPropertiesService(Properties overrides, PropertyProcessor postProcessor) { 048 Assert.noNulls(overrides, postProcessor); 049 this.overrides = PropertyUtils.toImmutable(overrides); 050 this.postProcessor = postProcessor; 051 } 052 053 protected PropertiesLoader getLoader(Location location, Cache<String, Properties> cache, Properties combined) { 054 return new CachingLoader(location, CACHE); 055 } 056 057 @Override 058 public Properties getProperties(List<Location> locations) { 059 060 // Allocate a new properties object 061 Properties properties = new Properties(); 062 063 // Cycle through our list of locations 064 for (Location location : locations) { 065 066 // Override anything we've loaded with properties from overrides 067 Properties combined = PropertyUtils.combine(properties, overrides); 068 069 // Use the combined properties to resolve values 070 ValueResolver resolver = new PropertiesValueResolver(combined); 071 072 // Resolve the location using the resolver 073 String resolvedLocation = resolver.resolve(location.getValue()); 074 075 // If the resolved location is different from the original location, create a new location object 076 Location actualLocation = getLocation(location, location.getValue(), resolvedLocation); 077 078 // Setup a loader capable of correctly handling things 079 // It might be perfectly acceptable for the location to not even exist 080 // The location might point to the default location for user specified overrides and the user hasn't provided any (for example) 081 // The loader is allowed to ignore missing locations, emit a log message about missing locations, or throw an exception 082 PropertiesLoader loader = getLoader(actualLocation, CACHE, combined); 083 084 // This may return an empty properties object depending on the configuration of the corresponding Location object 085 Properties loaded = loader.load(); 086 087 // Any freshly loaded properties "win" over previous properties 088 new OverridingProcessor(loaded).process(properties); 089 } 090 091 // Do any post processing as needed 092 postProcessor.process(properties); 093 094 // This is expensive, only do this in debug mode 095 if (logger.isDebugEnabled()) { 096 logger.debug("Displaying {} property values:\n\n{}", properties.size(), PropertyUtils.toString(properties)); 097 } 098 099 // 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}