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