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