001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j.helpers; 019 020import java.io.InputStream; 021import java.io.InterruptedIOException; 022import java.net.URL; 023import java.util.Properties; 024 025import org.apache.log4j.Level; 026import org.apache.log4j.PropertyConfigurator; 027import org.apache.log4j.spi.Configurator; 028import org.apache.log4j.spi.LoggerRepository; 029 030// Contributors: Avy Sharell (sharell@online.fr) 031// Matthieu Verbert (mve@zurich.ibm.com) 032// Colin Sampaleanu 033 034/** 035 A convenience class to convert property values to specific types. 036 037 @author Ceki Gülcü 038 @author Simon Kitching; 039 @author Anders Kristensen 040*/ 041public class OptionConverter { 042 043 static String DELIM_START = "${"; 044 static char DELIM_STOP = '}'; 045 static int DELIM_START_LEN = 2; 046 static int DELIM_STOP_LEN = 1; 047 048 /** OptionConverter is a static class. */ 049 private OptionConverter() {} 050 051 public 052 static 053 String[] concatanateArrays(String[] l, String[] r) { 054 int len = l.length + r.length; 055 String[] a = new String[len]; 056 057 System.arraycopy(l, 0, a, 0, l.length); 058 System.arraycopy(r, 0, a, l.length, r.length); 059 060 return a; 061 } 062 063 public 064 static 065 String convertSpecialChars(String s) { 066 char c; 067 int len = s.length(); 068 StringBuffer sbuf = new StringBuffer(len); 069 070 int i = 0; 071 while(i < len) { 072 c = s.charAt(i++); 073 if (c == '\\') { 074 c = s.charAt(i++); 075 if(c == 'n') c = '\n'; 076 else if(c == 'r') c = '\r'; 077 else if(c == 't') c = '\t'; 078 else if(c == 'f') c = '\f'; 079 else if(c == '\b') c = '\b'; 080 else if(c == '\"') c = '\"'; 081 else if(c == '\'') c = '\''; 082 else if(c == '\\') c = '\\'; 083 } 084 sbuf.append(c); 085 } 086 return sbuf.toString(); 087 } 088 089 090 /** 091 Very similar to <code>System.getProperty</code> except 092 that the {@link SecurityException} is hidden. 093 094 @param key The key to search for. 095 @param def The default value to return. 096 @return the string value of the system property, or the default 097 value if there is no property with that key. 098 099 @since 1.1 */ 100 public 101 static 102 String getSystemProperty(String key, String def) { 103 try { 104 return System.getProperty(key, def); 105 } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx 106 LogLog.debug("Was not allowed to read system property \""+key+"\"."); 107 return def; 108 } 109 } 110 111 112 public 113 static 114 Object instantiateByKey(Properties props, String key, Class superClass, 115 Object defaultValue) { 116 117 // Get the value of the property in string form 118 String className = findAndSubst(key, props); 119 if(className == null) { 120 LogLog.error("Could not find value for key " + key); 121 return defaultValue; 122 } 123 // Trim className to avoid trailing spaces that cause problems. 124 return OptionConverter.instantiateByClassName(className.trim(), superClass, 125 defaultValue); 126 } 127 128 /** 129 If <code>value</code> is "true", then <code>true</code> is 130 returned. If <code>value</code> is "false", then 131 <code>true</code> is returned. Otherwise, <code>default</code> is 132 returned. 133 134 <p>Case of value is unimportant. */ 135 public 136 static 137 boolean toBoolean(String value, boolean dEfault) { 138 if(value == null) 139 return dEfault; 140 String trimmedVal = value.trim(); 141 if("true".equalsIgnoreCase(trimmedVal)) 142 return true; 143 if("false".equalsIgnoreCase(trimmedVal)) 144 return false; 145 return dEfault; 146 } 147 148 public 149 static 150 int toInt(String value, int dEfault) { 151 if(value != null) { 152 String s = value.trim(); 153 try { 154 return Integer.valueOf(s).intValue(); 155 } 156 catch (NumberFormatException e) { 157 LogLog.error("[" + s + "] is not in proper int form."); 158 e.printStackTrace(); 159 } 160 } 161 return dEfault; 162 } 163 164 /** 165 Converts a standard or custom priority level to a Level 166 object. <p> If <code>value</code> is of form 167 "level#classname", then the specified class' toLevel method 168 is called to process the specified level string; if no '#' 169 character is present, then the default {@link org.apache.log4j.Level} 170 class is used to process the level value. 171 172 <p>As a special case, if the <code>value</code> parameter is 173 equal to the string "NULL", then the value <code>null</code> will 174 be returned. 175 176 <p> If any error occurs while converting the value to a level, 177 the <code>defaultValue</code> parameter, which may be 178 <code>null</code>, is returned. 179 180 <p> Case of <code>value</code> is insignificant for the level level, but is 181 significant for the class name part, if present. 182 183 @since 1.1 */ 184 public 185 static 186 Level toLevel(String value, Level defaultValue) { 187 if(value == null) 188 return defaultValue; 189 190 value = value.trim(); 191 192 int hashIndex = value.indexOf('#'); 193 if (hashIndex == -1) { 194 if("NULL".equalsIgnoreCase(value)) { 195 return null; 196 } else { 197 // no class name specified : use standard Level class 198 return(Level) Level.toLevel(value, defaultValue); 199 } 200 } 201 202 Level result = defaultValue; 203 204 String clazz = value.substring(hashIndex+1); 205 String levelName = value.substring(0, hashIndex); 206 207 // This is degenerate case but you never know. 208 if("NULL".equalsIgnoreCase(levelName)) { 209 return null; 210 } 211 212 LogLog.debug("toLevel" + ":class=[" + clazz + "]" 213 + ":pri=[" + levelName + "]"); 214 215 try { 216 Class customLevel = Loader.loadClass(clazz); 217 218 // get a ref to the specified class' static method 219 // toLevel(String, org.apache.log4j.Level) 220 Class[] paramTypes = new Class[] { String.class, 221 org.apache.log4j.Level.class 222 }; 223 java.lang.reflect.Method toLevelMethod = 224 customLevel.getMethod("toLevel", paramTypes); 225 226 // now call the toLevel method, passing level string + default 227 Object[] params = new Object[] {levelName, defaultValue}; 228 Object o = toLevelMethod.invoke(null, params); 229 230 result = (Level) o; 231 } catch(ClassNotFoundException e) { 232 LogLog.warn("custom level class [" + clazz + "] not found."); 233 } catch(NoSuchMethodException e) { 234 LogLog.warn("custom level class [" + clazz + "]" 235 + " does not have a class function toLevel(String, Level)", e); 236 } catch(java.lang.reflect.InvocationTargetException e) { 237 if (e.getTargetException() instanceof InterruptedException 238 || e.getTargetException() instanceof InterruptedIOException) { 239 Thread.currentThread().interrupt(); 240 } 241 LogLog.warn("custom level class [" + clazz + "]" 242 + " could not be instantiated", e); 243 } catch(ClassCastException e) { 244 LogLog.warn("class [" + clazz 245 + "] is not a subclass of org.apache.log4j.Level", e); 246 } catch(IllegalAccessException e) { 247 LogLog.warn("class ["+clazz+ 248 "] cannot be instantiated due to access restrictions", e); 249 } catch(RuntimeException e) { 250 LogLog.warn("class ["+clazz+"], level ["+levelName+ 251 "] conversion failed.", e); 252 } 253 return result; 254 } 255 256 public 257 static 258 long toFileSize(String value, long dEfault) { 259 if(value == null) 260 return dEfault; 261 262 String s = value.trim().toUpperCase(); 263 long multiplier = 1; 264 int index; 265 266 if((index = s.indexOf("KB")) != -1) { 267 multiplier = 1024; 268 s = s.substring(0, index); 269 } 270 else if((index = s.indexOf("MB")) != -1) { 271 multiplier = 1024*1024; 272 s = s.substring(0, index); 273 } 274 else if((index = s.indexOf("GB")) != -1) { 275 multiplier = 1024*1024*1024; 276 s = s.substring(0, index); 277 } 278 if(s != null) { 279 try { 280 return Long.valueOf(s).longValue() * multiplier; 281 } 282 catch (NumberFormatException e) { 283 LogLog.error("[" + s + "] is not in proper int form."); 284 LogLog.error("[" + value + "] not in expected format.", e); 285 } 286 } 287 return dEfault; 288 } 289 290 /** 291 Find the value corresponding to <code>key</code> in 292 <code>props</code>. Then perform variable substitution on the 293 found value. 294 295 */ 296 public 297 static 298 String findAndSubst(String key, Properties props) { 299 String value = props.getProperty(key); 300 if(value == null) 301 return null; 302 303 try { 304 return substVars(value, props); 305 } catch(IllegalArgumentException e) { 306 LogLog.error("Bad option value ["+value+"].", e); 307 return value; 308 } 309 } 310 311 /** 312 Instantiate an object given a class name. Check that the 313 <code>className</code> is a subclass of 314 <code>superClass</code>. If that test fails or the object could 315 not be instantiated, then <code>defaultValue</code> is returned. 316 317 @param className The fully qualified class name of the object to instantiate. 318 @param superClass The class to which the new object should belong. 319 @param defaultValue The object to return in case of non-fulfillment 320 */ 321 public 322 static 323 Object instantiateByClassName(String className, Class superClass, 324 Object defaultValue) { 325 if(className != null) { 326 try { 327 Class classObj = Loader.loadClass(className); 328 if(!superClass.isAssignableFrom(classObj)) { 329 LogLog.error("A \""+className+"\" object is not assignable to a \""+ 330 superClass.getName() + "\" variable."); 331 LogLog.error("The class \""+ superClass.getName()+"\" was loaded by "); 332 LogLog.error("["+superClass.getClassLoader()+"] whereas object of type "); 333 LogLog.error("\"" +classObj.getName()+"\" was loaded by [" 334 +classObj.getClassLoader()+"]."); 335 return defaultValue; 336 } 337 return classObj.newInstance(); 338 } catch (ClassNotFoundException e) { 339 LogLog.error("Could not instantiate class [" + className + "].", e); 340 } catch (IllegalAccessException e) { 341 LogLog.error("Could not instantiate class [" + className + "].", e); 342 } catch (InstantiationException e) { 343 LogLog.error("Could not instantiate class [" + className + "].", e); 344 } catch (RuntimeException e) { 345 LogLog.error("Could not instantiate class [" + className + "].", e); 346 } 347 } 348 return defaultValue; 349 } 350 351 352 /** 353 Perform variable substitution in string <code>val</code> from the 354 values of keys found in the system propeties. 355 356 <p>The variable substitution delimeters are <b>${</b> and <b>}</b>. 357 358 <p>For example, if the System properties contains "key=value", then 359 the call 360 <pre> 361 String s = OptionConverter.substituteVars("Value of key is ${key}."); 362 </pre> 363 364 will set the variable <code>s</code> to "Value of key is value.". 365 366 <p>If no value could be found for the specified key, then the 367 <code>props</code> parameter is searched, if the value could not 368 be found there, then substitution defaults to the empty string. 369 370 <p>For example, if system propeties contains no value for the key 371 "inexistentKey", then the call 372 373 <pre> 374 String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]"); 375 </pre> 376 will set <code>s</code> to "Value of inexistentKey is []" 377 378 <p>An {@link java.lang.IllegalArgumentException} is thrown if 379 <code>val</code> contains a start delimeter "${" which is not 380 balanced by a stop delimeter "}". </p> 381 382 <p><b>Author</b> Avy Sharell</a></p> 383 384 @param val The string on which variable substitution is performed. 385 @throws IllegalArgumentException if <code>val</code> is malformed. 386 387 */ 388 public static 389 String substVars(String val, Properties props) throws 390 IllegalArgumentException { 391 392 StringBuffer sbuf = new StringBuffer(); 393 394 int i = 0; 395 int j, k; 396 397 while(true) { 398 j=val.indexOf(DELIM_START, i); 399 if(j == -1) { 400 // no more variables 401 if(i==0) { // this is a simple string 402 return val; 403 } else { // add the tail string which contails no variables and return the result. 404 sbuf.append(val.substring(i, val.length())); 405 return sbuf.toString(); 406 } 407 } else { 408 sbuf.append(val.substring(i, j)); 409 k = val.indexOf(DELIM_STOP, j); 410 if(k == -1) { 411 throw new IllegalArgumentException('"'+val+ 412 "\" has no closing brace. Opening brace at position " + j 413 + '.'); 414 } else { 415 j += DELIM_START_LEN; 416 String key = val.substring(j, k); 417 // first try in System properties 418 String replacement = getSystemProperty(key, null); 419 // then try props parameter 420 if(replacement == null && props != null) { 421 replacement = props.getProperty(key); 422 } 423 424 if(replacement != null) { 425 // Do variable substitution on the replacement string 426 // such that we can solve "Hello ${x2}" as "Hello p1" 427 // the where the properties are 428 // x1=p1 429 // x2=${x1} 430 String recursiveReplacement = substVars(replacement, props); 431 sbuf.append(recursiveReplacement); 432 } 433 i = k + DELIM_STOP_LEN; 434 } 435 } 436 } 437 } 438 439 /** 440 * Configure log4j given an {@link InputStream}. 441 * 442 * <p> 443 * The InputStream will be interpreted by a new instance of a log4j configurator. 444 * </p> 445 * <p> 446 * All configurations steps are taken on the <code>hierarchy</code> passed as a parameter. 447 * </p> 448 * 449 * @param inputStream 450 * The configuration input stream. 451 * @param clazz 452 * The class name, of the log4j configurator which will parse the <code>inputStream</code>. This must be a 453 * subclass of {@link Configurator}, or null. If this value is null then a default configurator of 454 * {@link PropertyConfigurator} is used. 455 * @param hierarchy 456 * The {@link org.apache.log4j.Hierarchy} to act on. 457 * @since 1.2.17 458 */ 459 460static 461public 462void selectAndConfigure(InputStream inputStream, String clazz, LoggerRepository hierarchy) { 463Configurator configurator = null; 464 465if(clazz != null) { 466 LogLog.debug("Preferred configurator class: " + clazz); 467 configurator = (Configurator) instantiateByClassName(clazz, 468 Configurator.class, 469 null); 470 if(configurator == null) { 471 LogLog.error("Could not instantiate configurator ["+clazz+"]."); 472 return; 473 } 474} else { 475 configurator = new PropertyConfigurator(); 476} 477 478configurator.doConfigure(inputStream, hierarchy); 479} 480 481 482 /** 483 Configure log4j given a URL. 484 485 <p>The url must point to a file or resource which will be interpreted by 486 a new instance of a log4j configurator. 487 488 <p>All configurations steps are taken on the 489 <code>hierarchy</code> passed as a parameter. 490 491 <p> 492 @param url The location of the configuration file or resource. 493 @param clazz The classname, of the log4j configurator which will parse 494 the file or resource at <code>url</code>. This must be a subclass of 495 {@link Configurator}, or null. If this value is null then a default 496 configurator of {@link PropertyConfigurator} is used, unless the 497 filename pointed to by <code>url</code> ends in '.xml', in which case 498 {@link org.apache.log4j.xml.DOMConfigurator} is used. 499 @param hierarchy The {@link org.apache.log4j.Hierarchy} to act on. 500 501 @since 1.1.4 */ 502 503 static 504 public 505 void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) { 506 Configurator configurator = null; 507 String filename = url.getFile(); 508 509 if(clazz == null && filename != null && filename.endsWith(".xml")) { 510 clazz = "org.apache.log4j.xml.DOMConfigurator"; 511 } 512 513 if(clazz != null) { 514 LogLog.debug("Preferred configurator class: " + clazz); 515 configurator = (Configurator) instantiateByClassName(clazz, 516 Configurator.class, 517 null); 518 if(configurator == null) { 519 LogLog.error("Could not instantiate configurator ["+clazz+"]."); 520 return; 521 } 522 } else { 523 configurator = new PropertyConfigurator(); 524 } 525 526 configurator.doConfigure(url, hierarchy); 527 } 528}