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 package org.apache.logging.log4j.core.util; 018 019 import java.util.Locale; 020 import java.util.Properties; 021 022 import org.apache.logging.log4j.Logger; 023 import org.apache.logging.log4j.status.StatusLogger; 024 import org.apache.logging.log4j.util.PropertiesUtil; 025 026 /** 027 * A convenience class to convert property values to specific types. 028 */ 029 public final class OptionConverter { 030 031 private static final Logger LOGGER = StatusLogger.getLogger(); 032 033 private static final String DELIM_START = "${"; 034 private static final char DELIM_STOP = '}'; 035 private static final int DELIM_START_LEN = 2; 036 private static final int DELIM_STOP_LEN = 1; 037 private static final int ONE_K = 1024; 038 039 /** 040 * OptionConverter is a static class. 041 */ 042 private OptionConverter() { 043 } 044 045 public static String[] concatenateArrays(final String[] l, final String[] r) { 046 final int len = l.length + r.length; 047 final String[] a = new String[len]; 048 049 System.arraycopy(l, 0, a, 0, l.length); 050 System.arraycopy(r, 0, a, l.length, r.length); 051 052 return a; 053 } 054 055 public static String convertSpecialChars(final String s) { 056 char c; 057 final int len = s.length(); 058 final StringBuilder sbuf = new StringBuilder(len); 059 060 int i = 0; 061 while (i < len) { 062 c = s.charAt(i++); 063 if (c == '\\') { 064 c = s.charAt(i++); 065 if (c == 'n') { 066 c = '\n'; 067 } else if (c == 'r') { 068 c = '\r'; 069 } else if (c == 't') { 070 c = '\t'; 071 } else if (c == 'f') { 072 c = '\f'; 073 } else if (c == '\b') { 074 c = '\b'; 075 } else if (c == '\"') { 076 c = '\"'; 077 } else if (c == '\'') { 078 c = '\''; 079 } else if (c == '\\') { 080 c = '\\'; 081 } 082 } 083 sbuf.append(c); 084 } 085 return sbuf.toString(); 086 } 087 088 public static Object instantiateByKey(final Properties props, final String key, final Class<?> superClass, 089 final Object defaultValue) { 090 091 // Get the value of the property in string form 092 final String className = findAndSubst(key, props); 093 if (className == null) { 094 LOGGER.error("Could not find value for key {}", key); 095 return defaultValue; 096 } 097 // Trim className to avoid trailing spaces that cause problems. 098 return OptionConverter.instantiateByClassName(className.trim(), superClass, 099 defaultValue); 100 } 101 102 /** 103 * If <code>value</code> is "true", then {@code true} is 104 * returned. If <code>value</code> is "false", then 105 * {@code false} is returned. Otherwise, <code>default</code> is 106 * returned. 107 * <p/> 108 * <p>Case of value is unimportant. 109 * @param value The value to convert. 110 * @param defaultValue The default value. 111 * @return true or false, depending on the value and/or default. 112 */ 113 public static boolean toBoolean(final String value, final boolean defaultValue) { 114 if (value == null) { 115 return defaultValue; 116 } 117 final String trimmedVal = value.trim(); 118 if ("true".equalsIgnoreCase(trimmedVal)) { 119 return true; 120 } 121 if ("false".equalsIgnoreCase(trimmedVal)) { 122 return false; 123 } 124 return defaultValue; 125 } 126 127 /** 128 * Convert the String value to an int. 129 * @param value The value as a String. 130 * @param defaultValue The default value. 131 * @return The value as an int. 132 */ 133 public static int toInt(final String value, final int defaultValue) { 134 if (value != null) { 135 final String s = value.trim(); 136 try { 137 return Integer.parseInt(s); 138 } catch (final NumberFormatException e) { 139 LOGGER.error("[{}] is not in proper int form.", s, e); 140 } 141 } 142 return defaultValue; 143 } 144 145 /** 146 * 147 * @param value The size of the file as a String. 148 * @param defaultValue The default value. 149 * @return The size of the file as a long. 150 */ 151 public static long toFileSize(final String value, final long defaultValue) { 152 if (value == null) { 153 return defaultValue; 154 } 155 156 String str = value.trim().toUpperCase(Locale.ENGLISH); 157 long multiplier = 1; 158 int index; 159 160 if ((index = str.indexOf("KB")) != -1) { 161 multiplier = ONE_K; 162 str = str.substring(0, index); 163 } else if ((index = str.indexOf("MB")) != -1) { 164 multiplier = ONE_K * ONE_K; 165 str = str.substring(0, index); 166 } else if ((index = str.indexOf("GB")) != -1) { 167 multiplier = ONE_K * ONE_K * ONE_K; 168 str = str.substring(0, index); 169 } 170 if (str != null) { 171 try { 172 return Long.parseLong(str) * multiplier; 173 } catch (final NumberFormatException e) { 174 LOGGER.error("[{}] is not in proper int form.", str); 175 LOGGER.error("[{}] not in expected format.", value, e); 176 } 177 } 178 return defaultValue; 179 } 180 181 /** 182 * Find the value corresponding to <code>key</code> in 183 * <code>props</code>. Then perform variable substitution on the 184 * found value. 185 * @param key The key to locate. 186 * @param props The properties. 187 * @return The String after substitution. 188 */ 189 public static String findAndSubst(final String key, final Properties props) { 190 final String value = props.getProperty(key); 191 if (value == null) { 192 return null; 193 } 194 195 try { 196 return substVars(value, props); 197 } catch (final IllegalArgumentException e) { 198 LOGGER.error("Bad option value [{}].", value, e); 199 return value; 200 } 201 } 202 203 /** 204 * Instantiate an object given a class name. Check that the 205 * <code>className</code> is a subclass of 206 * <code>superClass</code>. If that test fails or the object could 207 * not be instantiated, then <code>defaultValue</code> is returned. 208 * 209 * @param className The fully qualified class name of the object to instantiate. 210 * @param superClass The class to which the new object should belong. 211 * @param defaultValue The object to return in case of non-fulfillment 212 * @return The created object. 213 */ 214 public static Object instantiateByClassName(final String className, final Class<?> superClass, 215 final Object defaultValue) { 216 if (className != null) { 217 try { 218 final Class<?> classObj = Loader.loadClass(className); 219 if (!superClass.isAssignableFrom(classObj)) { 220 LOGGER.error("A \"{}\" object is not assignable to a \"{}\" variable.", className, 221 superClass.getName()); 222 LOGGER.error("The class \"{}\" was loaded by [{}] whereas object of type [{}] was loaded by [{}].", 223 superClass.getName(), superClass.getClassLoader(), classObj.getName()); 224 return defaultValue; 225 } 226 return classObj.newInstance(); 227 } catch (final Exception e) { 228 LOGGER.error("Could not instantiate class [{}].", className, e); 229 } 230 } 231 return defaultValue; 232 } 233 234 235 /** 236 * Perform variable substitution in string <code>val</code> from the 237 * values of keys found in the system propeties. 238 * <p/> 239 * <p>The variable substitution delimiters are <b>${</b> and <b>}</b>. 240 * <p/> 241 * <p>For example, if the System properties contains "key=value", then 242 * the call 243 * <pre> 244 * String s = OptionConverter.substituteVars("Value of key is ${key}."); 245 * </pre> 246 * <p/> 247 * will set the variable <code>s</code> to "Value of key is value.". 248 * <p/> 249 * <p>If no value could be found for the specified key, then the 250 * <code>props</code> parameter is searched, if the value could not 251 * be found there, then substitution defaults to the empty string. 252 * <p/> 253 * <p>For example, if system properties contains no value for the key 254 * "inexistentKey", then the call 255 * <p/> 256 * <pre> 257 * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]"); 258 * </pre> 259 * will set <code>s</code> to "Value of inexistentKey is []" 260 * <p/> 261 * <p>An {@link java.lang.IllegalArgumentException} is thrown if 262 * <code>val</code> contains a start delimeter "${" which is not 263 * balanced by a stop delimeter "}". </p> 264 * <p/> 265 * 266 * @param val The string on which variable substitution is performed. 267 * @param props The properties to use for substitution. 268 * @return The String after substitution. 269 * @throws IllegalArgumentException if <code>val</code> is malformed. 270 */ 271 public static String substVars(final String val, final Properties props) throws 272 IllegalArgumentException { 273 274 final StringBuilder sbuf = new StringBuilder(); 275 276 int i = 0; 277 int j; 278 int k; 279 280 while (true) { 281 j = val.indexOf(DELIM_START, i); 282 if (j == -1) { 283 // no more variables 284 if (i == 0) { // this is a simple string 285 return val; 286 } 287 // add the tail string which contails no variables and return the result. 288 sbuf.append(val.substring(i, val.length())); 289 return sbuf.toString(); 290 } 291 sbuf.append(val.substring(i, j)); 292 k = val.indexOf(DELIM_STOP, j); 293 if (k == -1) { 294 throw new IllegalArgumentException('"' + val + 295 "\" has no closing brace. Opening brace at position " + j 296 + '.'); 297 } 298 j += DELIM_START_LEN; 299 final String key = val.substring(j, k); 300 // first try in System properties 301 String replacement = PropertiesUtil.getProperties().getStringProperty(key, null); 302 // then try props parameter 303 if (replacement == null && props != null) { 304 replacement = props.getProperty(key); 305 } 306 307 if (replacement != null) { 308 // Do variable substitution on the replacement string 309 // such that we can solve "Hello ${x2}" as "Hello p1" 310 // the where the properties are 311 // x1=p1 312 // x2=${x1} 313 final String recursiveReplacement = substVars(replacement, props); 314 sbuf.append(recursiveReplacement); 315 } 316 i = k + DELIM_STOP_LEN; 317 } 318 } 319 }