| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.spi.StandardLevel; |
| import org.apache.logging.log4j.util.LoaderUtil; |
| |
| import java.io.InterruptedIOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.Properties; |
| |
| /** |
| * A convenience class to convert property values to specific types. |
| */ |
| public class OptionConverter { |
| |
| static String DELIM_START = "${"; |
| static char DELIM_STOP = '}'; |
| static int DELIM_START_LEN = 2; |
| static int DELIM_STOP_LEN = 1; |
| private static final Logger LOGGER = LogManager.getLogger(OptionConverter.class); |
| private static final CharMap[] charMap = new CharMap[] { |
| new CharMap('n', '\n'), |
| new CharMap('r', '\r'), |
| new CharMap('t', '\t'), |
| new CharMap('f', '\f'), |
| new CharMap('\b', '\b'), |
| new CharMap('\"', '\"'), |
| new CharMap('\'', '\''), |
| new CharMap('\\', '\\') |
| }; |
| |
| /** |
| * OptionConverter is a static class. |
| */ |
| private OptionConverter() { |
| } |
| |
| 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(); |
| StringBuilder sbuf = new StringBuilder(len); |
| |
| int i = 0; |
| while (i < len) { |
| c = s.charAt(i++); |
| if (c == '\\') { |
| c = s.charAt(i++); |
| for (CharMap entry : charMap) { |
| if (entry.key == c) { |
| c = entry.replacement; |
| } |
| } |
| } |
| 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 |
| LOGGER.debug("Was not allowed to read system property \"{}\".", key); |
| return def; |
| } |
| } |
| |
| /** |
| * 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. |
| * @param value The value to convert. |
| * @param dEfault The default value. |
| * @return the value of the result. |
| */ |
| 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; |
| } |
| |
| /** |
| * 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. |
| * @param value The value to convert. |
| * @param defaultValue The default value. |
| * @return the value of the result. |
| * |
| * @since 1.1 |
| */ |
| public static Level toLevel(String value, Level defaultValue) { |
| if (value == null) { |
| return defaultValue; |
| } |
| |
| value = value.trim(); |
| |
| int hashIndex = value.indexOf('#'); |
| if (hashIndex == -1) { |
| if ("NULL".equalsIgnoreCase(value)) { |
| return null; |
| } else { |
| // no class name specified : use standard Level class |
| return 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; |
| } |
| |
| LOGGER.debug("toLevel" + ":class=[" + clazz + "]" |
| + ":pri=[" + levelName + "]"); |
| |
| try { |
| Class customLevel = LoaderUtil.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) { |
| LOGGER.warn("custom level class [" + clazz + "] not found."); |
| } catch (NoSuchMethodException e) { |
| LOGGER.warn("custom level class [" + clazz + "]" |
| + " does not have a class function toLevel(String, Level)", e); |
| } catch (java.lang.reflect.InvocationTargetException e) { |
| if (e.getTargetException() instanceof InterruptedException |
| || e.getTargetException() instanceof InterruptedIOException) { |
| Thread.currentThread().interrupt(); |
| } |
| LOGGER.warn("custom level class [" + clazz + "]" |
| + " could not be instantiated", e); |
| } catch (ClassCastException e) { |
| LOGGER.warn("class [" + clazz |
| + "] is not a subclass of org.apache.log4j.Level", e); |
| } catch (IllegalAccessException e) { |
| LOGGER.warn("class [" + clazz + |
| "] cannot be instantiated due to access restrictions", e); |
| } catch (RuntimeException e) { |
| LOGGER.warn("class [" + clazz + "], level [" + levelName + |
| "] conversion failed.", e); |
| } |
| return result; |
| } |
| |
| /** |
| * 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 |
| * @return The created object. |
| */ |
| public static Object instantiateByClassName(String className, Class<?> superClass, |
| Object defaultValue) { |
| if (className != null) { |
| try { |
| Object obj = LoaderUtil.newInstanceOf(className); |
| if (!superClass.isAssignableFrom(obj.getClass())) { |
| LOGGER.error("A \"{}\" object is not assignable to a \"{}\" variable", className, |
| superClass.getName()); |
| return defaultValue; |
| } |
| return obj; |
| } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |
| | InstantiationException | InvocationTargetException e) { |
| LOGGER.error("Could not instantiate class [" + className + "].", e); |
| } |
| } |
| return defaultValue; |
| } |
| |
| |
| /** |
| * Perform variable substitution in string <code>val</code> from the |
| * values of keys found in the system propeties. |
| * |
| * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>. |
| * |
| * <p>For example, if the System properties contains "key=value", then |
| * the call |
| * <pre> |
| * String s = OptionConverter.substituteVars("Value of key is ${key}."); |
| * </pre> |
| * <p> |
| * will set the variable <code>s</code> to "Value of key is value.". |
| * |
| * <p>If no value could be found for the specified key, then the |
| * <code>props</code> parameter is 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>An {@link IllegalArgumentException} is thrown if |
| * <code>val</code> contains a start delimeter "${" which is not |
| * balanced by a stop delimeter "}". </p> |
| * |
| * <p><b>Author</b> Avy Sharell</p> |
| * |
| * @param val The string on which variable substitution is performed. |
| * @param props The properties to use for the substitution. |
| * @return The substituted string. |
| * @throws IllegalArgumentException if <code>val</code> is malformed. |
| */ |
| public static String substVars(String val, Properties props) throws IllegalArgumentException { |
| |
| StringBuilder sbuf = new StringBuilder(); |
| |
| int i = 0; |
| int j, 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 key = val.substring(j, k); |
| // first try in System properties |
| String replacement = getSystemProperty(key, null); |
| // then try props parameter |
| if (replacement == null && props != null) { |
| replacement = props.getProperty(key); |
| } |
| |
| if (replacement != null) { |
| // Do variable substitution on the replacement string |
| // such that we can solve "Hello ${x2}" as "Hello p1" |
| // the where the properties are |
| // x1=p1 |
| // x2=${x1} |
| String recursiveReplacement = substVars(replacement, props); |
| sbuf.append(recursiveReplacement); |
| } |
| i = k + DELIM_STOP_LEN; |
| } |
| } |
| } |
| } |
| |
| public static org.apache.logging.log4j.Level convertLevel(String level, |
| org.apache.logging.log4j.Level defaultLevel) { |
| Level l = toLevel(level, null); |
| return l != null ? convertLevel(l) : defaultLevel; |
| } |
| |
| public static org.apache.logging.log4j.Level convertLevel(Level level) { |
| if (level == null) { |
| return org.apache.logging.log4j.Level.ERROR; |
| } |
| if (level.isGreaterOrEqual(Level.FATAL)) { |
| return org.apache.logging.log4j.Level.FATAL; |
| } else if (level.isGreaterOrEqual(Level.ERROR)) { |
| return org.apache.logging.log4j.Level.ERROR; |
| } else if (level.isGreaterOrEqual(Level.WARN)) { |
| return org.apache.logging.log4j.Level.WARN; |
| } else if (level.isGreaterOrEqual(Level.INFO)) { |
| return org.apache.logging.log4j.Level.INFO; |
| } else if (level.isGreaterOrEqual(Level.DEBUG)) { |
| return org.apache.logging.log4j.Level.DEBUG; |
| } else if (level.isGreaterOrEqual(Level.TRACE)) { |
| return org.apache.logging.log4j.Level.TRACE; |
| } |
| return org.apache.logging.log4j.Level.ALL; |
| } |
| |
| public static Level convertLevel(org.apache.logging.log4j.Level level) { |
| if (level == null) { |
| return Level.ERROR; |
| } |
| switch (level.getStandardLevel()) { |
| case FATAL: |
| return Level.FATAL; |
| case WARN: |
| return Level.WARN; |
| case INFO: |
| return Level.INFO; |
| case DEBUG: |
| return Level.DEBUG; |
| case TRACE: |
| return Level.TRACE; |
| case ALL: |
| return Level.ALL; |
| case OFF: |
| return Level.OFF; |
| default: |
| return Level.ERROR; |
| } |
| } |
| |
| /** |
| * Find the value corresponding to <code>key</code> in |
| * <code>props</code>. Then perform variable substitution on the |
| * found value. |
| * @param key The key used to locate the substitution string. |
| * @param props The properties to use in the substitution. |
| * @return The substituted string. |
| */ |
| 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) { |
| LOGGER.error("Bad option value [{}].", value, e); |
| return value; |
| } |
| } |
| |
| private static class CharMap { |
| final char key; |
| final char replacement; |
| |
| public CharMap(char key, char replacement) { |
| this.key = key; |
| this.replacement = replacement; |
| } |
| } |
| } |