001 /**
002 * Copyright 2005-2013 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 }