1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.helpers;
19
20 import java.util.Properties;
21 import java.net.URL;
22 import org.apache.log4j.Level;
23 import org.apache.log4j.spi.Configurator;
24 import org.apache.log4j.spi.LoggerRepository;
25 import org.apache.log4j.PropertyConfigurator;
26
27
28
29
30
31 /***
32 A convenience class to convert property values to specific types.
33
34 @author Ceki Gülcü
35 @author Simon Kitching;
36 @author Anders Kristensen
37 */
38 public class OptionConverter {
39
40 static String DELIM_START = "${";
41 static char DELIM_STOP = '}';
42 static int DELIM_START_LEN = 2;
43 static int DELIM_STOP_LEN = 1;
44
45 /*** OptionConverter is a static class. */
46 private OptionConverter() {}
47
48 public
49 static
50 String[] concatanateArrays(String[] l, String[] r) {
51 int len = l.length + r.length;
52 String[] a = new String[len];
53
54 System.arraycopy(l, 0, a, 0, l.length);
55 System.arraycopy(r, 0, a, l.length, r.length);
56
57 return a;
58 }
59
60 public
61 static
62 String convertSpecialChars(String s) {
63 char c;
64 int len = s.length();
65 StringBuffer sbuf = new StringBuffer(len);
66
67 int i = 0;
68 while(i < len) {
69 c = s.charAt(i++);
70 if (c == '//') {
71 c = s.charAt(i++);
72 if(c == 'n') c = '\n';
73 else if(c == 'r') c = '\r';
74 else if(c == 't') c = '\t';
75 else if(c == 'f') c = '\f';
76 else if(c == '\b') c = '\b';
77 else if(c == '\"') c = '\"';
78 else if(c == '\'') c = '\'';
79 else if(c == '//') c = '//';
80 }
81 sbuf.append(c);
82 }
83 return sbuf.toString();
84 }
85
86
87 /***
88 Very similar to <code>System.getProperty</code> except
89 that the {@link SecurityException} is hidden.
90
91 @param key The key to search for.
92 @param def The default value to return.
93 @return the string value of the system property, or the default
94 value if there is no property with that key.
95
96 @since 1.1 */
97 public
98 static
99 String getSystemProperty(String key, String def) {
100 try {
101 return System.getProperty(key, def);
102 } catch(Throwable e) {
103 LogLog.debug("Was not allowed to read system property \""+key+"\".");
104 return def;
105 }
106 }
107
108
109 public
110 static
111 Object instantiateByKey(Properties props, String key, Class superClass,
112 Object defaultValue) {
113
114
115 String className = findAndSubst(key, props);
116 if(className == null) {
117 LogLog.error("Could not find value for key " + key);
118 return defaultValue;
119 }
120
121 return OptionConverter.instantiateByClassName(className.trim(), superClass,
122 defaultValue);
123 }
124
125 /***
126 If <code>value</code> is "true", then <code>true</code> is
127 returned. If <code>value</code> is "false", then
128 <code>true</code> is returned. Otherwise, <code>default</code> is
129 returned.
130
131 <p>Case of value is unimportant. */
132 public
133 static
134 boolean toBoolean(String value, boolean dEfault) {
135 if(value == null)
136 return dEfault;
137 String trimmedVal = value.trim();
138 if("true".equalsIgnoreCase(trimmedVal))
139 return true;
140 if("false".equalsIgnoreCase(trimmedVal))
141 return false;
142 return dEfault;
143 }
144
145 public
146 static
147 int toInt(String value, int dEfault) {
148 if(value != null) {
149 String s = value.trim();
150 try {
151 return Integer.valueOf(s).intValue();
152 }
153 catch (NumberFormatException e) {
154 LogLog.error("[" + s + "] is not in proper int form.");
155 e.printStackTrace();
156 }
157 }
158 return dEfault;
159 }
160
161 /***
162 Converts a standard or custom priority level to a Level
163 object. <p> If <code>value</code> is of form
164 "level#classname", then the specified class' toLevel method
165 is called to process the specified level string; if no '#'
166 character is present, then the default {@link org.apache.log4j.Level}
167 class is used to process the level value.
168
169 <p>As a special case, if the <code>value</code> parameter is
170 equal to the string "NULL", then the value <code>null</code> will
171 be returned.
172
173 <p> If any error occurs while converting the value to a level,
174 the <code>defaultValue</code> parameter, which may be
175 <code>null</code>, is returned.
176
177 <p> Case of <code>value</code> is insignificant for the level level, but is
178 significant for the class name part, if present.
179
180 @since 1.1 */
181 public
182 static
183 Level toLevel(String value, Level defaultValue) {
184 if(value == null)
185 return defaultValue;
186
187 value = value.trim();
188
189 int hashIndex = value.indexOf('#');
190 if (hashIndex == -1) {
191 if("NULL".equalsIgnoreCase(value)) {
192 return null;
193 } else {
194
195 return(Level) Level.toLevel(value, defaultValue);
196 }
197 }
198
199 Level result = defaultValue;
200
201 String clazz = value.substring(hashIndex+1);
202 String levelName = value.substring(0, hashIndex);
203
204
205 if("NULL".equalsIgnoreCase(levelName)) {
206 return null;
207 }
208
209 LogLog.debug("toLevel" + ":class=[" + clazz + "]"
210 + ":pri=[" + levelName + "]");
211
212 try {
213 Class customLevel = Loader.loadClass(clazz);
214
215
216
217 Class[] paramTypes = new Class[] { String.class,
218 org.apache.log4j.Level.class
219 };
220 java.lang.reflect.Method toLevelMethod =
221 customLevel.getMethod("toLevel", paramTypes);
222
223
224 Object[] params = new Object[] {levelName, defaultValue};
225 Object o = toLevelMethod.invoke(null, params);
226
227 result = (Level) o;
228 } catch(ClassNotFoundException e) {
229 LogLog.warn("custom level class [" + clazz + "] not found.");
230 } catch(NoSuchMethodException e) {
231 LogLog.warn("custom level class [" + clazz + "]"
232 + " does not have a class function toLevel(String, Level)", e);
233 } catch(java.lang.reflect.InvocationTargetException e) {
234 LogLog.warn("custom level class [" + clazz + "]"
235 + " could not be instantiated", e);
236 } catch(ClassCastException e) {
237 LogLog.warn("class [" + clazz
238 + "] is not a subclass of org.apache.log4j.Level", e);
239 } catch(IllegalAccessException e) {
240 LogLog.warn("class ["+clazz+
241 "] cannot be instantiated due to access restrictions", e);
242 } catch(Exception e) {
243 LogLog.warn("class ["+clazz+"], level ["+levelName+
244 "] conversion failed.", e);
245 }
246 return result;
247 }
248
249 public
250 static
251 long toFileSize(String value, long dEfault) {
252 if(value == null)
253 return dEfault;
254
255 String s = value.trim().toUpperCase();
256 long multiplier = 1;
257 int index;
258
259 if((index = s.indexOf("KB")) != -1) {
260 multiplier = 1024;
261 s = s.substring(0, index);
262 }
263 else if((index = s.indexOf("MB")) != -1) {
264 multiplier = 1024*1024;
265 s = s.substring(0, index);
266 }
267 else if((index = s.indexOf("GB")) != -1) {
268 multiplier = 1024*1024*1024;
269 s = s.substring(0, index);
270 }
271 if(s != null) {
272 try {
273 return Long.valueOf(s).longValue() * multiplier;
274 }
275 catch (NumberFormatException e) {
276 LogLog.error("[" + s + "] is not in proper int form.");
277 LogLog.error("[" + value + "] not in expected format.", e);
278 }
279 }
280 return dEfault;
281 }
282
283 /***
284 Find the value corresponding to <code>key</code> in
285 <code>props</code>. Then perform variable substitution on the
286 found value.
287
288 */
289 public
290 static
291 String findAndSubst(String key, Properties props) {
292 String value = props.getProperty(key);
293 if(value == null)
294 return null;
295
296 try {
297 return substVars(value, props);
298 } catch(IllegalArgumentException e) {
299 LogLog.error("Bad option value ["+value+"].", e);
300 return value;
301 }
302 }
303
304 /***
305 Instantiate an object given a class name. Check that the
306 <code>className</code> is a subclass of
307 <code>superClass</code>. If that test fails or the object could
308 not be instantiated, then <code>defaultValue</code> is returned.
309
310 @param className The fully qualified class name of the object to instantiate.
311 @param superClass The class to which the new object should belong.
312 @param defaultValue The object to return in case of non-fulfillment
313 */
314 public
315 static
316 Object instantiateByClassName(String className, Class superClass,
317 Object defaultValue) {
318 if(className != null) {
319 try {
320 Class classObj = Loader.loadClass(className);
321 if(!superClass.isAssignableFrom(classObj)) {
322 LogLog.error("A \""+className+"\" object is not assignable to a \""+
323 superClass.getName() + "\" variable.");
324 LogLog.error("The class \""+ superClass.getName()+"\" was loaded by ");
325 LogLog.error("["+superClass.getClassLoader()+"] whereas object of type ");
326 LogLog.error("\"" +classObj.getName()+"\" was loaded by ["
327 +classObj.getClassLoader()+"].");
328 return defaultValue;
329 }
330 return classObj.newInstance();
331 } catch (Exception e) {
332 LogLog.error("Could not instantiate class [" + className + "].", e);
333 }
334 }
335 return defaultValue;
336 }
337
338
339 /***
340 Perform variable substitution in string <code>val</code> from the
341 values of keys found in the system propeties.
342
343 <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
344
345 <p>For example, if the System properties contains "key=value", then
346 the call
347 <pre>
348 String s = OptionConverter.substituteVars("Value of key is ${key}.");
349 </pre>
350
351 will set the variable <code>s</code> to "Value of key is value.".
352
353 <p>If no value could be found for the specified key, then the
354 <code>props</code> parameter is searched, if the value could not
355 be found there, then substitution defaults to the empty string.
356
357 <p>For example, if system propeties contains no value for the key
358 "inexistentKey", then the call
359
360 <pre>
361 String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
362 </pre>
363 will set <code>s</code> to "Value of inexistentKey is []"
364
365 <p>An {@link java.lang.IllegalArgumentException} is thrown if
366 <code>val</code> contains a start delimeter "${" which is not
367 balanced by a stop delimeter "}". </p>
368
369 <p><b>Author</b> Avy Sharell</a></p>
370
371 @param val The string on which variable substitution is performed.
372 @throws IllegalArgumentException if <code>val</code> is malformed.
373
374 */
375 public static
376 String substVars(String val, Properties props) throws
377 IllegalArgumentException {
378
379 StringBuffer sbuf = new StringBuffer();
380
381 int i = 0;
382 int j, k;
383
384 while(true) {
385 j=val.indexOf(DELIM_START, i);
386 if(j == -1) {
387
388 if(i==0) {
389 return val;
390 } else {
391 sbuf.append(val.substring(i, val.length()));
392 return sbuf.toString();
393 }
394 } else {
395 sbuf.append(val.substring(i, j));
396 k = val.indexOf(DELIM_STOP, j);
397 if(k == -1) {
398 throw new IllegalArgumentException('"'+val+
399 "\" has no closing brace. Opening brace at position " + j
400 + '.');
401 } else {
402 j += DELIM_START_LEN;
403 String key = val.substring(j, k);
404
405 String replacement = getSystemProperty(key, null);
406
407 if(replacement == null && props != null) {
408 replacement = props.getProperty(key);
409 }
410
411 if(replacement != null) {
412
413
414
415
416
417 String recursiveReplacement = substVars(replacement, props);
418 sbuf.append(recursiveReplacement);
419 }
420 i = k + DELIM_STOP_LEN;
421 }
422 }
423 }
424 }
425
426
427 /***
428 Configure log4j given a URL.
429
430 <p>The url must point to a file or resource which will be interpreted by
431 a new instance of a log4j configurator.
432
433 <p>All configurations steps are taken on the
434 <code>hierarchy</code> passed as a parameter.
435
436 <p>
437 @param url The location of the configuration file or resource.
438 @param clazz The classname, of the log4j configurator which will parse
439 the file or resource at <code>url</code>. This must be a subclass of
440 {@link Configurator}, or null. If this value is null then a default
441 configurator of {@link PropertyConfigurator} is used, unless the
442 filename pointed to by <code>url</code> ends in '.xml', in which case
443 {@link org.apache.log4j.xml.DOMConfigurator} is used.
444 @param hierarchy The {@link org.apache.log4j.Hierarchy} to act on.
445
446 @since 1.1.4 */
447
448 static
449 public
450 void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
451 Configurator configurator = null;
452 String filename = url.getFile();
453
454 if(clazz == null && filename != null && filename.endsWith(".xml")) {
455 clazz = "org.apache.log4j.xml.DOMConfigurator";
456 }
457
458 if(clazz != null) {
459 LogLog.debug("Preferred configurator class: " + clazz);
460 configurator = (Configurator) instantiateByClassName(clazz,
461 Configurator.class,
462 null);
463 if(configurator == null) {
464 LogLog.error("Could not instantiate configurator ["+clazz+"].");
465 return;
466 }
467 } else {
468 configurator = new PropertyConfigurator();
469 }
470
471 configurator.doConfigure(url, hierarchy);
472 }
473 }