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.helpers; 018 019 import org.apache.logging.log4j.Logger; 020 import org.apache.logging.log4j.status.StatusLogger; 021 import org.apache.logging.log4j.util.PropertiesUtil; 022 023 import java.util.Locale; 024 import java.util.Properties; 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[] concatanateArrays(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 StringBuffer sbuf = new StringBuffer(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 true} 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 dEfault 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 dEfault) { 114 if (value == null) { 115 return dEfault; 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 dEfault; 125 } 126 127 /** 128 * Convert the String value to an int. 129 * @param value The value as a String. 130 * @param dEfault The default value. 131 * @return The value as an int. 132 */ 133 public static int toInt(final String value, final int dEfault) { 134 if (value != null) { 135 final String s = value.trim(); 136 try { 137 return Integer.valueOf(s); 138 } catch (final NumberFormatException e) { 139 LOGGER.error("[" + s + "] is not in proper int form."); 140 e.printStackTrace(); 141 } 142 } 143 return dEfault; 144 } 145 146 /** 147 * 148 * @param value The size of the file as a String. 149 * @param defaultValue The default value. 150 * @return The size of the file as a long. 151 */ 152 public static long toFileSize(final String value, final long defaultValue) { 153 if (value == null) { 154 return defaultValue; 155 } 156 157 String s = value.trim().toUpperCase(Locale.ENGLISH); 158 long multiplier = 1; 159 int index; 160 161 if ((index = s.indexOf("KB")) != -1) { 162 multiplier = ONE_K; 163 s = s.substring(0, index); 164 } else if ((index = s.indexOf("MB")) != -1) { 165 multiplier = ONE_K * ONE_K; 166 s = s.substring(0, index); 167 } else if ((index = s.indexOf("GB")) != -1) { 168 multiplier = ONE_K * ONE_K * ONE_K; 169 s = s.substring(0, index); 170 } 171 if (s != null) { 172 try { 173 return Long.valueOf(s) * multiplier; 174 } catch (final NumberFormatException e) { 175 LOGGER.error("[" + s + "] is not in proper int form."); 176 LOGGER.error("[" + value + "] not in expected format.", e); 177 } 178 } 179 return defaultValue; 180 } 181 182 /** 183 * Find the value corresponding to <code>key</code> in 184 * <code>props</code>. Then perform variable substitution on the 185 * found value. 186 * @param key The key to locate. 187 * @param props The properties. 188 * @return The String after substitution. 189 */ 190 public static String findAndSubst(final String key, final Properties props) { 191 final String value = props.getProperty(key); 192 if (value == null) { 193 return null; 194 } 195 196 try { 197 return substVars(value, props); 198 } catch (final IllegalArgumentException e) { 199 LOGGER.error("Bad option value [" + value + "].", e); 200 return value; 201 } 202 } 203 204 /** 205 * Instantiate an object given a class name. Check that the 206 * <code>className</code> is a subclass of 207 * <code>superClass</code>. If that test fails or the object could 208 * not be instantiated, then <code>defaultValue</code> is returned. 209 * 210 * @param className The fully qualified class name of the object to instantiate. 211 * @param superClass The class to which the new object should belong. 212 * @param defaultValue The object to return in case of non-fulfillment 213 * @return The created object. 214 */ 215 public static Object instantiateByClassName(final String className, final Class<?> superClass, 216 final Object defaultValue) { 217 if (className != null) { 218 try { 219 final Class<?> classObj = Loader.loadClass(className); 220 if (!superClass.isAssignableFrom(classObj)) { 221 LOGGER.error("A \"" + className + "\" object is not assignable to a \"" + 222 superClass.getName() + "\" variable."); 223 LOGGER.error("The class \"" + superClass.getName() + "\" was loaded by "); 224 LOGGER.error("[" + superClass.getClassLoader() + "] whereas object of type "); 225 LOGGER.error("\"" + classObj.getName() + "\" was loaded by [" 226 + classObj.getClassLoader() + "]."); 227 return defaultValue; 228 } 229 return classObj.newInstance(); 230 } catch (final ClassNotFoundException e) { 231 LOGGER.error("Could not instantiate class [" + className + "].", e); 232 } catch (final IllegalAccessException e) { 233 LOGGER.error("Could not instantiate class [" + className + "].", e); 234 } catch (final InstantiationException e) { 235 LOGGER.error("Could not instantiate class [" + className + "].", e); 236 } catch (final RuntimeException 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 * <p/> 248 * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>. 249 * <p/> 250 * <p>For example, if the System properties contains "key=value", then 251 * the call 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. 261 * <p/> 262 * <p>For example, if system propeties 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 * will set <code>s</code> to "Value of inexistentKey is []" 269 * <p/> 270 * <p>An {@link java.lang.IllegalArgumentException} is thrown if 271 * <code>val</code> contains a start delimeter "${" which is not 272 * balanced by a stop delimeter "}". </p> 273 * <p/> 274 * <p><b>Author</b> Avy Sharell</a></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 } else { // 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 } else { 301 sbuf.append(val.substring(i, j)); 302 k = val.indexOf(DELIM_STOP, j); 303 if (k == -1) { 304 throw new IllegalArgumentException('"' + val + 305 "\" has no closing brace. Opening brace at position " + j 306 + '.'); 307 } else { 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 } 330 } 331 }