View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.core.impl.services;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.apache.log4j.Logger;
22  import org.kuali.rice.core.api.config.property.ConfigContext;
23  import org.kuali.rice.core.api.config.property.ConfigurationService;
24  import org.kuali.rice.core.api.exception.RiceRuntimeException;
25  import org.kuali.rice.core.api.util.Truth;
26  import org.kuali.rice.krad.util.KRADConstants;
27  
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.URL;
31  import java.util.Collections;
32  import java.util.Iterator;
33  import java.util.Map;
34  import java.util.Properties;
35  import java.util.ResourceBundle;
36  
37  /**
38   * Implementation of the {@link org.kuali.rice.core.api.config.property.ConfigurationService} that loads
39   * messages from the configured rice config files and stores them in an internal property holder
40   *
41   * @author Kuali Rice Team (rice.collab@kuali.org)
42   */
43  public class ConfigurationServiceImpl implements ConfigurationService {
44      private final PropertyHolder propertyHolder = new PropertyHolder();
45  
46      /**
47       * Default constructor
48       */
49      public ConfigurationServiceImpl() {
50          this.propertyHolder.getHeldProperties().putAll(ConfigContext.getCurrentContextConfig().getProperties());
51  
52          // TODO: remove loading of property files once KNS legacy code is removed
53          String propertyConfig = (String) ConfigContext.getCurrentContextConfig().getProperties().get(
54                  KRADConstants.MESSAGE_RESOURCES);
55          propertyConfig = removeSpacesAround(propertyConfig);
56  
57          String[] bundleNames = StringUtils.split(propertyConfig, ",");
58          for (String bundleName : bundleNames) {
59              ResourceBundle bundle = ResourceBundle.getBundle(bundleName);
60  
61              for (String key : bundle.keySet()) {
62                  String message = bundle.getString(key);
63                  this.propertyHolder.getHeldProperties().put(key, message);
64              }
65          }
66      }
67  
68      /**
69       * @see org.kuali.rice.core.api.config.property.ConfigurationService#getPropertyValueAsString(String)
70       */
71      @Override
72      public String getPropertyValueAsString(String key) {
73          if (key == null) {
74              throw new IllegalArgumentException("invalid (null) key");
75          }
76  
77          return this.propertyHolder.getProperty(key);
78      }
79  
80      /**
81       * @see org.kuali.rice.core.api.config.property.ConfigurationService#getPropertyValueAsBoolean(String)
82       */
83      @Override
84      public boolean getPropertyValueAsBoolean(String key, boolean defaultValue) {
85          if (key == null) {
86              throw new IllegalArgumentException("invalid (null) key");
87          }
88  
89          String property = this.propertyHolder.getProperty(key);
90          return Truth.strToBooleanIgnoreCase(property, defaultValue);
91      }
92  
93      @Override
94      public boolean getPropertyValueAsBoolean(String key) {
95          return getPropertyValueAsBoolean(key, false);
96      }
97  
98      /**
99       * @see org.kuali.rice.core.api.config.property.ConfigurationService#getAllProperties()
100      */
101     @Override
102     public Map<String, String> getAllProperties() {
103         return (Map) Collections.unmodifiableMap(propertyHolder.getHeldProperties());
104     }
105 
106     /**
107      * Removes the spaces around the elements on a csv list of elements
108      *
109      * <p>
110      * A null input will return a null output.
111      * </p>
112      *
113      * @param csv a list of elements in csv format e.g. foo, bar, baz
114      * @return a list of elements in csv format without spaces e.g. foo,bar,baz
115      */
116     private String removeSpacesAround(String csv) {
117         if (csv == null) {
118             return null;
119         }
120 
121         final StringBuilder result = new StringBuilder();
122         for (final String value : csv.split(",")) {
123             if (!"".equals(value.trim())) {
124                 result.append(value.trim());
125                 result.append(",");
126             }
127         }
128 
129         //remove trailing comma
130         int i = result.lastIndexOf(",");
131         if (i != -1) {
132             result.deleteCharAt(i);
133         }
134 
135         return result.toString();
136     }
137 
138     /**
139      * Interface for a source for properties
140      */
141     protected static interface PropertySource {
142 
143         /**
144          * @return Properties loaded from this PropertySource
145          */
146         public Properties loadProperties();
147     }
148 
149     /**
150      * This class is a Property container. It is able to load properties from various property-sources.
151      */
152     protected static class PropertyHolder {
153         private static Logger LOG = Logger.getLogger(PropertyHolder.class);
154 
155         Properties heldProperties;
156 
157         /**
158          * Default constructor.
159          */
160         public PropertyHolder() {
161             this.heldProperties = new Properties();
162         }
163 
164         /**
165          * @return true if this container currently has no properties
166          */
167         public boolean isEmpty() {
168             return this.heldProperties.isEmpty();
169         }
170 
171         /**
172          * @param key
173          * @return true if a property with the given key exists in this container
174          * @throws IllegalArgumentException if the given key is null
175          */
176         public boolean containsKey(String key) {
177             validateKey(key);
178 
179             return this.heldProperties.containsKey(key);
180         }
181 
182         /**
183          * @param key
184          * @return the current value of the property with the given key, or null if no property exists with that key
185          * @throws IllegalArgumentException if the given key is null
186          */
187         public String getProperty(String key) {
188             validateKey(key);
189 
190             return this.heldProperties.getProperty(key);
191         }
192 
193         /**
194          * Associates the given value with the given key
195          *
196          * @param key
197          * @param value
198          * @throws IllegalArgumentException if the given key is null
199          * @throws IllegalArgumentException if the given value is null
200          */
201         public void setProperty(String key, String value) {
202             setProperty(null, key, value);
203         }
204 
205         /**
206          * Associates the given value with the given key
207          *
208          * @param source
209          * @param key
210          * @param value
211          * @throws IllegalArgumentException if the given key is null
212          * @throws IllegalArgumentException if the given value is null
213          */
214         public void setProperty(PropertySource source, String key, String value) {
215             validateKey(key);
216             validateValue(value);
217 
218             if (containsKey(key)) {
219                 if (source != null && source instanceof FilePropertySource && ((FilePropertySource) source)
220                         .isAllowOverrides()) {
221                     LOG.info("Duplicate Key: Override is enabled [key="
222                             + key
223                             + ", new value="
224                             + value
225                             + ", old value="
226                             + this.heldProperties.getProperty(key)
227                             + "]");
228                 } else {
229                     throw new RiceRuntimeException("duplicate key '" + key + "'");
230                 }
231             }
232             this.heldProperties.setProperty(key, value);
233         }
234 
235         /**
236          * Removes the property with the given key from this container
237          *
238          * @param key
239          * @throws IllegalArgumentException if the given key is null
240          */
241         public void clearProperty(String key) {
242             validateKey(key);
243 
244             this.heldProperties.remove(key);
245         }
246 
247         /**
248          * Copies all name,value pairs from the given PropertySource instance into this container.
249          *
250          * @param source
251          * @throws IllegalStateException if the source is invalid (improperly initialized)
252          * as an existing property
253          */
254         public void loadProperties(PropertySource source) {
255             if (source == null) {
256                 throw new IllegalArgumentException("invalid (null) source");
257             }
258 
259             Properties newProperties = source.loadProperties();
260 
261             for (Iterator i = newProperties.keySet().iterator(); i.hasNext(); ) {
262                 String key = (String) i.next();
263                 setProperty(source, key, newProperties.getProperty(key));
264             }
265         }
266 
267         /**
268          * Removes all properties from this container.
269          */
270         public void clearProperties() {
271             this.heldProperties.clear();
272         }
273 
274         /**
275          * @return iterator over the keys of all properties in this container
276          */
277         public Iterator getKeys() {
278             return this.heldProperties.keySet().iterator();
279         }
280 
281         /**
282          * @param key
283          * @throws IllegalArgumentException if the given key is null
284          */
285         private void validateKey(String key) {
286             if (key == null) {
287                 throw new IllegalArgumentException("invalid (null) key");
288             }
289         }
290 
291         /**
292          * @throws IllegalArgumentException if the given value is null
293          */
294         private void validateValue(String value) {
295             if (value == null) {
296                 throw new IllegalArgumentException("invalid (null) value");
297             }
298         }
299 
300         public Properties getHeldProperties() {
301             return heldProperties;
302         }
303 
304         public void setHeldProperties(Properties heldProperties) {
305             this.heldProperties = heldProperties;
306         }
307     }
308 
309     /**
310      * Used to obtain properties from a properties file
311      */
312     protected static class FilePropertySource implements PropertySource {
313         private static Log log = LogFactory.getLog(FilePropertySource.class);
314 
315         private String fileName;
316         private boolean allowOverrides;
317 
318         public void setFileName(String fileName) {
319             this.fileName = fileName;
320         }
321 
322         public String getFileName() {
323             return this.fileName;
324         }
325 
326         public boolean isAllowOverrides() {
327             return this.allowOverrides;
328         }
329 
330         public void setAllowOverrides(boolean allowOverrides) {
331             this.allowOverrides = allowOverrides;
332         }
333 
334         /**
335          * Attempts to load properties from a properties file which has the current fileName and is located on the
336          * classpath
337          *
338          * @throws IllegalStateException if the fileName is null or empty
339          * @see ConfigurationServiceImpl.PropertySource#loadProperties()
340          */
341         public Properties loadProperties() {
342             if (StringUtils.isBlank(getFileName())) {
343                 throw new IllegalStateException("invalid (blank) fileName");
344             }
345 
346             Properties properties = new Properties();
347 
348             ClassLoader loader = Thread.currentThread().getContextClassLoader();
349             URL url = loader.getResource(getFileName());
350             if (url == null) {
351                 throw new RiceRuntimeException("unable to locate properties file '" + getFileName() + "'");
352             }
353 
354             InputStream in = null;
355 
356             try {
357                 in = url.openStream();
358                 properties.load(in);
359             } catch (IOException e) {
360                 throw new RiceRuntimeException("error loading from properties file '" + getFileName() + "'", e);
361             } finally {
362                 if (in != null) {
363                     try {
364                         in.close();
365                     } catch (IOException e) {
366                         log.error("caught exception closing InputStream: " + e);
367                     }
368 
369                 }
370             }
371 
372             return properties;
373         }
374     }
375 }