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 * <p/> 119 * <p>Case of value is unimportant. 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 if (str != null) { 182 try { 183 return Long.parseLong(str) * multiplier; 184 } catch (final NumberFormatException e) { 185 LOGGER.error("[{}] is not in proper int form.", str); 186 LOGGER.error("[{}] not in expected format.", value, e); 187 } 188 } 189 return defaultValue; 190 } 191 192 /** 193 * Find the value corresponding to <code>key</code> in 194 * <code>props</code>. Then perform variable substitution on the 195 * found value. 196 * @param key The key to locate. 197 * @param props The properties. 198 * @return The String after substitution. 199 */ 200 public static String findAndSubst(final String key, final Properties props) { 201 final String value = props.getProperty(key); 202 if (value == null) { 203 return null; 204 } 205 206 try { 207 return substVars(value, props); 208 } catch (final IllegalArgumentException e) { 209 LOGGER.error("Bad option value [{}].", value, e); 210 return value; 211 } 212 } 213 214 /** 215 * Instantiate an object given a class name. Check that the 216 * <code>className</code> is a subclass of 217 * <code>superClass</code>. If that test fails or the object could 218 * not be instantiated, then <code>defaultValue</code> is returned. 219 * 220 * @param className The fully qualified class name of the object to instantiate. 221 * @param superClass The class to which the new object should belong. 222 * @param defaultValue The object to return in case of non-fulfillment 223 * @return The created object. 224 */ 225 public static Object instantiateByClassName(final String className, final Class<?> superClass, 226 final Object defaultValue) { 227 if (className != null) { 228 try { 229 final Class<?> classObj = Loader.loadClass(className); 230 if (!superClass.isAssignableFrom(classObj)) { 231 LOGGER.error("A \"{}\" object is not assignable to a \"{}\" variable.", className, 232 superClass.getName()); 233 LOGGER.error("The class \"{}\" was loaded by [{}] whereas object of type [{}] was loaded by [{}].", 234 superClass.getName(), superClass.getClassLoader(), classObj.getName()); 235 return defaultValue; 236 } 237 return classObj.newInstance(); 238 } catch (final Exception e) { 239 LOGGER.error("Could not instantiate class [{}].", className, e); 240 } 241 } 242 return defaultValue; 243 } 244 245 246 /** 247 * Perform variable substitution in string <code>val</code> from the 248 * values of keys found in the system propeties. 249 * <p/> 250 * <p>The variable substitution delimiters are <b>${</b> and <b>}</b>. 251 * <p/> 252 * <p>For example, if the System properties contains "key=value", then 253 * the call 254 * <pre> 255 * String s = OptionConverter.substituteVars("Value of key is ${key}."); 256 * </pre> 257 * <p/> 258 * will set the variable <code>s</code> to "Value of key is value.". 259 * <p/> 260 * <p>If no value could be found for the specified key, then the 261 * <code>props</code> parameter is searched, if the value could not 262 * be found there, then substitution defaults to the empty string. 263 * <p/> 264 * <p>For example, if system properties contains no value for the key 265 * "inexistentKey", then the call 266 * <p/> 267 * <pre> 268 * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]"); 269 * </pre> 270 * will set <code>s</code> to "Value of inexistentKey is []" 271 * <p/> 272 * <p>An {@link java.lang.IllegalArgumentException} is thrown if 273 * <code>val</code> contains a start delimeter "${" which is not 274 * balanced by a stop delimeter "}". </p> 275 * <p/> 276 * 277 * @param val The string on which variable substitution is performed. 278 * @param props The properties to use for substitution. 279 * @return The String after substitution. 280 * @throws IllegalArgumentException if <code>val</code> is malformed. 281 */ 282 public static String substVars(final String val, final Properties props) throws 283 IllegalArgumentException { 284 285 final StringBuilder sbuf = new StringBuilder(); 286 287 int i = 0; 288 int j; 289 int k; 290 291 while (true) { 292 j = val.indexOf(DELIM_START, i); 293 if (j == -1) { 294 // no more variables 295 if (i == 0) { // this is a simple string 296 return val; 297 } 298 // add the tail string which contails no variables and return the result. 299 sbuf.append(val.substring(i, val.length())); 300 return sbuf.toString(); 301 } 302 sbuf.append(val.substring(i, j)); 303 k = val.indexOf(DELIM_STOP, j); 304 if (k == -1) { 305 throw new IllegalArgumentException('"' + val + 306 "\" has no closing brace. Opening brace at position " + j 307 + '.'); 308 } 309 j += DELIM_START_LEN; 310 final String key = val.substring(j, k); 311 // first try in System properties 312 String replacement = PropertiesUtil.getProperties().getStringProperty(key, null); 313 // then try props parameter 314 if (replacement == null && props != null) { 315 replacement = props.getProperty(key); 316 } 317 318 if (replacement != null) { 319 // Do variable substitution on the replacement string 320 // such that we can solve "Hello ${x2}" as "Hello p1" 321 // the where the properties are 322 // x1=p1 323 // x2=${x1} 324 final String recursiveReplacement = substVars(replacement, props); 325 sbuf.append(recursiveReplacement); 326 } 327 i = k + DELIM_STOP_LEN; 328 } 329 } 330 }