2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package org.apache.log4j.helpers;
20 import java.io.InputStream;
21 import java.io.InterruptedIOException;
23 import java.util.Properties;
25 import org.apache.log4j.Level;
26 import org.apache.log4j.PropertyConfigurator;
27 import org.apache.log4j.spi.Configurator;
28 import org.apache.log4j.spi.LoggerRepository;
30 // Contributors: Avy Sharell (sharell@online.fr)
31 // Matthieu Verbert (mve@zurich.ibm.com)
35 A convenience class to convert property values to specific types.
37 @author Ceki Gülcü
38 @author Simon Kitching;
39 @author Anders Kristensen
41 public class OptionConverter {
43 static String DELIM_START = "${";
44 static char DELIM_STOP = '}';
45 static int DELIM_START_LEN = 2;
46 static int DELIM_STOP_LEN = 1;
48 /** OptionConverter is a static class. */
49 private OptionConverter() {}
53 String[] concatanateArrays(String[] l, String[] r) {
54 int len = l.length + r.length;
55 String[] a = new String[len];
57 System.arraycopy(l, 0, a, 0, l.length);
58 System.arraycopy(r, 0, a, l.length, r.length);
65 String convertSpecialChars(String s) {
68 StringBuffer sbuf = new StringBuffer(len);
83 } else if(c == '\b') {
85 } else if(c == '\"') {
87 } else if(c == '\'') {
89 } else if(c == '\\') {
95 return sbuf.toString();
100 Very similar to <code>System.getProperty</code> except
101 that the {@link SecurityException} is hidden.
103 @param key The key to search for.
104 @param def The default value to return.
105 @return the string value of the system property, or the default
106 value if there is no property with that key.
111 String getSystemProperty(String key, String def) {
113 return System.getProperty(key, def);
114 } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
115 LogLog.debug("Was not allowed to read system property \""+key+"\".");
123 Object instantiateByKey(Properties props, String key, Class superClass,
124 Object defaultValue) {
126 // Get the value of the property in string form
127 String className = findAndSubst(key, props);
128 if(className == null) {
129 LogLog.error("Could not find value for key " + key);
132 // Trim className to avoid trailing spaces that cause problems.
133 return OptionConverter.instantiateByClassName(className.trim(), superClass,
138 If <code>value</code> is "true", then <code>true</code> is
139 returned. If <code>value</code> is "false", then
140 <code>true</code> is returned. Otherwise, <code>default</code> is
143 <p>Case of value is unimportant. */
146 boolean toBoolean(String value, boolean dEfault) {
150 String trimmedVal = value.trim();
151 if("true".equalsIgnoreCase(trimmedVal)) {
154 if("false".equalsIgnoreCase(trimmedVal)) {
162 int toInt(String value, int dEfault) {
164 String s = value.trim();
166 return Integer.valueOf(s).intValue();
168 catch (NumberFormatException e) {
169 LogLog.error("[" + s + "] is not in proper int form.");
177 Converts a standard or custom priority level to a Level
178 object. <p> If <code>value</code> is of form
179 "level#classname", then the specified class' toLevel method
180 is called to process the specified level string; if no '#'
181 character is present, then the default {@link org.apache.log4j.Level}
182 class is used to process the level value.
184 <p>As a special case, if the <code>value</code> parameter is
185 equal to the string "NULL", then the value <code>null</code> will
188 <p> If any error occurs while converting the value to a level,
189 the <code>defaultValue</code> parameter, which may be
190 <code>null</code>, is returned.
192 <p> Case of <code>value</code> is insignificant for the level level, but is
193 significant for the class name part, if present.
198 Level toLevel(String value, Level defaultValue) {
203 value = value.trim();
205 int hashIndex = value.indexOf('#');
206 if (hashIndex == -1) {
207 if("NULL".equalsIgnoreCase(value)) {
210 // no class name specified : use standard Level class
211 return Level.toLevel(value, defaultValue);
215 Level result = defaultValue;
217 String clazz = value.substring(hashIndex+1);
218 String levelName = value.substring(0, hashIndex);
220 // This is degenerate case but you never know.
221 if("NULL".equalsIgnoreCase(levelName)) {
225 LogLog.debug("toLevel" + ":class=[" + clazz + "]"
226 + ":pri=[" + levelName + "]");
229 Class customLevel = Loader.loadClass(clazz);
231 // get a ref to the specified class' static method
232 // toLevel(String, org.apache.log4j.Level)
233 Class[] paramTypes = new Class[] { String.class,
234 org.apache.log4j.Level.class
236 java.lang.reflect.Method toLevelMethod =
237 customLevel.getMethod("toLevel", paramTypes);
239 // now call the toLevel method, passing level string + default
240 Object[] params = new Object[] {levelName, defaultValue};
241 Object o = toLevelMethod.invoke(null, params);
244 } catch(ClassNotFoundException e) {
245 LogLog.warn("custom level class [" + clazz + "] not found.");
246 } catch(NoSuchMethodException e) {
247 LogLog.warn("custom level class [" + clazz + "]"
248 + " does not have a class function toLevel(String, Level)", e);
249 } catch(java.lang.reflect.InvocationTargetException e) {
250 if (e.getTargetException() instanceof InterruptedException
251 || e.getTargetException() instanceof InterruptedIOException) {
252 Thread.currentThread().interrupt();
254 LogLog.warn("custom level class [" + clazz + "]"
255 + " could not be instantiated", e);
256 } catch(ClassCastException e) {
257 LogLog.warn("class [" + clazz
258 + "] is not a subclass of org.apache.log4j.Level", e);
259 } catch(IllegalAccessException e) {
260 LogLog.warn("class ["+clazz+
261 "] cannot be instantiated due to access restrictions", e);
262 } catch(RuntimeException e) {
263 LogLog.warn("class ["+clazz+"], level ["+levelName+
264 "] conversion failed.", e);
271 long toFileSize(String value, long dEfault) {
276 String s = value.trim().toUpperCase();
280 if((index = s.indexOf("KB")) != -1) {
282 s = s.substring(0, index);
284 else if((index = s.indexOf("MB")) != -1) {
285 multiplier = 1024*1024;
286 s = s.substring(0, index);
288 else if((index = s.indexOf("GB")) != -1) {
289 multiplier = 1024*1024*1024;
290 s = s.substring(0, index);
294 return Long.valueOf(s).longValue() * multiplier;
296 catch (NumberFormatException e) {
297 LogLog.error("[" + s + "] is not in proper int form.");
298 LogLog.error("[" + value + "] not in expected format.", e);
305 Find the value corresponding to <code>key</code> in
306 <code>props</code>. Then perform variable substitution on the
312 String findAndSubst(String key, Properties props) {
313 String value = props.getProperty(key);
319 return substVars(value, props);
320 } catch(IllegalArgumentException e) {
321 LogLog.error("Bad option value ["+value+"].", e);
327 Instantiate an object given a class name. Check that the
328 <code>className</code> is a subclass of
329 <code>superClass</code>. If that test fails or the object could
330 not be instantiated, then <code>defaultValue</code> is returned.
332 @param className The fully qualified class name of the object to instantiate.
333 @param superClass The class to which the new object should belong.
334 @param defaultValue The object to return in case of non-fulfillment
338 Object instantiateByClassName(String className, Class superClass,
339 Object defaultValue) {
340 if(className != null) {
342 Class classObj = Loader.loadClass(className);
343 if(!superClass.isAssignableFrom(classObj)) {
344 LogLog.error("A \""+className+"\" object is not assignable to a \""+
345 superClass.getName() + "\" variable.");
346 LogLog.error("The class \""+ superClass.getName()+"\" was loaded by ");
347 LogLog.error("["+superClass.getClassLoader()+"] whereas object of type ");
348 LogLog.error("\"" +classObj.getName()+"\" was loaded by ["
349 +classObj.getClassLoader()+"].");
352 return classObj.newInstance();
353 } catch (ClassNotFoundException e) {
354 LogLog.error("Could not instantiate class [" + className + "].", e);
355 } catch (IllegalAccessException e) {
356 LogLog.error("Could not instantiate class [" + className + "].", e);
357 } catch (InstantiationException e) {
358 LogLog.error("Could not instantiate class [" + className + "].", e);
359 } catch (RuntimeException e) {
360 LogLog.error("Could not instantiate class [" + className + "].", e);
368 Perform variable substitution in string <code>val</code> from the
369 values of keys found in the system propeties.
371 <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
373 <p>For example, if the System properties contains "key=value", then
376 String s = OptionConverter.substituteVars("Value of key is ${key}.");
379 will set the variable <code>s</code> to "Value of key is value.".
381 <p>If no value could be found for the specified key, then the
382 <code>props</code> parameter is searched, if the value could not
383 be found there, then substitution defaults to the empty string.
385 <p>For example, if system propeties contains no value for the key
386 "inexistentKey", then the call
389 String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
391 will set <code>s</code> to "Value of inexistentKey is []"
393 <p>An {@link java.lang.IllegalArgumentException} is thrown if
394 <code>val</code> contains a start delimeter "${" which is not
395 balanced by a stop delimeter "}". </p>
397 <p><b>Author</b> Avy Sharell</a></p>
399 @param val The string on which variable substitution is performed.
400 @throws IllegalArgumentException if <code>val</code> is malformed.
404 String substVars(String val, Properties props) throws
405 IllegalArgumentException {
407 StringBuffer sbuf = new StringBuffer();
413 j=val.indexOf(DELIM_START, i);
416 if(i==0) { // this is a simple string
418 } else { // add the tail string which contails no variables and return the result.
419 sbuf.append(val.substring(i, val.length()));
420 return sbuf.toString();
423 sbuf.append(val.substring(i, j));
424 k = val.indexOf(DELIM_STOP, j);
426 throw new IllegalArgumentException('"'+val+
427 "\" has no closing brace. Opening brace at position " + j
430 j += DELIM_START_LEN;
431 String key = val.substring(j, k);
432 // first try in System properties
433 String replacement = getSystemProperty(key, null);
434 // then try props parameter
435 if(replacement == null && props != null) {
436 replacement = props.getProperty(key);
439 if(replacement != null) {
440 // Do variable substitution on the replacement string
441 // such that we can solve "Hello ${x2}" as "Hello p1"
442 // the where the properties are
445 String recursiveReplacement = substVars(replacement, props);
446 sbuf.append(recursiveReplacement);
448 i = k + DELIM_STOP_LEN;
455 * Configure log4j given an {@link InputStream}.
458 * The InputStream will be interpreted by a new instance of a log4j configurator.
461 * All configurations steps are taken on the <code>hierarchy</code> passed as a parameter.
465 * The configuration input stream.
467 * The class name, of the log4j configurator which will parse the <code>inputStream</code>. This must be a
468 * subclass of {@link Configurator}, or null. If this value is null then a default configurator of
469 * {@link PropertyConfigurator} is used.
471 * The {@link org.apache.log4j.Hierarchy} to act on.
477 void selectAndConfigure(InputStream inputStream, String clazz, LoggerRepository hierarchy) {
478 Configurator configurator = null;
481 LogLog.debug("Preferred configurator class: " + clazz);
482 configurator = (Configurator) instantiateByClassName(clazz,
485 if(configurator == null) {
486 LogLog.error("Could not instantiate configurator ["+clazz+"].");
490 configurator = new PropertyConfigurator();
493 configurator.doConfigure(inputStream, hierarchy);
498 Configure log4j given a URL.
500 <p>The url must point to a file or resource which will be interpreted by
501 a new instance of a log4j configurator.
503 <p>All configurations steps are taken on the
504 <code>hierarchy</code> passed as a parameter.
507 @param url The location of the configuration file or resource.
508 @param clazz The classname, of the log4j configurator which will parse
509 the file or resource at <code>url</code>. This must be a subclass of
510 {@link Configurator}, or null. If this value is null then a default
511 configurator of {@link PropertyConfigurator} is used, unless the
512 filename pointed to by <code>url</code> ends in '.xml', in which case
513 {@link org.apache.log4j.xml.DOMConfigurator} is used.
514 @param hierarchy The {@link org.apache.log4j.Hierarchy} to act on.
520 void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
521 Configurator configurator = null;
522 String filename = url.getFile();
524 if(clazz == null && filename != null && filename.endsWith(".xml")) {
525 clazz = "org.apache.log4j.xml.DOMConfigurator";
529 LogLog.debug("Preferred configurator class: " + clazz);
530 configurator = (Configurator) instantiateByClassName(clazz,
533 if(configurator == null) {
534 LogLog.error("Could not instantiate configurator ["+clazz+"].");
538 configurator = new PropertyConfigurator();
541 configurator.doConfigure(url, hierarchy);