001 /** 002 * Copyright 2005-2012 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 */ 016 package org.kuali.rice.krad.service.impl; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.apache.commons.logging.Log; 020 import org.apache.commons.logging.LogFactory; 021 import org.apache.log4j.Logger; 022 import org.kuali.rice.core.api.config.property.ConfigContext; 023 import org.kuali.rice.core.api.config.property.ConfigurationService; 024 import org.kuali.rice.core.api.util.Truth; 025 import org.kuali.rice.kns.web.struts.action.KualiPropertyMessageResources; 026 import org.kuali.rice.kns.web.struts.action.KualiPropertyMessageResourcesFactory; 027 import org.kuali.rice.krad.exception.DuplicateKeyException; 028 import org.kuali.rice.krad.exception.PropertiesException; 029 030 import java.io.IOException; 031 import java.io.InputStream; 032 import java.net.URL; 033 import java.util.Collections; 034 import java.util.Iterator; 035 import java.util.Map; 036 import java.util.Properties; 037 038 /** 039 * Implementation of the {@link ConfigurationService} that loads messages from the configured rice resource 040 * files and stores them in an internal property holder 041 * 042 * @author Kuali Rice Team (rice.collab@kuali.org) 043 */ 044 public class ConfigurationServiceImpl implements ConfigurationService { 045 private final PropertyHolder propertyHolder = new PropertyHolder(); 046 047 /** 048 * Default constructor 049 */ 050 public ConfigurationServiceImpl() { 051 this.propertyHolder.getHeldProperties().putAll(ConfigContext.getCurrentContextConfig().getProperties()); 052 053 KualiPropertyMessageResourcesFactory propertyMessageFactory = new KualiPropertyMessageResourcesFactory(); 054 055 // create default KualiPropertyMessageResources 056 KualiPropertyMessageResources messageResources = 057 (KualiPropertyMessageResources) propertyMessageFactory.createResources(""); 058 059 //Add Kuali Properties to property holder 060 this.propertyHolder.getHeldProperties().putAll(messageResources.getKualiProperties(null)); 061 } 062 063 /** 064 * @see org.kuali.rice.core.api.config.property.ConfigurationService#getPropertyValueAsString(java.lang.String) 065 */ 066 @Override 067 public String getPropertyValueAsString(String key) { 068 if (key == null) { 069 throw new IllegalArgumentException("invalid (null) key"); 070 } 071 072 return this.propertyHolder.getProperty(key); 073 } 074 075 /** 076 * @see org.kuali.rice.core.api.config.property.ConfigurationService#getPropertyValueAsBoolean(java.lang.String) 077 */ 078 @Override 079 public boolean getPropertyValueAsBoolean(String key) { 080 if (key == null) { 081 throw new IllegalArgumentException("invalid (null) key"); 082 } 083 084 String property = this.propertyHolder.getProperty(key); 085 Boolean b = Truth.strToBooleanIgnoreCase(property); 086 if (b == null) { 087 return false; 088 } 089 090 return b; 091 } 092 093 /** 094 * @see org.kuali.rice.core.api.config.property.ConfigurationService#getAllProperties() 095 */ 096 @Override 097 public Map<String, String> getAllProperties() { 098 return (Map) Collections.unmodifiableMap(propertyHolder.getHeldProperties()); 099 } 100 101 /** 102 * Interface for a source for properties 103 */ 104 protected static interface PropertySource { 105 106 /** 107 * @return Properties loaded from this PropertySource 108 * @throws org.kuali.rice.krad.exception.PropertiesException if there's a problem loading the properties 109 */ 110 public Properties loadProperties(); 111 } 112 113 /** 114 * This class is a Property container. It is able to load properties from various property-sources. 115 */ 116 protected static class PropertyHolder { 117 private static Logger LOG = Logger.getLogger(PropertyHolder.class); 118 119 Properties heldProperties; 120 121 /** 122 * Default constructor. 123 */ 124 public PropertyHolder() { 125 this.heldProperties = new Properties(); 126 } 127 128 /** 129 * @return true if this container currently has no properties 130 */ 131 public boolean isEmpty() { 132 return this.heldProperties.isEmpty(); 133 } 134 135 /** 136 * @param key 137 * @return true if a property with the given key exists in this container 138 * @throws IllegalArgumentException if the given key is null 139 */ 140 public boolean containsKey(String key) { 141 validateKey(key); 142 143 return this.heldProperties.containsKey(key); 144 } 145 146 /** 147 * @param key 148 * @return the current value of the property with the given key, or null if no property exists with that key 149 * @throws IllegalArgumentException if the given key is null 150 */ 151 public String getProperty(String key) { 152 validateKey(key); 153 154 return this.heldProperties.getProperty(key); 155 } 156 157 /** 158 * Associates the given value with the given key 159 * 160 * @param key 161 * @param value 162 * @throws IllegalArgumentException if the given key is null 163 * @throws IllegalArgumentException if the given value is null 164 * @throws org.kuali.rice.krad.exception.DuplicateKeyException if a property with the given key already exists 165 */ 166 public void setProperty(String key, String value) { 167 setProperty(null, key, value); 168 } 169 170 /** 171 * Associates the given value with the given key 172 * 173 * @param source 174 * @param key 175 * @param value 176 * @throws IllegalArgumentException if the given key is null 177 * @throws IllegalArgumentException if the given value is null 178 * @throws org.kuali.rice.krad.exception.DuplicateKeyException if a property with the given key already exists 179 */ 180 public void setProperty(PropertySource source, String key, String value) { 181 validateKey(key); 182 validateValue(value); 183 184 if (containsKey(key)) { 185 if (source != null && source instanceof FilePropertySource && ((FilePropertySource) source) 186 .isAllowOverrides()) { 187 LOG.info("Duplicate Key: Override is enabled [key=" 188 + key 189 + ", new value=" 190 + value 191 + ", old value=" 192 + this.heldProperties.getProperty(key) 193 + "]"); 194 } else { 195 throw new DuplicateKeyException("duplicate key '" + key + "'"); 196 } 197 } 198 this.heldProperties.setProperty(key, value); 199 } 200 201 /** 202 * Removes the property with the given key from this container 203 * 204 * @param key 205 * @throws IllegalArgumentException if the given key is null 206 */ 207 public void clearProperty(String key) { 208 validateKey(key); 209 210 this.heldProperties.remove(key); 211 } 212 213 /** 214 * Copies all name,value pairs from the given PropertySource instance into this container. 215 * 216 * @param source 217 * @throws IllegalStateException if the source is invalid (improperly initialized) 218 * @throws org.kuali.rice.krad.exception.DuplicateKeyException the first time a given property has the same key 219 * as an existing property 220 * @throws org.kuali.rice.krad.exception.PropertiesException if unable to load properties from the given source 221 */ 222 public void loadProperties(PropertySource source) { 223 if (source == null) { 224 throw new IllegalArgumentException("invalid (null) source"); 225 } 226 227 Properties newProperties = source.loadProperties(); 228 229 for (Iterator i = newProperties.keySet().iterator(); i.hasNext(); ) { 230 String key = (String) i.next(); 231 setProperty(source, key, newProperties.getProperty(key)); 232 } 233 } 234 235 /** 236 * Removes all properties from this container. 237 */ 238 public void clearProperties() { 239 this.heldProperties.clear(); 240 } 241 242 /** 243 * @return iterator over the keys of all properties in this container 244 */ 245 public Iterator getKeys() { 246 return this.heldProperties.keySet().iterator(); 247 } 248 249 /** 250 * @param key 251 * @throws IllegalArgumentException if the given key is null 252 */ 253 private void validateKey(String key) { 254 if (key == null) { 255 throw new IllegalArgumentException("invalid (null) key"); 256 } 257 } 258 259 /** 260 * @throws IllegalArgumentException if the given value is null 261 */ 262 private void validateValue(String value) { 263 if (value == null) { 264 throw new IllegalArgumentException("invalid (null) value"); 265 } 266 } 267 268 public Properties getHeldProperties() { 269 return heldProperties; 270 } 271 272 public void setHeldProperties(Properties heldProperties) { 273 this.heldProperties = heldProperties; 274 } 275 } 276 277 /** 278 * Used to obtain properties from a properties file 279 */ 280 protected static class FilePropertySource implements PropertySource { 281 private static Log log = LogFactory.getLog(FilePropertySource.class); 282 283 private String fileName; 284 private boolean allowOverrides; 285 286 public void setFileName(String fileName) { 287 this.fileName = fileName; 288 } 289 290 public String getFileName() { 291 return this.fileName; 292 } 293 294 public boolean isAllowOverrides() { 295 return this.allowOverrides; 296 } 297 298 public void setAllowOverrides(boolean allowOverrides) { 299 this.allowOverrides = allowOverrides; 300 } 301 302 /** 303 * Attempts to load properties from a properties file which has the current fileName and is located on the 304 * classpath 305 * 306 * @throws IllegalStateException if the fileName is null or empty 307 * @see org.kuali.rice.krad.service.impl.ConfigurationServiceImpl.PropertySource#loadProperties() 308 */ 309 public Properties loadProperties() { 310 if (StringUtils.isBlank(getFileName())) { 311 throw new IllegalStateException("invalid (blank) fileName"); 312 } 313 314 Properties properties = new Properties(); 315 316 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 317 URL url = loader.getResource(getFileName()); 318 if (url == null) { 319 throw new PropertiesException("unable to locate properties file '" + getFileName() + "'"); 320 } 321 322 InputStream in = null; 323 324 try { 325 in = url.openStream(); 326 properties.load(in); 327 } catch (IOException e) { 328 throw new PropertiesException("error loading from properties file '" + getFileName() + "'", e); 329 } finally { 330 if (in != null) { 331 try { 332 in.close(); 333 } catch (IOException e) { 334 log.error("caught exception closing InputStream: " + e); 335 } 336 337 } 338 } 339 340 return properties; 341 } 342 } 343 }