| /* |
| * Copyright 1999,2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.log4j.helpers; |
| |
| import org.apache.log4j.Level; |
| import org.apache.log4j.LogManager; |
| import org.apache.log4j.Logger; |
| import org.apache.log4j.PropertyConfigurator; |
| import org.apache.log4j.config.ConfiguratorBase; |
| import org.apache.log4j.joran.JoranConfigurator; |
| import org.apache.log4j.spi.Configurator; |
| import org.apache.log4j.spi.LoggerRepository; |
| |
| import java.net.URL; |
| |
| import java.util.Properties; |
| |
| |
| // Contributors: Avy Sharell |
| // Matthieu Verbert |
| // Colin Sampaleanu |
| |
| // Contributors: Avy Sharell |
| // Matthieu Verbert |
| // Colin Sampaleanu |
| |
| /** |
| * A convenience class to convert property values to specific types. |
| * |
| * @author Ceki Gülcü |
| * @author Simon Kitching; |
| * @author Anders Kristensen |
| * @author Avy Sharell |
| */ |
| public class OptionConverter { |
| static String DELIM_START = "${"; |
| static char DELIM_STOP = '}'; |
| static int DELIM_START_LEN = 2; |
| static int DELIM_STOP_LEN = 1; |
| |
| // TODO: this method should be removed if OptionConverter becomes a static |
| static Logger getLogger() { |
| return LogManager.getLogger(OptionConverter.class); |
| } |
| |
| // TODO: this method should be removed if OptionConverter becomes totally static |
| public static void setLoggerRepository(LoggerRepository lr) { |
| |
| } |
| |
| |
| public static String[] concatanateArrays(String[] l, String[] r) { |
| int len = l.length + r.length; |
| String[] a = new String[len]; |
| |
| System.arraycopy(l, 0, a, 0, l.length); |
| System.arraycopy(r, 0, a, l.length, r.length); |
| |
| return a; |
| } |
| |
| public static String convertSpecialChars(String s) { |
| char c; |
| int len = s.length(); |
| StringBuffer sbuf = new StringBuffer(len); |
| |
| int i = 0; |
| |
| while (i < len) { |
| c = s.charAt(i++); |
| |
| if (c == '\\') { |
| c = s.charAt(i++); |
| |
| if (c == 'n') { |
| c = '\n'; |
| } else if (c == 'r') { |
| c = '\r'; |
| } else if (c == 't') { |
| c = '\t'; |
| } else if (c == 'f') { |
| c = '\f'; |
| } else if (c == '\b') { |
| c = '\b'; |
| } else if (c == '\"') { |
| c = '\"'; |
| } else if (c == '\'') { |
| c = '\''; |
| } else if (c == '\\') { |
| c = '\\'; |
| } |
| } |
| |
| sbuf.append(c); |
| } |
| |
| return sbuf.toString(); |
| } |
| |
| /** |
| Very similar to <code>System.getProperty</code> except |
| that the {@link SecurityException} is hidden. |
| |
| @param key The key to search for. |
| @param def The default value to return. |
| @return the string value of the system property, or the default |
| value if there is no property with that key. |
| |
| @since 1.1 */ |
| public static String getSystemProperty(String key, String def) { |
| try { |
| return System.getProperty(key, def); |
| } catch (Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx |
| return def; |
| } |
| } |
| |
| public static Object instantiateByKey( |
| Properties props, String key, Class superClass, Object defaultValue) { |
| // Get the value of the property in string form |
| String className = findAndSubst(key, props); |
| |
| if (className == null) { |
| getLogger().error("Could not find value for key {}", key); |
| |
| return defaultValue; |
| } |
| |
| // Trim className to avoid trailing spaces that cause problems. |
| return instantiateByClassName(className.trim(), superClass, defaultValue); |
| } |
| |
| /** |
| If <code>value</code> is "true", then <code>true</code> is |
| returned. If <code>value</code> is "false", then |
| <code>true</code> is returned. Otherwise, <code>default</code> is |
| returned. |
| |
| <p>Case of value is unimportant. */ |
| public static boolean toBoolean(String value, boolean dEfault) { |
| if (value == null) { |
| return dEfault; |
| } |
| |
| String trimmedVal = value.trim(); |
| |
| if ("true".equalsIgnoreCase(trimmedVal)) { |
| return true; |
| } |
| |
| if ("false".equalsIgnoreCase(trimmedVal)) { |
| return false; |
| } |
| |
| return dEfault; |
| } |
| |
| public static int toInt(String value, int dEfault) { |
| if (value != null) { |
| String s = value.trim(); |
| |
| try { |
| return Integer.valueOf(s).intValue(); |
| } catch (NumberFormatException e) { |
| getLogger().error("[{}] is not in proper int form.", s); |
| } |
| } |
| |
| return dEfault; |
| } |
| |
| /** |
| Converts a standard or custom priority level to a Level |
| object. <p> If <code>value</code> is of form |
| "level#classname", then the specified class' toLevel method |
| is called to process the specified level string; if no '#' |
| character is present, then the default {@link org.apache.log4j.Level} |
| class is used to process the level value. |
| |
| <p>As a special case, if the <code>value</code> parameter is |
| equal to the string "NULL", then the value <code>null</code> will |
| be returned. |
| |
| <p> If any error occurs while converting the value to a level, |
| the <code>defaultValue</code> parameter, which may be |
| <code>null</code>, is returned. |
| |
| <p> Case of <code>value</code> is insignificant for the level level, but is |
| significant for the class name part, if present. |
| |
| @since 1.1 */ |
| public static Level toLevel(String value, Level defaultValue) { |
| if (value == null) { |
| return defaultValue; |
| } |
| |
| int hashIndex = value.indexOf('#'); |
| |
| if (hashIndex == -1) { |
| if ("NULL".equalsIgnoreCase(value)) { |
| return null; |
| } else { |
| // no class name specified : use standard Level class |
| return (Level) Level.toLevel(value, defaultValue); |
| } |
| } |
| |
| Level result = defaultValue; |
| |
| String clazz = value.substring(hashIndex + 1); |
| String levelName = value.substring(0, hashIndex); |
| |
| // This is degenerate case but you never know. |
| if ("NULL".equalsIgnoreCase(levelName)) { |
| return null; |
| } |
| |
| try { |
| Class customLevel = Loader.loadClass(clazz); |
| |
| // get a ref to the specified class' static method |
| // toLevel(String, org.apache.log4j.Level) |
| Class[] paramTypes = |
| new Class[] { String.class, org.apache.log4j.Level.class }; |
| java.lang.reflect.Method toLevelMethod = |
| customLevel.getMethod("toLevel", paramTypes); |
| |
| // now call the toLevel method, passing level string + default |
| Object[] params = new Object[] { levelName, defaultValue }; |
| Object o = toLevelMethod.invoke(null, params); |
| |
| result = (Level) o; |
| } catch (ClassNotFoundException e) { |
| getLogger().warn("custom level class [" + clazz + "] not found."); |
| } catch (NoSuchMethodException e) { |
| getLogger().warn( |
| "custom level class [" + clazz + "]" |
| + " does not have a constructor which takes one string parameter", e); |
| } catch (java.lang.reflect.InvocationTargetException e) { |
| getLogger().warn( |
| "custom level class [" + clazz + "]" + " could not be instantiated", e); |
| } catch (ClassCastException e) { |
| getLogger().warn( |
| "class [" + clazz + "] is not a subclass of org.apache.log4j.Level", e); |
| } catch (IllegalAccessException e) { |
| getLogger().warn( |
| "class [" + clazz |
| + "] cannot be instantiated due to access restrictions", e); |
| } catch (Exception e) { |
| getLogger().warn( |
| "class [" + clazz + "], level [" + levelName + "] conversion failed.", |
| e); |
| } |
| |
| return result; |
| } |
| |
| public static long toFileSize(String value, long dEfault) { |
| if (value == null) { |
| return dEfault; |
| } |
| |
| String s = value.trim().toUpperCase(); |
| long multiplier = 1; |
| int index; |
| |
| if ((index = s.indexOf("KB")) != -1) { |
| multiplier = 1024; |
| s = s.substring(0, index); |
| } else if ((index = s.indexOf("MB")) != -1) { |
| multiplier = 1024 * 1024; |
| s = s.substring(0, index); |
| } else if ((index = s.indexOf("GB")) != -1) { |
| multiplier = 1024 * 1024 * 1024; |
| s = s.substring(0, index); |
| } |
| |
| if (s != null) { |
| try { |
| return Long.valueOf(s).longValue() * multiplier; |
| } catch (NumberFormatException e) { |
| getLogger().error("[{}] is not in proper int form.", s); |
| getLogger().error("[" + value + "] not in expected format.", e); |
| } |
| } |
| |
| return dEfault; |
| } |
| |
| /** |
| Find the value corresponding to <code>key</code> in |
| <code>props</code>. Then perform variable substitution on the |
| found value. |
| |
| */ |
| public static String findAndSubst(String key, Properties props) { |
| String value = props.getProperty(key); |
| |
| if (value == null) { |
| return null; |
| } |
| |
| try { |
| return substVars(value, props); |
| } catch (IllegalArgumentException e) { |
| getLogger().error("Bad option value [" + value + "].", e); |
| |
| return value; |
| } |
| } |
| |
| /** |
| Instantiate an object given a class name. Check that the |
| <code>className</code> is a subclass of |
| <code>superClass</code>. If that test fails or the object could |
| not be instantiated, then <code>defaultValue</code> is returned. |
| |
| @param className The fully qualified class name of the object to instantiate. |
| @param superClass The class to which the new object should belong. |
| @param defaultValue The object to return in case of non-fulfillment |
| */ |
| public static Object instantiateByClassName( |
| String className, Class superClass, Object defaultValue) { |
| if (className != null) { |
| try { |
| Class classObj = Loader.loadClass(className); |
| |
| if (!superClass.isAssignableFrom(classObj)) { |
| getLogger().error( |
| "A \"" + className + "\" object is not assignable to a \"" |
| + superClass.getName() + "\" variable."); |
| getLogger().error( |
| "The class \"" + superClass.getName() + "\" was loaded by "); |
| getLogger().error( |
| "[" + superClass.getClassLoader() + "] whereas object of type "); |
| getLogger().error( |
| "\"" + classObj.getName() + "\" was loaded by [" |
| + classObj.getClassLoader() + "]."); |
| |
| return defaultValue; |
| } |
| |
| //System.out.println("About to call classObj.newInstance(), "+classObj.getName()); |
| |
| return classObj.newInstance(); |
| } catch(NoClassDefFoundError ncfe) { |
| getLogger().error("Could not instantiate object of class [" + className + "].", ncfe); |
| } catch (Throwable e) { |
| getLogger().error("Could not instantiate object of class [" + className + "].", e); |
| } |
| } |
| |
| return defaultValue; |
| } |
| |
| /** |
| * Perform variable substitution in string <code>val</code> from the values of |
| * keys found the properties passed as parameter or in the system propeties. |
| * |
| * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>. |
| * |
| * <p>For example, if the properties parameter contains a property "key1" set |
| * as "value1", then the call <pre> |
| * String s = OptionConverter.substituteVars("Value of key is ${key1}.");</pre> |
| * will set the variable <code>s</code> to "Value of key is value1.". |
| * |
| * <p>If no value could be found for the specified key, then the |
| * system properties are searched, if the value could not be found |
| * there, then substitution defaults to the empty string. |
| * |
| * <p>For example, if system propeties contains no value for the key |
| * "inexistentKey", then the call |
| * <pre> |
| * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]"); </pre> |
| * will set <code>s</code> to "Value of inexistentKey is []". |
| * |
| * <p>Nevertheless, it is possible to specify a default substitution value |
| * using the ":-" operator. For example, the call |
| * <pre> |
| * String s = OptionConverter.subsVars("Value of key is [${key2:-val2}]");</pre> |
| * will set <code>s</code> to "Value of key is [val2]" even if the "key2" |
| * property is unset. |
| * |
| * <p>An {@link java.lang.IllegalArgumentException} is thrown if |
| * <code>val</code> contains a start delimeter "${" which is not |
| * balanced by a stop delimeter "}". </p> |
| * |
| * @param val The string on which variable substitution is performed. |
| * @throws IllegalArgumentException if <code>val</code> is malformed. |
| */ |
| public static String substVars(String val, Properties props) { |
| |
| StringBuffer sbuf = new StringBuffer(); |
| |
| int i = 0; |
| int j; |
| int k; |
| |
| while (true) { |
| j = val.indexOf(DELIM_START, i); |
| |
| if (j == -1) { |
| // no more variables |
| if (i == 0) { // this is a simple string |
| |
| return val; |
| } else { // add the tail string which contails no variables and return the result. |
| sbuf.append(val.substring(i, val.length())); |
| |
| return sbuf.toString(); |
| } |
| } else { |
| sbuf.append(val.substring(i, j)); |
| k = val.indexOf(DELIM_STOP, j); |
| |
| if (k == -1) { |
| throw new IllegalArgumentException( |
| '"' + val + "\" has no closing brace. Opening brace at position " |
| + j + '.'); |
| } else { |
| j += DELIM_START_LEN; |
| |
| String rawKey = val.substring(j, k); |
| |
| // Massage the key to extract a default replacement if there is one |
| String[] extracted = extractDefaultReplacement(rawKey); |
| String key = extracted[0]; |
| String defaultReplacement = extracted[1]; // can be null |
| |
| String replacement = null; |
| |
| // first try the props passed as parameter |
| if(props != null) { |
| replacement = props.getProperty(key); |
| } |
| |
| // then try in System properties |
| if (replacement == null) { |
| replacement = getSystemProperty(key, null); |
| } |
| |
| // if replacement is still null, use the defaultReplacement which |
| // still be null |
| if(replacement == null) { |
| replacement = defaultReplacement; |
| } |
| |
| if (replacement != null) { |
| // Do variable substitution on the replacement string |
| // such that we can solve "Hello ${x2}" as "Hello p1" |
| // where the properties are |
| // x1=p1 |
| // x2=${x1} |
| String recursiveReplacement = substVars(replacement, props); |
| sbuf.append(recursiveReplacement); |
| } |
| |
| i = k + DELIM_STOP_LEN; |
| } |
| } |
| } |
| } |
| |
| static public String[] extractDefaultReplacement(String key) { |
| String[] result = new String[2]; |
| result[0] = key; |
| int d = key.indexOf(":-"); |
| if(d != -1) { |
| result[0] = key.substring(0, d); |
| result[1] = key.substring(d+2); |
| } |
| return result; |
| } |
| |
| /** |
| Configure log4j given a URL. |
| |
| <p>The url must point to a file or resource which will be interpreted by |
| a new instance of a log4j configurator. |
| |
| <p>All configurations steps are taken on the |
| <code>hierarchy</code> passed as a parameter. |
| |
| <p> |
| @param url The location of the configuration file or resource. |
| @param clazz The classname, of the log4j configurator which will parse |
| the file or resource at <code>url</code>. This must be a subclass of |
| {@link Configurator}, or null. If this value is null then a default |
| configurator of {@link PropertyConfigurator} is used, unless the |
| filename pointed to by <code>url</code> ends in '.xml', in which case |
| {@link org.apache.log4j.xml.DOMConfigurator} is used. |
| @param hierarchy The {@link org.apache.log4j.Hierarchy} to act on. |
| |
| @since 1.1.4 */ |
| public static void selectAndConfigure( |
| URL url, String clazz, LoggerRepository repository) { |
| Configurator configurator = null; |
| String filename = url.getFile(); |
| |
| if ((clazz == null) && (filename != null) && filename.endsWith(".xml")) { |
| clazz = JoranConfigurator.class.getName(); |
| } |
| |
| if (clazz != null) { |
| Logger logger = repository.getLogger(OptionConverter.class.getName()); |
| logger.info("Preferred configurator class: " + clazz); |
| |
| configurator = |
| (Configurator) instantiateByClassName(clazz, Configurator.class, null); |
| |
| if (configurator == null) { |
| logger.error("Could not instantiate configurator [" + clazz + "]."); |
| |
| return; |
| } |
| } else { |
| configurator = new PropertyConfigurator(); |
| } |
| |
| configurator.doConfigure(url, repository); |
| if(configurator instanceof ConfiguratorBase) { |
| ((ConfiguratorBase)configurator).dumpErrors(); |
| } |
| } |
| } |