001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.logging.log4j.core.config.plugins.util; 019 020 import java.io.File; 021 import java.math.BigDecimal; 022 import java.math.BigInteger; 023 import java.net.MalformedURLException; 024 import java.net.URI; 025 import java.net.URISyntaxException; 026 import java.net.URL; 027 import java.nio.charset.Charset; 028 import java.security.Provider; 029 import java.security.Security; 030 import java.util.Map; 031 import java.util.concurrent.ConcurrentHashMap; 032 import java.util.regex.Pattern; 033 034 import javax.xml.bind.DatatypeConverter; 035 036 import org.apache.logging.log4j.Level; 037 import org.apache.logging.log4j.Logger; 038 import org.apache.logging.log4j.core.Filter; 039 import org.apache.logging.log4j.core.layout.HtmlLayout; 040 import org.apache.logging.log4j.core.net.Facility; 041 import org.apache.logging.log4j.core.net.Protocol; 042 import org.apache.logging.log4j.core.util.Assert; 043 import org.apache.logging.log4j.core.util.Loader; 044 import org.apache.logging.log4j.status.StatusLogger; 045 import org.apache.logging.log4j.util.EnglishEnums; 046 047 /** 048 * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find 049 * registered TypeConverters. 050 */ 051 public final class TypeConverters { 052 053 // TODO: this could probably be combined with the usual plugin architecture instead 054 055 /** 056 * Parses a {@link String} into a {@link BigDecimal}. 057 */ 058 public static class BigDecimalConverter implements TypeConverter<BigDecimal> { 059 @Override 060 public BigDecimal convert(final String s) { 061 return new BigDecimal(s); 062 } 063 } 064 065 /** 066 * Parses a {@link String} into a {@link BigInteger}. 067 */ 068 public static class BigIntegerConverter implements TypeConverter<BigInteger> { 069 @Override 070 public BigInteger convert(final String s) { 071 return new BigInteger(s); 072 } 073 } 074 075 /** 076 * Converts a {@link String} into a {@link Boolean}. 077 */ 078 public static class BooleanConverter implements TypeConverter<Boolean> { 079 @Override 080 public Boolean convert(final String s) { 081 return Boolean.valueOf(s); 082 } 083 } 084 085 /** 086 * Converts a {@link String} into a {@code byte[]}. 087 * 088 * The supported formats are: 089 * <ul> 090 * <li>0x0123456789ABCDEF</li> 091 * <li>Base64:ABase64String</li> 092 * <li>String</li> 093 * </ul> 094 */ 095 public static class ByteArrayConverter implements TypeConverter<byte[]> { 096 097 private static final String PREFIX_0x = "0x"; 098 private static final String PREFIX_BASE64 = "Base64:"; 099 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 }