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.util;
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.Map;
31  import java.util.concurrent.ConcurrentHashMap;
32  import java.util.regex.Pattern;
33  
34  import javax.xml.bind.DatatypeConverter;
35  
36  import org.apache.logging.log4j.Level;
37  import org.apache.logging.log4j.Logger;
38  import org.apache.logging.log4j.core.Filter;
39  import org.apache.logging.log4j.core.layout.HtmlLayout;
40  import org.apache.logging.log4j.core.net.Facility;
41  import org.apache.logging.log4j.core.net.Protocol;
42  import org.apache.logging.log4j.core.util.Assert;
43  import org.apache.logging.log4j.core.util.Loader;
44  import org.apache.logging.log4j.status.StatusLogger;
45  import org.apache.logging.log4j.util.EnglishEnums;
46  
47  /**
48   * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find
49   * registered TypeConverters.
50   */
51  public final class TypeConverters {
52  
53      // TODO: this could probably be combined with the usual plugin architecture instead
54  
55      /**
56       * Parses a {@link String} into a {@link BigDecimal}.
57       */
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      public static class BigIntegerConverter implements TypeConverter<BigInteger> {
69          @Override
70          public BigInteger convert(final String s) {
71              return new BigInteger(s);
72          }
73      }
74  
75      /**
76       * Converts a {@link String} into a {@link Boolean}.
77       */
78      public static class BooleanConverter implements TypeConverter<Boolean> {
79          @Override
80          public Boolean convert(final String s) {
81              return Boolean.valueOf(s);
82          }
83      }
84  
85      /**
86       * Converts a {@link String} into a {@code byte[]}.
87       * 
88       * The supported formats are:
89       * <ul>
90       * <li>0x0123456789ABCDEF</li>
91       * <li>Base64:ABase64String</li>
92       * <li>String</li>
93       * </ul>
94       */
95      public static class ByteArrayConverter implements TypeConverter<byte[]> {
96  
97          private static final String PREFIX_0x = "0x";
98          private static final String PREFIX_BASE64 = "Base64:";
99  
100         @Override
101         public byte[] convert(final String value) {
102             byte[] bytes;
103             if (value == null || value.isEmpty()) {
104                 bytes = new byte[0];
105             } else if (value.startsWith(PREFIX_BASE64)) {
106                 final String lexicalXSDBase64Binary = value.substring(PREFIX_BASE64.length());
107                 bytes = DatatypeConverter.parseBase64Binary(lexicalXSDBase64Binary);
108             } else if (value.startsWith(PREFIX_0x)) {
109                 final String lexicalXSDHexBinary = value.substring(PREFIX_0x.length());
110                 bytes = DatatypeConverter.parseHexBinary(lexicalXSDHexBinary);
111             } else {
112                 bytes = value.getBytes(Charset.defaultCharset());
113             }
114             return bytes;
115         }
116     }
117 
118     /**
119      * Converts a {@link String} into a {@link Byte}.
120      */
121     public static class ByteConverter implements TypeConverter<Byte> {
122         @Override
123         public Byte convert(final String s) {
124             return Byte.valueOf(s);
125         }
126     }
127 
128     /**
129      * Converts a {@link String} into a {@link Character}.
130      */
131     public static class CharacterConverter implements TypeConverter<Character> {
132         @Override
133         public Character convert(final String s) {
134             if (s.length() != 1) {
135                 throw new IllegalArgumentException("Character string must be of length 1: " + s);
136             }
137             return Character.valueOf(s.toCharArray()[0]);
138         }
139     }
140 
141     /**
142      * Converts a {@link String} into a {@code char[]}.
143      */
144     public static class CharArrayConverter implements TypeConverter<char[]> {
145         @Override
146         public char[] convert(final String s) {
147             return s.toCharArray();
148         }
149     }
150 
151     /**
152      * Converts a {@link String} into a {@link Charset}.
153      */
154     public static class CharsetConverter implements TypeConverter<Charset> {
155         @Override
156         public Charset convert(final String s) {
157             return Charset.forName(s);
158         }
159     }
160 
161     /**
162      * Converts a {@link String} into a {@link Class}.
163      */
164     public static class ClassConverter implements TypeConverter<Class<?>> {
165         @Override
166         public Class<?> convert(final String s) throws ClassNotFoundException {
167             return Loader.loadClass(s);
168         }
169     }
170 
171     /**
172      * Converts a {@link String} into a {@link Double}.
173      */
174     public static class DoubleConverter implements TypeConverter<Double> {
175         @Override
176         public Double convert(final String s) {
177             return Double.valueOf(s);
178         }
179     }
180 
181     /**
182      * Converts a {@link String} into a {@link Enum}. Returns {@code null} for invalid enum names.
183      * 
184      * @param <E>
185      *        the enum class to parse.
186      */
187     public static class EnumConverter<E extends Enum<E>> implements TypeConverter<E> {
188         private final Class<E> clazz;
189 
190         private EnumConverter(final Class<E> clazz) {
191             this.clazz = clazz;
192         }
193 
194         @Override
195         public E convert(final String s) {
196             return EnglishEnums.valueOf(clazz, s);
197         }
198     }
199 
200     /**
201      * Converts a {@link String} into a {@link File}.
202      */
203     public static class FileConverter implements TypeConverter<File> {
204         @Override
205         public File convert(final String s) {
206             return new File(s);
207         }
208     }
209 
210     /**
211      * Converts a {@link String} into a {@link Float}.
212      */
213     public static class FloatConverter implements TypeConverter<Float> {
214         @Override
215         public Float convert(final String s) {
216             return Float.valueOf(s);
217         }
218     }
219 
220     private static final class Holder {
221         private static final TypeConverters INSTANCE = new TypeConverters();
222     }
223 
224     /**
225      * Converts a {@link String} into a {@link Integer}.
226      */
227     public static class IntegerConverter implements TypeConverter<Integer> {
228         @Override
229         public Integer convert(final String s) {
230             return Integer.valueOf(s);
231         }
232     }
233 
234     /**
235      * Converts a {@link String} into a Log4j {@link Level}. Returns {@code null} for invalid level names.
236      */
237     public static class LevelConverter implements TypeConverter<Level> {
238         @Override
239         public Level convert(final String s) {
240             return Level.valueOf(s);
241         }
242     }
243 
244     /**
245      * Converts a {@link String} into a {@link Long}.
246      */
247     public static class LongConverter implements TypeConverter<Long> {
248         @Override
249         public Long convert(final String s) {
250             return Long.valueOf(s);
251         }
252     }
253 
254     /**
255      * Converts a {@link String} into a {@link Pattern}.
256      */
257     public static class PatternConverter implements TypeConverter<Pattern> {
258         @Override
259         public Pattern convert(final String s) {
260             return Pattern.compile(s);
261         }
262     }
263 
264     /**
265      * Converts a {@link String} into a {@link Pattern}.
266      */
267     public static class SecurityProviderConverter implements TypeConverter<Provider> {
268         @Override
269         public Provider convert(final String s) {
270             return Security.getProvider(s);
271         }
272     }
273 
274     /**
275      * Converts a {@link String} into a {@link Short}.
276      */
277     public static class ShortConverter implements TypeConverter<Short> {
278         @Override
279         public Short convert(final String s) {
280             return Short.valueOf(s);
281         }
282     }
283 
284     /**
285      * Returns the given {@link String}, no conversion takes place.
286      */
287     public static class StringConverter implements TypeConverter<String> {
288         @Override
289         public String convert(final String s) {
290             return s;
291         }
292     }
293 
294     /**
295      * Converts a {@link String} into a {@link URI}.
296      */
297     public static class UriConverter implements TypeConverter<URI> {
298         @Override
299         public URI convert(final String s) throws URISyntaxException {
300             return new URI(s);
301         }
302     }
303 
304     /**
305      * Converts a {@link String} into a {@link URL}.
306      */
307     public static class UrlConverter implements TypeConverter<URL> {
308         @Override
309         public URL convert(final String s) throws MalformedURLException {
310             return new URL(s);
311         }
312     }
313 
314     /**
315      * Converts a String to a given class if a TypeConverter is available for that class. Falls back to the provided
316      * default value if the conversion is unsuccessful. However, if the default value is <em>also</em> invalid, then
317      * {@code null} is returned (along with a nasty status log message).
318      * 
319      * @param s
320      *        the string to convert
321      * @param clazz
322      *        the class to try to convert the string to
323      * @param defaultValue
324      *        the fallback object to use if the conversion is unsuccessful
325      * @return the converted object which may be {@code null} if the string is invalid for the given type
326      * @throws NullPointerException
327      *         if {@code clazz} is {@code null}
328      * @throws IllegalArgumentException
329      *         if no TypeConverter exists for the given class
330      */
331     public static Object convert(final String s, final Class<?> clazz, final Object defaultValue) {
332         final TypeConverter<?> converter = findTypeConverter(Assert.requireNonNull(clazz,
333                 "No class specified to convert to."));
334         if (converter == null) {
335             throw new IllegalArgumentException("No type converter found for class: " + clazz.getName());
336         }
337         if (s == null) {
338             // don't debug print here, resulting output is hard to understand
339             //LOGGER.debug("Null string given to convert. Using default [{}].", defaultValue);
340             return parseDefaultValue(converter, defaultValue);
341         }
342         try {
343             return converter.convert(s);
344         } catch (final Exception e) {
345             LOGGER.warn("Error while converting string [{}] to type [{}]. Using default value [{}].", s, clazz,
346                     defaultValue, e);
347             return parseDefaultValue(converter, defaultValue);
348         }
349     }
350 
351     /**
352      * Locates a TypeConverter for a specified class.
353      * 
354      * @param clazz
355      *        the class to get a TypeConverter for
356      * @return the associated TypeConverter for that class or {@code null} if none could be found
357      */
358     public static TypeConverter<?> findTypeConverter(final Class<?> clazz) {
359         // TODO: what to do if there's no converter?
360         // supplementary idea: automatically add type converters for enums using EnglishEnums
361         // Idea 1: use reflection to see if the class has a static "valueOf" method and use that
362         // Idea 2: reflect on class's declared methods to see if any methods look suitable (probably too complex)
363         return Holder.INSTANCE.registry.get(clazz);
364     }
365 
366     private static Object parseDefaultValue(final TypeConverter<?> converter, final Object defaultValue) {
367         if (defaultValue == null) {
368             return null;
369         }
370         if (!(defaultValue instanceof String)) {
371             return defaultValue;
372         }
373         try {
374             return converter.convert((String) defaultValue);
375         } catch (final Exception e) {
376             LOGGER.debug("Can't parse default value [{}] for type [{}].", defaultValue, converter.getClass(), e);
377             return null;
378         }
379     }
380 
381     /**
382      * Registers a TypeConverter for a specified class. This will overwrite any existing TypeConverter that may be
383      * registered for the class.
384      * 
385      * @param clazz
386      *        the class to register the TypeConverter for
387      * @param converter
388      *        the TypeConverter to register
389      */
390     public static void registerTypeConverter(final Class<?> clazz, final TypeConverter<?> converter) {
391         Holder.INSTANCE.registry.put(clazz, converter);
392     }
393 
394     private static final Logger LOGGER = StatusLogger.getLogger();
395 
396     private final Map<Class<?>, TypeConverter<?>> registry = new ConcurrentHashMap<Class<?>, TypeConverter<?>>();
397 
398     /**
399      * Constructs default TypeConverter registry. Used solely by singleton instance.
400      */
401     private TypeConverters() {
402         // Primitive wrappers
403         registry.put(Boolean.class, new BooleanConverter());
404         registry.put(Byte.class, new ByteConverter());
405         registry.put(Character.class, new CharacterConverter());
406         registry.put(Double.class, new DoubleConverter());
407         registry.put(Float.class, new FloatConverter());
408         registry.put(Integer.class, new IntegerConverter());
409         registry.put(Long.class, new LongConverter());
410         registry.put(Short.class, new ShortConverter());
411         // Primitives
412         registry.put(boolean.class, registry.get(Boolean.class));
413         registry.put(byte.class, new ByteConverter());
414         registry.put(char[].class, new CharArrayConverter());
415         registry.put(double.class, registry.get(Double.class));
416         registry.put(float.class, registry.get(Float.class));
417         registry.put(int.class, registry.get(Integer.class));
418         registry.put(long.class, registry.get(Long.class));
419         registry.put(short.class, registry.get(Short.class));
420         // Primitive arrays
421         registry.put(byte[].class, new ByteArrayConverter());
422         registry.put(char.class, new CharacterConverter());
423         // Numbers
424         registry.put(BigInteger.class, new BigIntegerConverter());
425         registry.put(BigDecimal.class, new BigDecimalConverter());
426         // JRE
427         registry.put(String.class, new StringConverter());
428         registry.put(Charset.class, new CharsetConverter());
429         registry.put(File.class, new FileConverter());
430         registry.put(URL.class, new UrlConverter());
431         registry.put(URI.class, new UriConverter());
432         registry.put(Class.class, new ClassConverter());
433         registry.put(Pattern.class, new PatternConverter());
434         registry.put(Provider.class, new SecurityProviderConverter());
435         // Log4J
436         registry.put(Level.class, new LevelConverter());
437         registry.put(Filter.Result.class, new EnumConverter<Filter.Result>(Filter.Result.class));
438         registry.put(Facility.class, new EnumConverter<Facility>(Facility.class));
439         registry.put(Protocol.class, new EnumConverter<Protocol>(Protocol.class));
440         registry.put(HtmlLayout.FontSize.class, new EnumConverter<HtmlLayout.FontSize>(HtmlLayout.FontSize.class));
441     }
442 }