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