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  package org.apache.logging.log4j.core.config.plugins.convert;
    18
    19  import java.lang.reflect.ParameterizedType;
    20  import java.lang.reflect.Type;
    21  import java.util.Collection;
    22  import java.util.Map;
    23  import java.util.UnknownFormatConversionException;
    24  import java.util.concurrent.ConcurrentHashMap;
    25  import java.util.concurrent.ConcurrentMap;
    26
    27  import org.apache.logging.log4j.Logger;
    28  import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
    29  import org.apache.logging.log4j.core.config.plugins.util.PluginType;
    30  import org.apache.logging.log4j.core.util.Assert;
    31  import org.apache.logging.log4j.core.util.ReflectionUtil;
    32  import org.apache.logging.log4j.core.util.TypeUtil;
    33  import org.apache.logging.log4j.status.StatusLogger;
    34
    35  /**
    36   * Registry for {@link TypeConverter} plugins.
    37   *
    38   * @since 2.1
    39   */
    40  public class TypeConverterRegistry {
    41
    42      private static final Logger LOGGER = StatusLogger.getLogger();
    43      private static volatile TypeConverterRegistry INSTANCE;
    44      private static final Object INSTANCE_LOCK = new Object();
    45
    46      private final ConcurrentMap<Type, TypeConverter<?>> registry = new ConcurrentHashMap<Type, TypeConverter<?>>();
    47
    48      /**
    49       * Gets the singleton instance of the TypeConverterRegistry.
    50       *
    51       * @return the singleton instance.
    52       */
    53      public static TypeConverterRegistry getInstance() {
    54          TypeConverterRegistry result = INSTANCE;
    55          if (result == null) {
    56              synchronized (INSTANCE_LOCK) {
    57                  result = INSTANCE;
    58                  if (result == null) {
    59                      INSTANCE = result = new TypeConverterRegistry();
    60                  }
    61              }
    62          }
    63          return result;
    64      }
    65
    66      /**
    67       * Finds a {@link TypeConverter} for the given {@link Type}, falling back to an assignment-compatible TypeConverter
    68       * if none exist for the given type. That is, if the given Type does not have a TypeConverter, but another Type
    69       * which can be assigned to the given Type <em>does</em> have a TypeConverter, then that TypeConverter will be
    70       * used and registered.
    71       *
    72       * @param type the Type to find a TypeConverter for (must not be {@code null}).
    73       * @return a TypeConverter for the given Type.
    74       * @throws UnknownFormatConversionException if no TypeConverter can be found for the given type.
    75       */
    76      public TypeConverter<?> findCompatibleConverter(final Type type) {
    77          Assert.requireNonNull(type, "No type was provided");
    78          final TypeConverter<?> primary = registry.get(type);
    79          // cached type converters
    80          if (primary != null) {
    81              return primary;
    82          }
    83          // dynamic enum support
    84          if (type instanceof Class<?>) {
    85              final Class<?> clazz = (Class<?>) type;
    86              if (clazz.isEnum()) {
    87                  @SuppressWarnings({"unchecked","rawtypes"})
    88                  final EnumConverter<? extends Enum> converter = new EnumConverter(clazz.asSubclass(Enum.class));
    89                  registry.putIfAbsent(type, converter);
    90                  return converter;
    91              }
    92          }
    93          // look for compatible converters
    94          for (final Map.Entry<Type, TypeConverter<?>> entry : registry.entrySet()) {
    95              final Type key = entry.getKey();
    96              if (TypeUtil.isAssignable(type, key)) {
    97                  LOGGER.debug("Found compatible TypeConverter<{}> for type [{}].", key, type);
    98                  final TypeConverter<?> value = entry.getValue();
    99                  registry.putIfAbsent(type, value);
    100                 return value;
    101             }
    102         }
    103         throw new UnknownFormatConversionException(type.toString());
    104     }
    105
    106     private TypeConverterRegistry() {
    107         LOGGER.debug("TypeConverterRegistry initializing.");
    108         final PluginManager manager = new PluginManager(TypeConverters.CATEGORY);
    109         manager.collectPlugins();
    110         loadKnownTypeConverters(manager.getPlugins().values());
    111         registerPrimitiveTypes();
    112     }
    113
    114     private void loadKnownTypeConverters(final Collection<PluginType<?>> knownTypes) {
    115         for (final PluginType<?> knownType : knownTypes) {
    116             final Class<?> clazz = knownType.getPluginClass();
    117             if (TypeConverter.class.isAssignableFrom(clazz)) {
    118                 @SuppressWarnings("rawtypes")
    119                 final Class<? extends TypeConverter> pluginClass =  clazz.asSubclass(TypeConverter.class);
    120                 final Type conversionType = getTypeConverterSupportedType(pluginClass);
    121                 final TypeConverter<?> converter = ReflectionUtil.instantiate(pluginClass);
    122                 if (registry.putIfAbsent(conversionType, converter) != null) {
    123                     LOGGER.warn("Found a TypeConverter [{}] for type [{}] that already exists.", converter,
    124                         conversionType);
    125                 }
    126             }
    127         }
    128     }
    129
    130     private static Type getTypeConverterSupportedType(@SuppressWarnings("rawtypes") final Class<? extends TypeConverter> typeConverterClass) {
    131         for (final Type type : typeConverterClass.getGenericInterfaces()) {
    132             if (type instanceof ParameterizedType) {
    133                 final ParameterizedType pType = (ParameterizedType) type;
    134                 if (TypeConverter.class.equals(pType.getRawType())) {
    135                     // TypeConverter<T> has only one type argument (T), so return that
    136                     return pType.getActualTypeArguments()[0];
    137                 }
    138             }
    139         }
    140         return Void.TYPE;
    141     }
    142
    143     private void registerPrimitiveTypes() {
    144         registerTypeAlias(Boolean.class, Boolean.TYPE);
    145         registerTypeAlias(Byte.class, Byte.TYPE);
    146         registerTypeAlias(Character.class, Character.TYPE);
    147         registerTypeAlias(Double.class, Double.TYPE);
    148         registerTypeAlias(Float.class, Float.TYPE);
    149         registerTypeAlias(Integer.class, Integer.TYPE);
    150         registerTypeAlias(Long.class, Long.TYPE);
    151         registerTypeAlias(Short.class, Short.TYPE);
    152     }
    153
    154     private void registerTypeAlias(final Type knownType, final Type aliasType) {
    155         registry.putIfAbsent(aliasType, registry.get(knownType));
    156     }
    157
    158 }