View Javadoc
1   /**
2    * Copyright 2005-2014 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.testtools.common;
17  
18  import java.io.FileInputStream;
19  import java.io.FileNotFoundException;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.util.ArrayList;
23  import java.util.Enumeration;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Properties;
30  
31  /**
32   * <p>
33   * Util Properties methods which have come from refactoring test generation using freemarker, providing overriding of
34   * file properties from System Properties as well as setting file properties as System Properties, as well as turning
35   * numbered properties to a List.
36   * </p>
37   * @author Kuali Rice Team (rice.collab@kuali.org)
38   */
39  public class PropertiesUtils {
40  
41      /**
42       * <p>
43       * Plain load Properties from the given inputStream.
44       * </p>
45       *
46       * @param inputStream to read properties from
47       * @return Properties read from given Inputstream
48       * @throws IOException
49       */
50      public Properties loadProperties(InputStream inputStream) throws IOException {
51          Properties props = new Properties();
52  
53          if(inputStream != null) {
54              props.load(inputStream);
55          }
56  
57          return props;
58      }
59  
60      /**
61       * <p>
62       * Read Properties from given Inputstream or Resource Loaction if the InputStream is null.
63       * </p><p>
64       * If a FileNotFoundException is thrown opening the InputStream an attempt will be made to read as a resource.
65       * </p>
66       *
67       * @param fileLocation null means use resourceLocation
68       * @param resourceLocation load resource as a stream {@code getClass().getClassLoader().getResourceAsStream(resourceLocation);}
69       * @return Properties read from given Inputstream or Resource Loaction if the InputStream is null
70       * @throws IOException
71       * @deprecated {@see #loadProperties(String)}
72       */
73      public Properties loadProperties(String fileLocation, String resourceLocation) throws IOException {
74          Properties props = null;
75          InputStream in = null;
76          if(fileLocation != null) {
77              try {
78                  in = new FileInputStream(fileLocation);
79              } catch (FileNotFoundException fio) {
80                  System.out.println(fio.getMessage() + " trying to read as resource.");
81                  if (resourceLocation != null) {
82                      System.out.println("Trying to read as " + resourceLocation+ " as a resource.");
83                      in = getClass().getClassLoader().getResourceAsStream(resourceLocation);
84                  }
85              }
86          } else {
87              in = getClass().getClassLoader().getResourceAsStream(resourceLocation);
88              if (in == null) {
89                  System.out.println("Unable to read " + fileLocation + " as a file or " + resourceLocation + " as a resource stream");
90              }
91          }
92          if(in != null) {
93              props = loadProperties(in);
94              in.close();
95          }
96  
97          return props;
98      }
99  
100     /**
101      * <p>
102      * Read Properties from given Loaction
103      * </p><p>
104      * If a FileNotFoundException is thrown opening the InputStream an attempt will be made to read as a resource.
105      * </p>
106      *
107      * @param location
108      * @return Properties read from given Inputstream or Resource Loaction if the InputStream is null
109      * @throws IOException
110      */
111     public Properties loadProperties(String location) throws IOException {
112         Properties props = null;
113         InputStream in = null;
114         try {
115             in = new FileInputStream(location);
116         } catch (FileNotFoundException fio) {
117             System.out.println(fio.getMessage() + " trying to read as resource.");
118             in = getClass().getClassLoader().getResourceAsStream(location);
119             if (in == null) {
120                 System.out.println("Unable to read " + location + " as a resource stream");
121             }
122         }
123         if(in != null) {
124             props = loadProperties(in);
125             in.close();
126         }
127 
128         return props;
129     }
130 
131     public Properties loadPropertiesWithSystemAndOverrides(String location) throws IOException {
132         Properties properties =  loadProperties(location);
133         return systemPropertiesAndOverride(properties);
134     }
135 
136     /**
137      * <p>
138      * Beware of classloader/timing issues!  Sometimes properties get added to the System properties after the point
139      * you might expect.  Resulting in the System property not really being set for when you expected, such as with
140      * settign statics.  Looks like WebDriverLegacyITBase has this going on with public.remote.url
141      * </p>
142      *
143      * @param location file or resource to load properties from, file is attempted first
144      * @return Properties
145      * @throws IOException
146      */
147     public Properties loadPropertiesWithSystemAndOverridesIntoSystem(String location) throws IOException {
148         Properties properties = loadProperties(location);
149         properties = systemPropertiesAndOverride(properties);
150 
151         Iterator propKeys = properties.keySet().iterator();
152         while (propKeys.hasNext()) {
153             String key = (String)propKeys.next();
154             if (System.getProperty(key) == null) {
155                 System.setProperty(key, properties.getProperty(key));
156             }
157         }
158         return properties;
159     }
160 
161     public Properties loadPropertiesWithSystemOverrides(String location) throws IOException {
162         Properties properties =  loadProperties(location);
163         return systemPropertiesOverride(properties);
164     }
165 
166     /**
167      * <p>
168      * Read the properties from an inputStream overridding keys defined as JVM arguments.
169      * </p><p>
170      * {@see #systemPropertiesOverride}
171      * </p>
172      *
173      * @param inputStream to read properties from
174      * @return Properties loaded from inputStream and overridden with JVM arguments
175      * @throws IOException
176      */
177     public  Properties loadPropertiesWithSystemOverrides(InputStream inputStream) throws IOException {
178         Properties props = loadProperties(inputStream);
179         props = systemPropertiesOverride(props);
180         return props;
181     }
182 
183     /**
184      * <p>
185      * Read the properties from an inputStream overridding keys defined as JVM arguments and transforming numbered keys
186      * into a list.
187      * </p><p>
188      * {@see #systemPropertiesOverride}
189      * {@see #transformNumberedPropertiesToList}
190      * </p>
191      *
192      * @param inputStream
193      * @return Properties loaded from inputStream, overridden with JVM arguments, and keys ending in numbers transformed to a List
194      * @throws IOException
195      */
196     public Properties loadPropertiesWithSystemOverridesAndNumberedPropertiesToList(InputStream inputStream) throws IOException {
197         Properties props = loadProperties(inputStream);
198         props = systemPropertiesOverride(props);
199         props = transformNumberedPropertiesToList(props);
200         return props;
201     }
202 
203     /**
204      * <p>
205      * Given a key that ends in a number, remove the number.
206      * </p>
207      *
208      * @param numberedKey in the form of some.key.number
209      * @return some.key part of some.key.number
210      */
211     public String removeNumber(final String numberedKey) {
212         String unnumberedKey = numberedKey;
213         int firstNumberIndex = unnumberedKey.length() - 1;
214         while (Character.isDigit(unnumberedKey.charAt(firstNumberIndex))) {
215             firstNumberIndex--;
216         }
217         unnumberedKey = unnumberedKey.substring(0, firstNumberIndex + 1);
218 
219         return unnumberedKey;
220     }
221 
222     public Properties systemPropertiesAndOverride(Properties props) {
223         return systemPropertiesAndOverride(props, null);
224     }
225 
226     /**
227      * <p>
228      * Override the given Properties with JVM argument {@code -Dkey=value}.
229      * </p>
230      *
231      * @param props properties to update with System.getProperty overrides.
232      */
233     public Properties systemPropertiesOverride(Properties props) {
234         return systemPropertiesOverride(props, null);
235     }
236 
237     /**
238      * <p>
239      * In addition to overriding file properties from System Properties, System properties are added to the returned
240      * Properties.
241      * </p><p>
242      * {@see #systemPropertiesOverride}
243      * </p>
244      *
245      *
246      * @param props Properties with System Properties added and Overriding file properties
247      * @param arg filter System Properties added to Properties by the Property key starting with arg
248      * @return
249      */
250     public Properties systemPropertiesAndOverride(Properties props, String arg) {
251         PropertiesUtils propUtils = new PropertiesUtils();
252         props = propUtils.systemPropertiesOverride(props, arg);
253 
254         Iterator iter = System.getProperties().stringPropertyNames().iterator();
255         while (iter.hasNext()) {
256             String key = (String) iter.next();
257             if (arg == null || arg.equals("")) {
258                 if (!props.contains(key)) {
259                     props.setProperty(key, System.getProperty(key));
260                 }
261             } else {
262                 if (key.startsWith(arg) && !props.contains(key)) {
263                     props.setProperty(key, System.getProperty(key));
264                 }
265             }
266         }
267         return props;
268     }
269 
270     /**
271      * <p>
272      * Override the given Properties with JVM argument {@code -Darg.key=value}.
273      * </p><p>
274      * -Dkey.propertyname= to override the property value for propertyname.
275      * </p>
276      *
277      * @param props properties to update with System.getProperty overrides.
278      * @param arg optional value that the property names will be appended to.
279      */
280     public Properties systemPropertiesOverride(Properties props, String arg) {
281         Enumeration<?> names = props.propertyNames();
282         Object nameObject;
283         String key;
284         while (names.hasMoreElements()) {
285 
286             nameObject = names.nextElement();
287             if (nameObject instanceof String) {
288 
289                 key = (String)nameObject;
290                 if (arg == null || arg.isEmpty()) {
291                     props.setProperty(key, System.getProperty(key, props.getProperty(key)));
292                 } else {
293                     props.setProperty(key, System.getProperty(arg + "." + key, props.getProperty(key)));
294                 }
295             }
296         }
297         return props;
298     }
299 
300     /**
301      * <p>
302      * Transform the given Properties keys which end in numbers to a List placed in a Map with the map key being the unumbered
303      * part of the Properties key with an s appended to it.
304      * </p>
305      *
306      * @param props Properties to have keys ending in
307      */
308     public Properties transformNumberedPropertiesToList(Properties props) {
309         String key = null;
310         String unnumberedKey = null;
311         List<String> keyList = null;
312         List<String> removeKeys = new LinkedList<String>();
313 
314         // unnumber keys and place their values in a list
315         Iterator keys = props.keySet().iterator();
316         Map<String, List<String>> keysLists = new HashMap<String, List<String>>();
317         while (keys.hasNext()) {
318             key = (String)keys.next();
319             if (Character.isDigit(key.charAt(key.length()-1))) {
320                 unnumberedKey = removeNumber(key);
321                 if (keysLists.get(unnumberedKey) == null) {
322                     keyList = new ArrayList<String>();
323                     keyList.add(props.getProperty(key));
324                     keysLists.put(unnumberedKey, keyList);
325                     removeKeys.add(key);
326                 } else {
327                     keyList = keysLists.get(unnumberedKey);
328                     keyList.add(props.getProperty(key));
329                     keysLists.put(unnumberedKey, keyList);
330                     removeKeys.add(key);
331                 }
332             }
333         }
334 
335         // remove keys that where unnumbered
336         Iterator removeKey = removeKeys.iterator();
337         while (removeKey.hasNext()) {
338             key = (String)removeKey.next();
339             props.remove(key);
340         }
341 
342         // put new unnumbered key values mapped by unnumber key with an s appended to it.
343         Iterator newKeys = keysLists.keySet().iterator();
344         String newKey = null;
345         while (newKeys.hasNext()) {
346             newKey = (String)newKeys.next();
347             props.put(newKey + "s", keysLists.get(newKey));
348         }
349         return props;
350     }
351 }