001/** 002 * Copyright 2005-2017 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 public Properties loadProperties(String fileLocation, String resourceLocation) throws IOException { 074 Properties props = null; 075 InputStream in = null; 076 if(fileLocation != null) { 077 try { 078 in = new FileInputStream(fileLocation); 079 } catch (FileNotFoundException fio) { 080 System.out.println(fio.getMessage() + " trying to read as resource."); 081 if (resourceLocation != null) { 082 System.out.println("Trying to read as " + resourceLocation+ " as a resource."); 083 in = getClass().getClassLoader().getResourceAsStream(resourceLocation); 084 } 085 } 086 } else { 087 in = getClass().getClassLoader().getResourceAsStream(resourceLocation); 088 if (in == null) { 089 System.out.println("Unable to read " + fileLocation + " as a file or " + resourceLocation + " as a resource stream"); 090 } 091 } 092 if(in != null) { 093 props = loadProperties(in); 094 in.close(); 095 } 096 097 return props; 098 } 099 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}