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