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 018package org.apache.logging.log4j.core.config.plugins.convert; 019 020import java.io.File; 021import java.math.BigDecimal; 022import java.math.BigInteger; 023import java.net.InetAddress; 024import java.net.MalformedURLException; 025import java.net.URI; 026import java.net.URISyntaxException; 027import java.net.URL; 028import java.nio.charset.Charset; 029import java.nio.file.Path; 030import java.nio.file.Paths; 031import java.security.Provider; 032import java.security.Security; 033import java.util.UUID; 034import java.util.regex.Pattern; 035 036import org.apache.logging.log4j.Level; 037import org.apache.logging.log4j.Logger; 038import org.apache.logging.log4j.core.appender.rolling.action.Duration; 039import org.apache.logging.log4j.core.config.plugins.Plugin; 040import org.apache.logging.log4j.core.util.CronExpression; 041import org.apache.logging.log4j.status.StatusLogger; 042import org.apache.logging.log4j.util.LoaderUtil; 043 044/** 045 * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find 046 * registered TypeConverters. 047 * 048 * @since 2.1 Moved to the {@code convert} package. 049 */ 050public final class TypeConverters { 051 052 /** 053 * The {@link Plugin#category() Plugin Category} to use for {@link TypeConverter} plugins. 054 * 055 * @since 2.1 056 */ 057 public static final String CATEGORY = "TypeConverter"; 058 059 /** 060 * Parses a {@link String} into a {@link BigDecimal}. 061 */ 062 @Plugin(name = "BigDecimal", category = CATEGORY) 063 public static class BigDecimalConverter implements TypeConverter<BigDecimal> { 064 @Override 065 public BigDecimal convert(final String s) { 066 return new BigDecimal(s); 067 } 068 } 069 070 /** 071 * Parses a {@link String} into a {@link BigInteger}. 072 */ 073 @Plugin(name = "BigInteger", category = CATEGORY) 074 public static class BigIntegerConverter implements TypeConverter<BigInteger> { 075 @Override 076 public BigInteger convert(final String s) { 077 return new BigInteger(s); 078 } 079 } 080 081 /** 082 * Converts a {@link String} into a {@link Boolean}. 083 */ 084 @Plugin(name = "Boolean", category = CATEGORY) 085 public static class BooleanConverter implements TypeConverter<Boolean> { 086 @Override 087 public Boolean convert(final String s) { 088 return Boolean.valueOf(s); 089 } 090 } 091 092 /** 093 * Converts a {@link String} into a {@code byte[]}. 094 * 095 * The supported formats are: 096 * <ul> 097 * <li>0x0123456789ABCDEF</li> 098 * <li>Base64:ABase64String</li> 099 * <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}