View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.helpers;
18  
19  import org.apache.logging.log4j.Logger;
20  import org.apache.logging.log4j.status.StatusLogger;
21  
22  import java.util.Locale;
23  import java.util.Properties;
24  
25  /**
26   * A convenience class to convert property values to specific types.
27   */
28  public final class OptionConverter {
29  
30      private static final Logger LOGGER = StatusLogger.getLogger();
31  
32      private static final String DELIM_START = "${";
33      private static final char DELIM_STOP = '}';
34      private static final int DELIM_START_LEN = 2;
35      private static final int DELIM_STOP_LEN = 1;
36      private static final int ONE_K = 1024;
37  
38      /**
39       * OptionConverter is a static class.
40       */
41      private OptionConverter() {
42      }
43  
44      public static String[] concatanateArrays(String[] l, String[] r) {
45          int len = l.length + r.length;
46          String[] a = new String[len];
47  
48          System.arraycopy(l, 0, a, 0, l.length);
49          System.arraycopy(r, 0, a, l.length, r.length);
50  
51          return a;
52      }
53  
54      public static String convertSpecialChars(String s) {
55          char c;
56          int len = s.length();
57          StringBuffer sbuf = new StringBuffer(len);
58  
59          int i = 0;
60          while (i < len) {
61              c = s.charAt(i++);
62              if (c == '\\') {
63                  c = s.charAt(i++);
64                  if (c == 'n') {
65                      c = '\n';
66                  } else if (c == 'r') {
67                      c = '\r';
68                  } else if (c == 't') {
69                      c = '\t';
70                  } else if (c == 'f') {
71                      c = '\f';
72                  } else if (c == '\b') {
73                      c = '\b';
74                  } else if (c == '\"') {
75                      c = '\"';
76                  } else if (c == '\'') {
77                      c = '\'';
78                  } else if (c == '\\') {
79                      c = '\\';
80                  }
81              }
82              sbuf.append(c);
83          }
84          return sbuf.toString();
85      }
86  
87  
88      /**
89       * Very similar to <code>System.getProperty</code> except
90       * that the {@link SecurityException} is hidden.
91       *
92       * @param key The key to search for.
93       * @param def The default value to return.
94       * @return the string value of the system property, or the default
95       *         value if there is no property with that key.
96       */
97      public static String getSystemProperty(String key, String def) {
98          try {
99              return System.getProperty(key, def);
100         } catch (Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
101             LOGGER.debug("Was not allowed to read system property \"" + key + "\".");
102             return def;
103         }
104     }
105 
106 
107     public static Object instantiateByKey(Properties props, String key, Class<?> superClass,
108                                    Object defaultValue) {
109 
110         // Get the value of the property in string form
111         String className = findAndSubst(key, props);
112         if (className == null) {
113             LOGGER.error("Could not find value for key " + key);
114             return defaultValue;
115         }
116         // Trim className to avoid trailing spaces that cause problems.
117         return OptionConverter.instantiateByClassName(className.trim(), superClass,
118             defaultValue);
119     }
120 
121     /**
122      * If <code>value</code> is "true", then {@code true} is
123      * returned. If <code>value</code> is "false", then
124      * {@code true} is returned. Otherwise, <code>default</code> is
125      * returned.
126      * <p/>
127      * <p>Case of value is unimportant.
128      * @param value The value to convert.
129      * @param dEfault The default value.
130      * @return true or false, depending on the value and/or default.
131      */
132     public static boolean toBoolean(String value, boolean dEfault) {
133         if (value == null) {
134             return dEfault;
135         }
136         String trimmedVal = value.trim();
137         if ("true".equalsIgnoreCase(trimmedVal)) {
138             return true;
139         }
140         if ("false".equalsIgnoreCase(trimmedVal)) {
141             return false;
142         }
143         return dEfault;
144     }
145 
146     /**
147      * Convert the String value to an int.
148      * @param value The value as a String.
149      * @param dEfault The default value.
150      * @return The value as an int.
151      */
152     public static int toInt(String value, int dEfault) {
153         if (value != null) {
154             String s = value.trim();
155             try {
156                 return Integer.valueOf(s);
157             } catch (NumberFormatException e) {
158                 LOGGER.error("[" + s + "] is not in proper int form.");
159                 e.printStackTrace();
160             }
161         }
162         return dEfault;
163     }
164 
165     /**
166      *
167      * @param value The size of the file as a String.
168      * @param defaultValue The default value.
169      * @return The size of the file as a long.
170      */
171     public static long toFileSize(String value, long defaultValue) {
172         if (value == null) {
173             return defaultValue;
174         }
175 
176         String s = value.trim().toUpperCase(Locale.ENGLISH);
177         long multiplier = 1;
178         int index;
179 
180         if ((index = s.indexOf("KB")) != -1) {
181             multiplier = ONE_K;
182             s = s.substring(0, index);
183         } else if ((index = s.indexOf("MB")) != -1) {
184             multiplier = ONE_K * ONE_K;
185             s = s.substring(0, index);
186         } else if ((index = s.indexOf("GB")) != -1) {
187             multiplier = ONE_K * ONE_K * ONE_K;
188             s = s.substring(0, index);
189         }
190         if (s != null) {
191             try {
192                 return Long.valueOf(s) * multiplier;
193             } catch (NumberFormatException e) {
194                 LOGGER.error("[" + s + "] is not in proper int form.");
195                 LOGGER.error("[" + value + "] not in expected format.", e);
196             }
197         }
198         return defaultValue;
199     }
200 
201     /**
202      * Find the value corresponding to <code>key</code> in
203      * <code>props</code>. Then perform variable substitution on the
204      * found value.
205      * @param key The key to locate.
206      * @param props The properties.
207      * @return The String after substitution.
208      */
209     public static String findAndSubst(String key, Properties props) {
210         String value = props.getProperty(key);
211         if (value == null) {
212             return null;
213         }
214 
215         try {
216             return substVars(value, props);
217         } catch (IllegalArgumentException e) {
218             LOGGER.error("Bad option value [" + value + "].", e);
219             return value;
220         }
221     }
222 
223     /**
224      * Instantiate an object given a class name. Check that the
225      * <code>className</code> is a subclass of
226      * <code>superClass</code>. If that test fails or the object could
227      * not be instantiated, then <code>defaultValue</code> is returned.
228      *
229      * @param className    The fully qualified class name of the object to instantiate.
230      * @param superClass   The class to which the new object should belong.
231      * @param defaultValue The object to return in case of non-fulfillment
232      * @return The created object.
233      */
234     public static Object instantiateByClassName(String className, Class<?> superClass,
235                                          Object defaultValue) {
236         if (className != null) {
237             try {
238                 Class<?> classObj = Loader.loadClass(className);
239                 if (!superClass.isAssignableFrom(classObj)) {
240                     LOGGER.error("A \"" + className + "\" object is not assignable to a \"" +
241                         superClass.getName() + "\" variable.");
242                     LOGGER.error("The class \"" + superClass.getName() + "\" was loaded by ");
243                     LOGGER.error("[" + superClass.getClassLoader() + "] whereas object of type ");
244                     LOGGER.error("\"" + classObj.getName() + "\" was loaded by ["
245                         + classObj.getClassLoader() + "].");
246                     return defaultValue;
247                 }
248                 return classObj.newInstance();
249             } catch (ClassNotFoundException e) {
250                 LOGGER.error("Could not instantiate class [" + className + "].", e);
251             } catch (IllegalAccessException e) {
252                 LOGGER.error("Could not instantiate class [" + className + "].", e);
253             } catch (InstantiationException e) {
254                 LOGGER.error("Could not instantiate class [" + className + "].", e);
255             } catch (RuntimeException e) {
256                 LOGGER.error("Could not instantiate class [" + className + "].", e);
257             }
258         }
259         return defaultValue;
260     }
261 
262 
263     /**
264      * Perform variable substitution in string <code>val</code> from the
265      * values of keys found in the system propeties.
266      * <p/>
267      * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
268      * <p/>
269      * <p>For example, if the System properties contains "key=value", then
270      * the call
271      * <pre>
272      * String s = OptionConverter.substituteVars("Value of key is ${key}.");
273      * </pre>
274      * <p/>
275      * will set the variable <code>s</code> to "Value of key is value.".
276      * <p/>
277      * <p>If no value could be found for the specified key, then the
278      * <code>props</code> parameter is searched, if the value could not
279      * be found there, then substitution defaults to the empty string.
280      * <p/>
281      * <p>For example, if system propeties contains no value for the key
282      * "inexistentKey", then the call
283      * <p/>
284      * <pre>
285      * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
286      * </pre>
287      * will set <code>s</code> to "Value of inexistentKey is []"
288      * <p/>
289      * <p>An {@link java.lang.IllegalArgumentException} is thrown if
290      * <code>val</code> contains a start delimeter "${" which is not
291      * balanced by a stop delimeter "}". </p>
292      * <p/>
293      * <p><b>Author</b> Avy Sharell</a></p>
294      *
295      * @param val The string on which variable substitution is performed.
296      * @param props The properties to use for substitution.
297      * @return The String after substitution.
298      * @throws IllegalArgumentException if <code>val</code> is malformed.
299      */
300     public static String substVars(String val, Properties props) throws
301         IllegalArgumentException {
302 
303         StringBuilder sbuf = new StringBuilder();
304 
305         int i = 0;
306         int j;
307         int k;
308 
309         while (true) {
310             j = val.indexOf(DELIM_START, i);
311             if (j == -1) {
312                 // no more variables
313                 if (i == 0) { // this is a simple string
314                     return val;
315                 } else { // add the tail string which contails no variables and return the result.
316                     sbuf.append(val.substring(i, val.length()));
317                     return sbuf.toString();
318                 }
319             } else {
320                 sbuf.append(val.substring(i, j));
321                 k = val.indexOf(DELIM_STOP, j);
322                 if (k == -1) {
323                     throw new IllegalArgumentException('"' + val +
324                         "\" has no closing brace. Opening brace at position " + j
325                         + '.');
326                 } else {
327                     j += DELIM_START_LEN;
328                     String key = val.substring(j, k);
329                     // first try in System properties
330                     String replacement = getSystemProperty(key, null);
331                     // then try props parameter
332                     if (replacement == null && props != null) {
333                         replacement = props.getProperty(key);
334                     }
335 
336                     if (replacement != null) {
337                         // Do variable substitution on the replacement string
338                         // such that we can solve "Hello ${x2}" as "Hello p1"
339                         // the where the properties are
340                         // x1=p1
341                         // x2=${x1}
342                         String recursiveReplacement = substVars(replacement, props);
343                         sbuf.append(recursiveReplacement);
344                     }
345                     i = k + DELIM_STOP_LEN;
346                 }
347             }
348         }
349     }
350 }