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 package org.apache.logging.log4j.core.config.plugins.convert; 018 019 import java.lang.reflect.ParameterizedType; 020 import java.lang.reflect.Type; 021 import java.util.Collection; 022 import java.util.Map; 023 import java.util.UnknownFormatConversionException; 024 import java.util.concurrent.ConcurrentHashMap; 025 import java.util.concurrent.ConcurrentMap; 026 027 import org.apache.logging.log4j.Logger; 028 import org.apache.logging.log4j.core.config.plugins.util.PluginManager; 029 import org.apache.logging.log4j.core.config.plugins.util.PluginType; 030 import org.apache.logging.log4j.core.util.Assert; 031 import org.apache.logging.log4j.core.util.ReflectionUtil; 032 import org.apache.logging.log4j.core.util.TypeUtil; 033 import org.apache.logging.log4j.status.StatusLogger; 034 035 /** 036 * Registry for {@link TypeConverter} plugins. 037 * 038 * @since 2.1 039 */ 040 public class TypeConverterRegistry { 041 042 private static final Logger LOGGER = StatusLogger.getLogger(); 043 private static volatile TypeConverterRegistry INSTANCE; 044 private static final Object INSTANCE_LOCK = new Object(); 045 046 private final ConcurrentMap<Type, TypeConverter<?>> registry = new ConcurrentHashMap<Type, TypeConverter<?>>(); 047 048 /** 049 * Gets the singleton instance of the TypeConverterRegistry. 050 * 051 * @return the singleton instance. 052 */ 053 public static TypeConverterRegistry getInstance() { 054 TypeConverterRegistry result = INSTANCE; 055 if (result == null) { 056 synchronized (INSTANCE_LOCK) { 057 result = INSTANCE; 058 if (result == null) { 059 INSTANCE = result = new TypeConverterRegistry(); 060 } 061 } 062 } 063 return result; 064 } 065 066 /** 067 * Finds a {@link TypeConverter} for the given {@link Type}, falling back to an assignment-compatible TypeConverter 068 * if none exist for the given type. That is, if the given Type does not have a TypeConverter, but another Type 069 * which can be assigned to the given Type <em>does</em> have a TypeConverter, then that TypeConverter will be 070 * used and registered. 071 * 072 * @param type the Type to find a TypeConverter for (must not be {@code null}). 073 * @return a TypeConverter for the given Type. 074 * @throws UnknownFormatConversionException if no TypeConverter can be found for the given type. 075 */ 076 public TypeConverter<?> findCompatibleConverter(final Type type) { 077 Assert.requireNonNull(type, "No type was provided"); 078 final TypeConverter<?> primary = registry.get(type); 079 // cached type converters 080 if (primary != null) { 081 return primary; 082 } 083 // dynamic enum support 084 if (type instanceof Class<?>) { 085 final Class<?> clazz = (Class<?>) type; 086 if (clazz.isEnum()) { 087 @SuppressWarnings({"unchecked","rawtypes"}) 088 final EnumConverter<? extends Enum> converter = new EnumConverter(clazz.asSubclass(Enum.class)); 089 registry.putIfAbsent(type, converter); 090 return converter; 091 } 092 } 093 // look for compatible converters 094 for (final Map.Entry<Type, TypeConverter<?>> entry : registry.entrySet()) { 095 final Type key = entry.getKey(); 096 if (TypeUtil.isAssignable(type, key)) { 097 LOGGER.debug("Found compatible TypeConverter<{}> for type [{}].", key, type); 098 final TypeConverter<?> value = entry.getValue(); 099 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 }