blob: 31926ea35f37408e25f3be52853010652ece0229 [file] [log] [blame]
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);
}
}
}