| package org.apache.logging.log4j.layout.template.json.resolver; |
| |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.plugins.util.PluginType; |
| import org.apache.logging.log4j.plugins.util.PluginUtil; |
| import org.apache.logging.log4j.status.StatusLogger; |
| |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Utility class for {@link TemplateResolverFactory}. |
| */ |
| public final class TemplateResolverFactories { |
| |
| private static final Logger LOGGER = StatusLogger.getLogger(); |
| |
| private TemplateResolverFactories() {} |
| |
| /** |
| * Populates plugins implementing |
| * {@link TemplateResolverFactory TemplateResolverFactory<V, C>}, |
| * where {@code V} and {@code C} denote the value and context class types, |
| * respectively. |
| */ |
| public static <V, C extends TemplateResolverContext<V, C>, F extends TemplateResolverFactory<V, C>> Map<String, F> populateFactoryByName( |
| final List<String> pluginPackages, |
| final Class<V> valueClass, |
| final Class<C> contextClass) { |
| |
| // Populate template resolver factories. |
| final Map<String, PluginType<?>> pluginTypeByName = |
| PluginUtil.collectPluginsByCategoryAndPackage( |
| TemplateResolverFactory.CATEGORY, |
| pluginPackages); |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug( |
| "found {} plugins of category \"{}\": {}", |
| pluginTypeByName.size(), |
| TemplateResolverFactory.CATEGORY, |
| pluginTypeByName.keySet()); |
| } |
| |
| // Filter matching resolver factories. |
| final Map<String, F> factoryByName = |
| populateFactoryByName(pluginTypeByName, valueClass, contextClass); |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug( |
| "matched {} resolver factories out of {} for value class {} and context class {}: {}", |
| factoryByName.size(), |
| pluginTypeByName.size(), |
| valueClass, |
| contextClass, |
| factoryByName.keySet()); |
| } |
| return factoryByName; |
| |
| } |
| |
| private static <V, C extends TemplateResolverContext<V, C>, F extends TemplateResolverFactory<V, C>> Map<String, F> populateFactoryByName( |
| final Map<String, PluginType<?>> pluginTypeByName, |
| final Class<V> valueClass, |
| final Class<C> contextClass) { |
| final Map<String, F> factoryByName = new LinkedHashMap<>(); |
| final Set<String> pluginNames = pluginTypeByName.keySet(); |
| for (final String pluginName : pluginNames) { |
| final PluginType<?> pluginType = pluginTypeByName.get(pluginName); |
| final Class<?> pluginClass = pluginType.getPluginClass(); |
| final boolean pluginClassMatched = |
| TemplateResolverFactory.class.isAssignableFrom(pluginClass); |
| if (pluginClassMatched) { |
| final TemplateResolverFactory<?, ?> rawFactory = |
| instantiateFactory(pluginName, pluginClass); |
| final F factory = castFactory(valueClass, contextClass, rawFactory); |
| if (factory != null) { |
| addFactory(factoryByName, factory); |
| } |
| } |
| } |
| return factoryByName; |
| } |
| |
| private static TemplateResolverFactory<?, ?> instantiateFactory( |
| final String pluginName, |
| final Class<?> pluginClass) { |
| try { |
| return (TemplateResolverFactory<?, ?>) |
| PluginUtil.instantiatePlugin(pluginClass); |
| } catch (final Exception error) { |
| final String message = String.format( |
| "failed instantiating resolver factory plugin %s of name %s", |
| pluginClass, pluginName); |
| throw new RuntimeException(message, error); |
| } |
| } |
| |
| private static <V, C extends TemplateResolverContext<V, C>, F extends TemplateResolverFactory<V, C>> F castFactory( |
| final Class<V> valueClass, |
| final Class<C> contextClass, |
| final TemplateResolverFactory<?, ?> factory) { |
| final Class<?> factoryValueClass = factory.getValueClass(); |
| final Class<?> factoryContextClass = factory.getContextClass(); |
| final boolean factoryValueClassMatched = |
| valueClass.isAssignableFrom(factoryValueClass); |
| final boolean factoryContextClassMatched = |
| contextClass.isAssignableFrom(factoryContextClass); |
| if (factoryValueClassMatched && factoryContextClassMatched) { |
| @SuppressWarnings("unchecked") |
| final F typedFactory = (F) factory; |
| return typedFactory; |
| } |
| return null; |
| } |
| |
| private static <V, C extends TemplateResolverContext<V, C>, F extends TemplateResolverFactory<V, C>> void addFactory( |
| final Map<String, F> factoryByName, |
| final F factory) { |
| final String factoryName = factory.getName(); |
| final F conflictingFactory = factoryByName.putIfAbsent(factoryName, factory); |
| if (conflictingFactory != null) { |
| final String message = String.format( |
| "found resolver factories with overlapping names: %s (%s and %s)", |
| factoryName, conflictingFactory, factory); |
| throw new IllegalArgumentException(message); |
| } |
| } |
| |
| } |