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