001/** 002 * Copyright 2005-2015 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.testtools.common; 017 018import java.io.FileInputStream; 019import java.io.FileNotFoundException; 020import java.io.IOException; 021import java.io.InputStream; 022import java.util.ArrayList; 023import java.util.Enumeration; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Map; 029import java.util.Properties; 030 031/** 032 * <p> 033 * Util Properties methods which have come from refactoring test generation using freemarker, providing overriding of 034 * file properties from System Properties as well as setting file properties as System Properties, as well as turning 035 * numbered properties to a List. 036 * </p> 037 * @author Kuali Rice Team (rice.collab@kuali.org) 038 */ 039public class PropertiesUtils { 040 041 /** 042 * <p> 043 * Plain load Properties from the given inputStream. 044 * </p> 045 * 046 * @param inputStream to read properties from 047 * @return Properties read from given Inputstream 048 * @throws IOException 049 */ 050 public Properties loadProperties(InputStream inputStream) throws IOException { 051 Properties props = new Properties(); 052 053 if(inputStream != null) { 054 props.load(inputStream); 055 } 056 057 return props; 058 } 059 060 /** 061 * <p> 062 * Read Properties from given Inputstream or Resource Loaction if the InputStream is null. 063 * </p><p> 064 * If a FileNotFoundException is thrown opening the InputStream an attempt will be made to read as a resource. 065 * </p> 066 * 067 * @param fileLocation null means use resourceLocation 068 * @param resourceLocation load resource as a stream {@code getClass().getClassLoader().getResourceAsStream(resourceLocation);} 069 * @return Properties read from given Inputstream or Resource Loaction if the InputStream is null 070 * @throws IOException 071 * @deprecated {@see #loadProperties(String)} 072 */ 073 @Deprecated 074 public Properties loadProperties(String fileLocation, String resourceLocation) throws IOException { 075 Properties props = null; 076 InputStream in = null; 077 if(fileLocation != null) { 078 try { 079 in = new FileInputStream(fileLocation); 080 } catch (FileNotFoundException fio) { 081 System.out.println(fio.getMessage() + " trying to read as resource."); 082 if (resourceLocation != null) { 083 System.out.println("Trying to read as " + resourceLocation+ " as a resource."); 084 in = getClass().getClassLoader().getResourceAsStream(resourceLocation); 085 } 086 } 087 } else { 088 in = getClass().getClassLoader().getResourceAsStream(resourceLocation); 089 if (in == null) { 090 System.out.println("Unable to read " + fileLocation + " as a file or " + resourceLocation + " as a resource stream"); 091 } 092 } 093 if(in != null) { 094 props = loadProperties(in); 095 in.close(); 096 } 097 098 return props; 099 } 100 101 /** 102 * <p> 103 * Read Properties from given Loaction 104 * </p><p> 105 * If a FileNotFoundException is thrown opening the InputStream an attempt will be made to read as a resource. 106 * </p> 107 * 108 * @param location 109 * @return Properties read from given Inputstream or Resource Loaction if the InputStream is null 110 * @throws IOException 111 */ 112 public Properties loadProperties(String location) throws IOException { 113 Properties props = null; 114 InputStream in = null; 115 try { 116 in = new FileInputStream(location); 117 } catch (FileNotFoundException fio) { 118 System.out.println(fio.getMessage() + " trying to read as resource."); 119 in = getClass().getClassLoader().getResourceAsStream(location); 120 if (in == null) { 121 System.out.println("Unable to read " + location + " as a resource stream"); 122 } 123 } 124 if(in != null) { 125 props = loadProperties(in); 126 in.close(); 127 } 128 129 return props; 130 } 131 132 public Properties loadPropertiesWithSystemAndOverrides(String location) throws IOException { 133 Properties properties = loadProperties(location); 134 return systemPropertiesAndOverride(properties); 135 } 136 137 /** 138 * <p> 139 * Beware of classloader/timing issues! Sometimes properties get added to the System properties after the point 140 * you might expect. Resulting in the System property not really being set for when you expected, such as with 141 * settign statics. Looks like WebDriverLegacyITBase has this going on with public.remote.url 142 * </p> 143 * 144 * @param location file or resource to load properties from, file is attempted first 145 * @return Properties 146 * @throws IOException 147 */ 148 public Properties loadPropertiesWithSystemAndOverridesIntoSystem(String location) throws IOException { 149 Properties properties = loadProperties(location); 150 properties = systemPropertiesAndOverride(properties); 151 152 Iterator propKeys = properties.keySet().iterator(); 153 while (propKeys.hasNext()) { 154 String key = (String)propKeys.next(); 155 if (System.getProperty(key) == null) { 156 System.setProperty(key, properties.getProperty(key)); 157 } 158 } 159 return properties; 160 } 161 162 public Properties loadPropertiesWithSystemOverrides(String location) throws IOException { 163 Properties properties = loadProperties(location); 164 return systemPropertiesOverride(properties); 165 } 166 167 /** 168 * <p> 169 * Read the properties from an inputStream overridding keys defined as JVM arguments. 170 * </p><p> 171 * {@see #systemPropertiesOverride} 172 * </p> 173 * 174 * @param inputStream to read properties from 175 * @return Properties loaded from inputStream and overridden with JVM arguments 176 * @throws IOException 177 */ 178 public Properties loadPropertiesWithSystemOverrides(InputStream inputStream) throws IOException { 179 Properties props = loadProperties(inputStream); 180 props = systemPropertiesOverride(props); 181 return props; 182 } 183 184 /** 185 * <p> 186 * Read the properties from an inputStream overridding keys defined as JVM arguments and transforming numbered keys 187 * into a list. 188 * </p><p> 189 * {@see #systemPropertiesOverride} 190 * {@see #transformNumberedPropertiesToList} 191 * </p> 192 * 193 * @param inputStream 194 * @return Properties loaded from inputStream, overridden with JVM arguments, and keys ending in numbers transformed to a List 195 * @throws IOException 196 */ 197 public Properties loadPropertiesWithSystemOverridesAndNumberedPropertiesToList(InputStream inputStream) throws IOException { 198 Properties props = loadProperties(inputStream); 199 props = systemPropertiesOverride(props); 200 props = transformNumberedPropertiesToList(props); 201 return props; 202 } 203 204 /** 205 * <p> 206 * Given a key that ends in a number, remove the number. 207 * </p> 208 * 209 * @param numberedKey in the form of some.key.number 210 * @return some.key part of some.key.number 211 */ 212 public String removeNumber(final String numberedKey) { 213 String unnumberedKey = numberedKey; 214 int firstNumberIndex = unnumberedKey.length() - 1; 215 while (Character.isDigit(unnumberedKey.charAt(firstNumberIndex))) { 216 firstNumberIndex--; 217 } 218 unnumberedKey = unnumberedKey.substring(0, firstNumberIndex + 1); 219 220 return unnumberedKey; 221 } 222 223 public Properties systemPropertiesAndOverride(Properties props) { 224 return systemPropertiesAndOverride(props, null); 225 } 226 227 /** 228 * <p> 229 * Override the given Properties with JVM argument {@code -Dkey=value}. 230 * </p> 231 * 232 * @param props properties to update with System.getProperty overrides. 233 */ 234 public Properties systemPropertiesOverride(Properties props) { 235 return systemPropertiesOverride(props, null); 236 } 237 238 /** 239 * <p> 240 * In addition to overriding file properties from System Properties, System properties are added to the returned 241 * Properties. 242 * </p><p> 243 * {@see #systemPropertiesOverride} 244 * </p> 245 * 246 * 247 * @param props Properties with System Properties added and Overriding file properties 248 * @param arg filter System Properties added to Properties by the Property key starting with arg 249 * @return 250 */ 251 public Properties systemPropertiesAndOverride(Properties props, String arg) { 252 PropertiesUtils propUtils = new PropertiesUtils(); 253 props = propUtils.systemPropertiesOverride(props, arg); 254 255 Iterator iter = System.getProperties().stringPropertyNames().iterator(); 256 while (iter.hasNext()) { 257 String key = (String) iter.next(); 258 if (arg == null || arg.equals("")) { 259 if (!props.containsValue(key)) { 260 props.setProperty(key, System.getProperty(key)); 261 } 262 } else { 263 if (key.startsWith(arg) && !props.containsValue(key)) { 264 props.setProperty(key, System.getProperty(key)); 265 } 266 } 267 } 268 return props; 269 } 270 271 /** 272 * <p> 273 * Override the given Properties with JVM argument {@code -Darg.key=value}. 274 * </p><p> 275 * -Dkey.propertyname= to override the property value for propertyname. 276 * </p> 277 * 278 * @param props properties to update with System.getProperty overrides. 279 * @param arg optional value that the property names will be appended to. 280 */ 281 public Properties systemPropertiesOverride(Properties props, String arg) { 282 Enumeration<?> names = props.propertyNames(); 283 Object nameObject; 284 String key; 285 while (names.hasMoreElements()) { 286 287 nameObject = names.nextElement(); 288 if (nameObject instanceof String) { 289 290 key = (String)nameObject; 291 if (arg == null || arg.isEmpty()) { 292 props.setProperty(key, System.getProperty(key, props.getProperty(key))); 293 } else { 294 props.setProperty(key, System.getProperty(arg + "." + key, props.getProperty(key))); 295 } 296 } 297 } 298 return props; 299 } 300 301 /** 302 * <p> 303 * Transform the given Properties keys which end in numbers to a List placed in a Map with the map key being the unumbered 304 * part of the Properties key with an s appended to it. 305 * </p> 306 * 307 * @param props Properties to have keys ending in 308 */ 309 public Properties transformNumberedPropertiesToList(Properties props) { 310 String key = null; 311 String unnumberedKey = null; 312 List<String> keyList = null; 313 List<String> removeKeys = new LinkedList<String>(); 314 315 // unnumber keys and place their values in a list 316 Iterator keys = props.keySet().iterator(); 317 Map<String, List<String>> keysLists = new HashMap<String, List<String>>(); 318 while (keys.hasNext()) { 319 key = (String)keys.next(); 320 if (Character.isDigit(key.charAt(key.length()-1))) { 321 unnumberedKey = removeNumber(key); 322 if (keysLists.get(unnumberedKey) == null) { 323 keyList = new ArrayList<String>(); 324 keyList.add(props.getProperty(key)); 325 keysLists.put(unnumberedKey, keyList); 326 removeKeys.add(key); 327 } else { 328 keyList = keysLists.get(unnumberedKey); 329 keyList.add(props.getProperty(key)); 330 keysLists.put(unnumberedKey, keyList); 331 removeKeys.add(key); 332 } 333 } 334 } 335 336 // remove keys that where unnumbered 337 Iterator removeKey = removeKeys.iterator(); 338 while (removeKey.hasNext()) { 339 key = (String)removeKey.next(); 340 props.remove(key); 341 } 342 343 // put new unnumbered key values mapped by unnumber key with an s appended to it. 344 Iterator newKeys = keysLists.keySet().iterator(); 345 String newKey = null; 346 while (newKeys.hasNext()) { 347 newKey = (String)newKeys.next(); 348 props.put(newKey + "s", keysLists.get(newKey)); 349 } 350 return props; 351 } 352}