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.Constants; 043import org.apache.logging.log4j.util.LoaderUtil; 044 045/** 046 * Collection of basic TypeConverter implementations. May be used to register additional TypeConverters or find 047 * registered TypeConverters. 048 * 049 * @since 2.1 Moved to the {@code convert} package. 050 */ 051public final class TypeConverters { 052 053 /** 054 * The {@link Plugin#category() Plugin Category} to use for {@link TypeConverter} plugins. 055 * 056 * @since 2.1 057 */ 058 public static final String CATEGORY = "TypeConverter"; 059 060 /** 061 * Parses a {@link String} into a {@link BigDecimal}. 062 */ 063 @Plugin(name = "BigDecimal", category = CATEGORY) 064 public static class BigDecimalConverter implements TypeConverter<BigDecimal> { 065 @Override 066 public BigDecimal convert(final String s) { 067 return new BigDecimal(s); 068 } 069 } 070 071 /** 072 * Parses a {@link String} into a {@link BigInteger}. 073 */ 074 @Plugin(name = "BigInteger", category = CATEGORY) 075 public static class BigIntegerConverter implements TypeConverter<BigInteger> { 076 @Override 077 public BigInteger convert(final String s) { 078 return new BigInteger(s); 079 } 080 } 081 082 /** 083 * Converts a {@link String} into a {@link Boolean}. 084 */ 085 @Plugin(name = "Boolean", category = CATEGORY) 086 public static class BooleanConverter implements TypeConverter<Boolean> { 087 @Override 088 public Boolean convert(final String s) { 089 return Boolean.valueOf(s); 090 } 091 } 092 093 /** 094 * Converts a {@link String} into a {@code byte[]}. 095 * 096 * The supported formats are: 097 * <ul> 098 * <li>0x0123456789ABCDEF</li> 099 * <li>Base64:ABase64String</li> 100 * <li>String using {@link Charset#defaultCharset()} [TODO Should this be UTF-8 instead?]</li> 101 * </ul> 102 */ 103 @Plugin(name = "ByteArray", category = CATEGORY) 104 public static class ByteArrayConverter implements TypeConverter<byte[]> { 105 106 private static final String PREFIX_0x = "0x"; 107 private static final String PREFIX_BASE64 = "Base64:"; 108 109 @Override 110 public byte[] convert(final String value) { 111 byte[] bytes; 112 if (value == null || value.isEmpty()) { 113 bytes = Constants.EMPTY_BYTE_ARRAY; 114 } else if (value.startsWith(PREFIX_BASE64)) { 115 final String lexicalXSDBase64Binary = value.substring(PREFIX_BASE64.length()); 116 bytes = Base64Converter.parseBase64Binary(lexicalXSDBase64Binary); 117 } else if (value.startsWith(PREFIX_0x)) { 118 final String lexicalXSDHexBinary = value.substring(PREFIX_0x.length()); 119 bytes = HexConverter.parseHexBinary(lexicalXSDHexBinary); 120 } else { 121 bytes = value.getBytes(Charset.defaultCharset()); 122 } 123 return bytes; 124 } 125 } 126 127 /** 128 * Converts a {@link String} into a {@link Byte}. 129 */ 130 @Plugin(name = "Byte", category = CATEGORY) 131 public static class ByteConverter implements TypeConverter<Byte> { 132 @Override 133 public Byte convert(final String s) { 134 return Byte.valueOf(s); 135 } 136 } 137 138 /** 139 * Converts a {@link String} into a {@link Character}. 140 */ 141 @Plugin(name = "Character", category = CATEGORY) 142 public static class CharacterConverter implements TypeConverter<Character> { 143 @Override 144 public Character convert(final String s) { 145 if (s.length() != 1) { 146 throw new IllegalArgumentException("Character string must be of length 1: " + s); 147 } 148 return Character.valueOf(s.toCharArray()[0]); 149 } 150 } 151 152 /** 153 * Converts a {@link String} into a {@code char[]}. 154 */ 155 @Plugin(name = "CharacterArray", category = CATEGORY) 156 public static class CharArrayConverter implements TypeConverter<char[]> { 157 @Override 158 public char[] convert(final String s) { 159 return s.toCharArray(); 160 } 161 } 162 163 /** 164 * Converts a {@link String} into a {@link Charset}. 165 */ 166 @Plugin(name = "Charset", category = CATEGORY) 167 public static class CharsetConverter implements TypeConverter<Charset> { 168 @Override 169 public Charset convert(final String s) { 170 return Charset.forName(s); 171 } 172 } 173 174 /** 175 * Converts a {@link String} into a {@link Class}. 176 */ 177 @Plugin(name = "Class", category = CATEGORY) 178 public static class ClassConverter implements TypeConverter<Class<?>> { 179 @Override 180 public Class<?> convert(final String s) throws ClassNotFoundException { 181 switch (s.toLowerCase()) { 182 case "boolean": 183 return boolean.class; 184 case "byte": 185 return byte.class; 186 case "char": 187 return char.class; 188 case "double": 189 return double.class; 190 case "float": 191 return float.class; 192 case "int": 193 return int.class; 194 case "long": 195 return long.class; 196 case "short": 197 return short.class; 198 case "void": 199 return void.class; 200 default: 201 return LoaderUtil.loadClass(s); 202 } 203 204 } 205 } 206 207 @Plugin(name = "CronExpression", category = CATEGORY) 208 public static class CronExpressionConverter implements TypeConverter<CronExpression> { 209 @Override 210 public CronExpression convert(final String s) throws Exception { 211 return new CronExpression(s); 212 } 213 } 214 215 /** 216 * Converts a {@link String} into a {@link Double}. 217 */ 218 @Plugin(name = "Double", category = CATEGORY) 219 public static class DoubleConverter implements TypeConverter<Double> { 220 @Override 221 public Double convert(final String s) { 222 return Double.valueOf(s); 223 } 224 } 225 226 /** 227 * Converts a {@link String} into a {@link Duration}. 228 * @since 2.5 229 */ 230 @Plugin(name = "Duration", category = CATEGORY) 231 public static class DurationConverter implements TypeConverter<Duration> { 232 @Override 233 public Duration convert(final String s) { 234 return Duration.parse(s); 235 } 236 } 237 238 /** 239 * Converts a {@link String} into a {@link File}. 240 */ 241 @Plugin(name = "File", category = CATEGORY) 242 public static class FileConverter implements TypeConverter<File> { 243 @Override 244 public File convert(final String s) { 245 return new File(s); 246 } 247 } 248 249 /** 250 * Converts a {@link String} into a {@link Float}. 251 */ 252 @Plugin(name = "Float", category = CATEGORY) 253 public static class FloatConverter implements TypeConverter<Float> { 254 @Override 255 public Float convert(final String s) { 256 return Float.valueOf(s); 257 } 258 } 259 260 /** 261 * Converts a {@link String} into an {@link InetAddress}. 262 */ 263 @Plugin(name = "InetAddress", category = CATEGORY) 264 public static class InetAddressConverter implements TypeConverter<InetAddress> { 265 @Override 266 public InetAddress convert(final String s) throws Exception { 267 return InetAddress.getByName(s); 268 } 269 } 270 271 /** 272 * Converts a {@link String} into a {@link Integer}. 273 */ 274 @Plugin(name = "Integer", category = CATEGORY) 275 public static class IntegerConverter implements TypeConverter<Integer> { 276 @Override 277 public Integer convert(final String s) { 278 return Integer.valueOf(s); 279 } 280 } 281 282 /** 283 * Converts a {@link String} into a Log4j {@link Level}. Returns {@code null} for invalid level names. 284 */ 285 @Plugin(name = "Level", category = CATEGORY) 286 public static class LevelConverter implements TypeConverter<Level> { 287 @Override 288 public Level convert(final String s) { 289 return Level.valueOf(s); 290 } 291 } 292 293 /** 294 * Converts a {@link String} into a {@link Long}. 295 */ 296 @Plugin(name = "Long", category = CATEGORY) 297 public static class LongConverter implements TypeConverter<Long> { 298 @Override 299 public Long convert(final String s) { 300 return Long.valueOf(s); 301 } 302 } 303 304 /** 305 * Converts a {@link String} into a {@link Path}. 306 * @since 2.8 307 */ 308 @Plugin(name = "Path", category = CATEGORY) 309 public static class PathConverter implements TypeConverter<Path> { 310 @Override 311 public Path convert(final String s) throws Exception { 312 return Paths.get(s); 313 } 314 } 315 316 /** 317 * Converts a {@link String} into a {@link Pattern}. 318 */ 319 @Plugin(name = "Pattern", category = CATEGORY) 320 public static class PatternConverter implements TypeConverter<Pattern> { 321 @Override 322 public Pattern convert(final String s) { 323 return Pattern.compile(s); 324 } 325 } 326 327 /** 328 * Converts a {@link String} into a {@link Provider}. 329 */ 330 @Plugin(name = "SecurityProvider", category = CATEGORY) 331 public static class SecurityProviderConverter implements TypeConverter<Provider> { 332 @Override 333 public Provider convert(final String s) { 334 return Security.getProvider(s); 335 } 336 } 337 338 /** 339 * Converts a {@link String} into a {@link Short}. 340 */ 341 @Plugin(name = "Short", category = CATEGORY) 342 public static class ShortConverter implements TypeConverter<Short> { 343 @Override 344 public Short convert(final String s) { 345 return Short.valueOf(s); 346 } 347 } 348 349 /** 350 * Returns the given {@link String}, no conversion takes place. 351 */ 352 @Plugin(name = "String", category = CATEGORY) 353 public static class StringConverter implements TypeConverter<String> { 354 @Override 355 public String convert(final String s) { 356 return s; 357 } 358 } 359 360 /** 361 * Converts a {@link String} into a {@link URI}. 362 */ 363 @Plugin(name = "URI", category = CATEGORY) 364 public static class UriConverter implements TypeConverter<URI> { 365 @Override 366 public URI convert(final String s) throws URISyntaxException { 367 return new URI(s); 368 } 369 } 370 371 /** 372 * Converts a {@link String} into a {@link URL}. 373 */ 374 @Plugin(name = "URL", category = CATEGORY) 375 public static class UrlConverter implements TypeConverter<URL> { 376 @Override 377 public URL convert(final String s) throws MalformedURLException { 378 return new URL(s); 379 } 380 } 381 382 /** 383 * Converts a {@link String} into a {@link UUID}. 384 * @since 2.8 385 */ 386 @Plugin(name = "UUID", category = CATEGORY) 387 public static class UuidConverter implements TypeConverter<UUID> { 388 @Override 389 public UUID convert(final String s) throws Exception { 390 return UUID.fromString(s); 391 } 392 } 393 394 /** 395 * Converts a String to a given class if a TypeConverter is available for that class. Falls back to the provided 396 * default value if the conversion is unsuccessful. However, if the default value is <em>also</em> invalid, then 397 * {@code null} is returned (along with a nasty status log message). 398 * 399 * @param s 400 * the string to convert 401 * @param clazz 402 * the class to try to convert the string to 403 * @param defaultValue 404 * the fallback object to use if the conversion is unsuccessful 405 * @return the converted object which may be {@code null} if the string is invalid for the given type 406 * @throws NullPointerException 407 * if {@code clazz} is {@code null} 408 * @throws IllegalArgumentException 409 * if no TypeConverter exists for the given class 410 */ 411 public static <T> T convert(final String s, final Class<? extends T> clazz, final Object defaultValue) { 412 @SuppressWarnings("unchecked") 413 final TypeConverter<T> converter = (TypeConverter<T>) TypeConverterRegistry.getInstance().findCompatibleConverter(clazz); 414 if (s == null) { 415 // don't debug print here, resulting output is hard to understand 416 // LOGGER.debug("Null string given to convert. Using default [{}].", defaultValue); 417 return parseDefaultValue(converter, defaultValue); 418 } 419 try { 420 return converter.convert(s); 421 } catch (final Exception e) { 422 LOGGER.warn("Error while converting string [{}] to type [{}]. Using default value [{}].", s, clazz, 423 defaultValue, e); 424 return parseDefaultValue(converter, defaultValue); 425 } 426 } 427 428 @SuppressWarnings("unchecked") 429 private static <T> T parseDefaultValue(final TypeConverter<T> converter, final Object defaultValue) { 430 if (defaultValue == null) { 431 return null; 432 } 433 if (!(defaultValue instanceof String)) { 434 return (T) defaultValue; 435 } 436 try { 437 return converter.convert((String) defaultValue); 438 } catch (final Exception e) { 439 LOGGER.debug("Can't parse default value [{}] for type [{}].", defaultValue, converter.getClass(), e); 440 return null; 441 } 442 } 443 444 private static final Logger LOGGER = StatusLogger.getLogger(); 445 446}