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  
18  package org.apache.logging.log4j.core.config.plugins.convert;
19  
20  import java.io.File;
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.net.InetAddress;
24  import java.net.MalformedURLException;
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.nio.charset.Charset;
29  import java.nio.file.Path;
30  import java.nio.file.Paths;
31  import java.security.Provider;
32  import java.security.Security;
33  import java.util.UUID;
34  import java.util.regex.Pattern;
35  
36  import org.apache.logging.log4j.Level;
37  import org.apache.logging.log4j.Logger;
38  import org.apache.logging.log4j.core.appender.rolling.action.Duration;
39  import org.apache.logging.log4j.core.config.plugins.Plugin;
40  import org.apache.logging.log4j.core.util.CronExpression;
41  import org.apache.logging.log4j.status.StatusLogger;
42  import org.apache.logging.log4j.util.LoaderUtil;
43  
44  /**
45   * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find
46   * registered TypeConverters.
47   *
48   * @since 2.1 Moved to the {@code convert} package.
49   */
50  public final class TypeConverters {
51  
52      /**
53       * The {@link Plugin#category() Plugin Category} to use for {@link TypeConverter} plugins.
54       *
55       * @since 2.1
56       */
57      public static final String CATEGORY = "TypeConverter";
58  
59      /**
60       * Parses a {@link String} into a {@link BigDecimal}.
61       */
62      @Plugin(name = "BigDecimal", category = CATEGORY)
63      public static class BigDecimalConverter implements TypeConverter<BigDecimal> {
64          @Override
65          public BigDecimal convert(final String s) {
66              return new BigDecimal(s);
67          }
68      }
69  
70      /**
71       * Parses a {@link String} into a {@link BigInteger}.
72       */
73      @Plugin(name = "BigInteger", category = CATEGORY)
74      public static class BigIntegerConverter implements TypeConverter<BigInteger> {
75          @Override
76          public BigInteger convert(final String s) {
77              return new BigInteger(s);
78          }
79      }
80  
81      /**
82       * Converts a {@link String} into a {@link Boolean}.
83       */
84      @Plugin(name = "Boolean", category = CATEGORY)
85      public static class BooleanConverter implements TypeConverter<Boolean> {
86          @Override
87          public Boolean convert(final String s) {
88              return Boolean.valueOf(s);
89          }
90      }
91  
92      /**
93       * Converts a {@link String} into a {@code byte[]}.
94       *
95       * The supported formats are:
96       * <ul>
97       * <li>0x0123456789ABCDEF</li>
98       * <li>Base64:ABase64String</li>
99       * <li>String using {@link Charset#defaultCharset()} [TODO Should this be UTF-8 instead?]</li>
100      * </ul>
101      */
102     @Plugin(name = "ByteArray", category = CATEGORY)
103     public static class ByteArrayConverter implements TypeConverter<byte[]> {
104 
105         private static final String PREFIX_0x = "0x";
106         private static final String PREFIX_BASE64 = "Base64:";
107 
108         @Override
109         public byte[] convert(final String value) {
110             byte[] bytes;
111             if (value == null || value.isEmpty()) {
112                 bytes = new byte[0];
113             } else if (value.startsWith(PREFIX_BASE64)) {
114                 final String lexicalXSDBase64Binary = value.substring(PREFIX_BASE64.length());
115                 bytes = Base64Converter.parseBase64Binary(lexicalXSDBase64Binary);
116             } else if (value.startsWith(PREFIX_0x)) {
117                 final String lexicalXSDHexBinary = value.substring(PREFIX_0x.length());
118                 bytes = HexConverter.parseHexBinary(lexicalXSDHexBinary);
119             } else {
120                 bytes = value.getBytes(Charset.defaultCharset());
121             }
122             return bytes;
123         }
124     }
125 
126     /**
127      * Converts a {@link String} into a {@link Byte}.
128      */
129     @Plugin(name = "Byte", category = CATEGORY)
130     public static class ByteConverter implements TypeConverter<Byte> {
131         @Override
132         public Byte convert(final String s) {
133             return Byte.valueOf(s);
134         }
135     }
136 
137     /**
138      * Converts a {@link String} into a {@link Character}.
139      */
140     @Plugin(name = "Character", category = CATEGORY)
141     public static class CharacterConverter implements TypeConverter<Character> {
142         @Override
143         public Character convert(final String s) {
144             if (s.length() != 1) {
145                 throw new IllegalArgumentException("Character string must be of length 1: " + s);
146             }
147             return Character.valueOf(s.toCharArray()[0]);
148         }
149     }
150 
151     /**
152      * Converts a {@link String} into a {@code char[]}.
153      */
154     @Plugin(name = "CharacterArray", category = CATEGORY)
155     public static class CharArrayConverter implements TypeConverter<char[]> {
156         @Override
157         public char[] convert(final String s) {
158             return s.toCharArray();
159         }
160     }
161 
162     /**
163      * Converts a {@link String} into a {@link Charset}.
164      */
165     @Plugin(name = "Charset", category = CATEGORY)
166     public static class CharsetConverter implements TypeConverter<Charset> {
167         @Override
168         public Charset convert(final String s) {
169             return Charset.forName(s);
170         }
171     }
172 
173     /**
174      * Converts a {@link String} into a {@link Class}.
175      */
176     @Plugin(name = "Class", category = CATEGORY)
177     public static class ClassConverter implements TypeConverter<Class<?>> {
178         @Override
179         public Class<?> convert(final String s) throws ClassNotFoundException {
180             switch (s.toLowerCase()) {
181                 case "boolean":
182                     return boolean.class;
183                 case "byte":
184                     return byte.class;
185                 case "char":
186                     return char.class;
187                 case "double":
188                     return double.class;
189                 case "float":
190                     return float.class;
191                 case "int":
192                     return int.class;
193                 case "long":
194                     return long.class;
195                 case "short":
196                     return short.class;
197                 case "void":
198                     return void.class;
199                 default:
200                     return LoaderUtil.loadClass(s);
201             }
202 
203         }
204     }
205 
206     @Plugin(name = "CronExpression", category = CATEGORY)
207     public static class CronExpressionConverter implements TypeConverter<CronExpression> {
208         @Override
209         public CronExpression convert(final String s) throws Exception {
210             return new CronExpression(s);
211         }
212     }
213 
214     /**
215      * Converts a {@link String} into a {@link Double}.
216      */
217     @Plugin(name = "Double", category = CATEGORY)
218     public static class DoubleConverter implements TypeConverter<Double> {
219         @Override
220         public Double convert(final String s) {
221             return Double.valueOf(s);
222         }
223     }
224 
225     /**
226      * Converts a {@link String} into a {@link Duration}.
227      * @since 2.5
228      */
229     @Plugin(name = "Duration", category = CATEGORY)
230     public static class DurationConverter implements TypeConverter<Duration> {
231         @Override
232         public Duration convert(final String s) {
233             return Duration.parse(s);
234         }
235     }
236 
237     /**
238      * Converts a {@link String} into a {@link File}.
239      */
240     @Plugin(name = "File", category = CATEGORY)
241     public static class FileConverter implements TypeConverter<File> {
242         @Override
243         public File convert(final String s) {
244             return new File(s);
245         }
246     }
247 
248     /**
249      * Converts a {@link String} into a {@link Float}.
250      */
251     @Plugin(name = "Float", category = CATEGORY)
252     public static class FloatConverter implements TypeConverter<Float> {
253         @Override
254         public Float convert(final String s) {
255             return Float.valueOf(s);
256         }
257     }
258 
259     /**
260      * Converts a {@link String} into an {@link InetAddress}.
261      */
262     @Plugin(name = "InetAddress", category = CATEGORY)
263     public static class InetAddressConverter implements TypeConverter<InetAddress> {
264         @Override
265         public InetAddress convert(final String s) throws Exception {
266             return InetAddress.getByName(s);
267         }
268     }
269 
270     /**
271      * Converts a {@link String} into a {@link Integer}.
272      */
273     @Plugin(name = "Integer", category = CATEGORY)
274     public static class IntegerConverter implements TypeConverter<Integer> {
275         @Override
276         public Integer convert(final String s) {
277             return Integer.valueOf(s);
278         }
279     }
280 
281     /**
282      * Converts a {@link String} into a Log4j {@link Level}. Returns {@code null} for invalid level names.
283      */
284     @Plugin(name = "Level", category = CATEGORY)
285     public static class LevelConverter implements TypeConverter<Level> {
286         @Override
287         public Level convert(final String s) {
288             return Level.valueOf(s);
289         }
290     }
291 
292     /**
293      * Converts a {@link String} into a {@link Long}.
294      */
295     @Plugin(name = "Long", category = CATEGORY)
296     public static class LongConverter implements TypeConverter<Long> {
297         @Override
298         public Long convert(final String s) {
299             return Long.valueOf(s);
300         }
301     }
302 
303     /**
304      * Converts a {@link String} into a {@link Path}.
305      * @since 2.8
306      */
307     @Plugin(name = "Path", category = CATEGORY)
308     public static class PathConverter implements TypeConverter<Path> {
309         @Override
310         public Path convert(final String s) throws Exception {
311             return Paths.get(s);
312         }
313     }
314 
315     /**
316      * Converts a {@link String} into a {@link Pattern}.
317      */
318     @Plugin(name = "Pattern", category = CATEGORY)
319     public static class PatternConverter implements TypeConverter<Pattern> {
320         @Override
321         public Pattern convert(final String s) {
322             return Pattern.compile(s);
323         }
324     }
325 
326     /**
327      * Converts a {@link String} into a {@link Provider}.
328      */
329     @Plugin(name = "SecurityProvider", category = CATEGORY)
330     public static class SecurityProviderConverter implements TypeConverter<Provider> {
331         @Override
332         public Provider convert(final String s) {
333             return Security.getProvider(s);
334         }
335     }
336 
337     /**
338      * Converts a {@link String} into a {@link Short}.
339      */
340     @Plugin(name = "Short", category = CATEGORY)
341     public static class ShortConverter implements TypeConverter<Short> {
342         @Override
343         public Short convert(final String s) {
344             return Short.valueOf(s);
345         }
346     }
347 
348     /**
349      * Returns the given {@link String}, no conversion takes place.
350      */
351     @Plugin(name = "String", category = CATEGORY)
352     public static class StringConverter implements TypeConverter<String> {
353         @Override
354         public String convert(final String s) {
355             return s;
356         }
357     }
358 
359     /**
360      * Converts a {@link String} into a {@link URI}.
361      */
362     @Plugin(name = "URI", category = CATEGORY)
363     public static class UriConverter implements TypeConverter<URI> {
364         @Override
365         public URI convert(final String s) throws URISyntaxException {
366             return new URI(s);
367         }
368     }
369 
370     /**
371      * Converts a {@link String} into a {@link URL}.
372      */
373     @Plugin(name = "URL", category = CATEGORY)
374     public static class UrlConverter implements TypeConverter<URL> {
375         @Override
376         public URL convert(final String s) throws MalformedURLException {
377             return new URL(s);
378         }
379     }
380 
381     /**
382      * Converts a {@link String} into a {@link UUID}.
383      * @since 2.8
384      */
385     @Plugin(name = "UUID", category = CATEGORY)
386     public static class UuidConverter implements TypeConverter<UUID> {
387         @Override
388         public UUID convert(final String s) throws Exception {
389             return UUID.fromString(s);
390         }
391     }
392 
393     /**
394      * Converts a String to a given class if a TypeConverter is available for that class. Falls back to the provided
395      * default value if the conversion is unsuccessful. However, if the default value is <em>also</em> invalid, then
396      * {@code null} is returned (along with a nasty status log message).
397      *
398      * @param s
399      *        the string to convert
400      * @param clazz
401      *        the class to try to convert the string to
402      * @param defaultValue
403      *        the fallback object to use if the conversion is unsuccessful
404      * @return the converted object which may be {@code null} if the string is invalid for the given type
405      * @throws NullPointerException
406      *         if {@code clazz} is {@code null}
407      * @throws IllegalArgumentException
408      *         if no TypeConverter exists for the given class
409      */
410     public static <T> T convert(final String s, final Class<? extends T> clazz, final Object defaultValue) {
411         @SuppressWarnings("unchecked")
412         final TypeConverter<T> converter = (TypeConverter<T>) TypeConverterRegistry.getInstance().findCompatibleConverter(clazz);
413         if (s == null) {
414             // don't debug print here, resulting output is hard to understand
415             // LOGGER.debug("Null string given to convert. Using default [{}].", defaultValue);
416             return parseDefaultValue(converter, defaultValue);
417         }
418         try {
419             return converter.convert(s);
420         } catch (final Exception e) {
421             LOGGER.warn("Error while converting string [{}] to type [{}]. Using default value [{}].", s, clazz,
422                     defaultValue, e);
423             return parseDefaultValue(converter, defaultValue);
424         }
425     }
426 
427     @SuppressWarnings("unchecked")
428     private static <T> T parseDefaultValue(final TypeConverter<T> converter, final Object defaultValue) {
429         if (defaultValue == null) {
430             return null;
431         }
432         if (!(defaultValue instanceof String)) {
433             return (T) defaultValue;
434         }
435         try {
436             return converter.convert((String) defaultValue);
437         } catch (final Exception e) {
438             LOGGER.debug("Can't parse default value [{}] for type [{}].", defaultValue, converter.getClass(), e);
439             return null;
440         }
441     }
442 
443     private static final Logger LOGGER = StatusLogger.getLogger();
444 
445 }