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