You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2022/01/17 01:51:01 UTC
[logging-log4j2] 02/02: Load TypeConverters via BeanManager
This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 35467f6d0ccd4f43c9d44b2d81b73213637c574a
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Sun Jan 16 19:50:29 2022 -0600
Load TypeConverters via BeanManager
- Make BeanManager a service loader class
- Add lazy loading Value<Class<?>> for PluginSource instances
- Refactor TypeConverterRegistry to use BeanManager
- Add ResolverUtil-based classpath scanning option for loading beans
Signed-off-by: Matt Sicker <ma...@apache.org>
---
.../log4j/plugin/processor/BeanProcessor.java | 108 +++++++++----------
.../log4j/plugin/processor/BeanProcessorTest.java | 21 ++--
log4j-plugins/src/main/java/module-info.java | 3 +
.../plugins/convert/TypeConverterRegistry.java | 52 +++++++---
.../log4j/plugins/di/model/DisposesMethod.java | 29 +++---
.../log4j/plugins/di/model/GenericPlugin.java | 20 ++--
.../log4j/plugins/di/model/InjectionTarget.java | 23 ++---
.../log4j/plugins/di/model/PluginModule.java | 12 +--
.../log4j/plugins/di/model/PluginSource.java | 5 +-
.../log4j/plugins/di/model/ProducerField.java | 24 ++---
.../log4j/plugins/di/model/ProducerMethod.java | 33 +++---
.../logging/log4j/plugins/spi/BeanManager.java | 32 +++++-
.../log4j/plugins/spi/InitializationException.java | 4 +
.../log4j/plugins/spi/impl/DefaultBeanManager.java | 114 ++++++++++++++++++---
.../logging/log4j/plugins/util/PluginLoader.java | 60 +++++++++++
.../logging/log4j/plugins/util/PluginUtil.java | 15 +++
.../logging/log4j/plugins/util/TypeUtil.java | 11 +-
...rg.apache.logging.log4j.plugins.spi.BeanManager | 18 ++++
18 files changed, 393 insertions(+), 191 deletions(-)
diff --git a/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/BeanProcessor.java b/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/BeanProcessor.java
index 3ab23ca..74d3c44 100644
--- a/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/BeanProcessor.java
+++ b/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/BeanProcessor.java
@@ -17,12 +17,10 @@
package org.apache.logging.log4j.plugin.processor;
-import org.apache.logging.log4j.plugins.di.DependentScoped;
import org.apache.logging.log4j.plugins.di.Disposes;
import org.apache.logging.log4j.plugins.di.Inject;
import org.apache.logging.log4j.plugins.di.Producer;
import org.apache.logging.log4j.plugins.di.Qualifier;
-import org.apache.logging.log4j.plugins.di.ScopeType;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
@@ -40,6 +38,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.ElementKindVisitor9;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor9;
@@ -56,7 +55,6 @@ import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
@SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
@SupportedOptions({"pluginPackage", "pluginClassName"})
@@ -71,6 +69,9 @@ public class BeanProcessor extends AbstractProcessor {
return SourceVersion.latestSupported();
}
+ /**
+ * Collects fields and methods annotated with {@link Producer}-type annotations.
+ */
private static class ProducerAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
private final List<ProducerMethodMirror> producerMethods = new ArrayList<>();
private final List<ProducerFieldMirror> producerFields = new ArrayList<>();
@@ -88,6 +89,9 @@ public class BeanProcessor extends AbstractProcessor {
}
}
+ /**
+ * Collects {@link Disposes} methods (applied to a method argument).
+ */
private static class DisposesAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
private final List<DisposesMirror> disposesParameters = new ArrayList<>();
@@ -98,6 +102,9 @@ public class BeanProcessor extends AbstractProcessor {
}
}
+ /**
+ * Collects {@link Inject}-annotated constructors, fields, and methods.
+ */
private static class InjectAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
@@ -120,6 +127,10 @@ public class BeanProcessor extends AbstractProcessor {
}
}
+ /**
+ * Collects fields and method parameters annotated with {@link Qualifier}-type annotations that do not also include
+ * {@link Producer}-type annotations.
+ */
private static class QualifiedAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
private final Predicate<AnnotationMirror> isProducerAnnotation;
private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
@@ -148,53 +159,29 @@ public class BeanProcessor extends AbstractProcessor {
}
}
+ /**
+ * Collects generic {@code @Plugin}-annotated classes. Plugins with a single constructor are considered for
+ * {@link Inject} targets.
+ */
private static class PluginAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
- private final List<GenericPluginMirror> plugins = new ArrayList<>();
+ private final List<InjectionTargetMirror> implicitPlugins = new ArrayList<>();
+ private final List<GenericPluginMirror> genericPlugins = new ArrayList<>();
@Override
public Void visitTypeAsClass(final TypeElement e, final Void unused) {
- plugins.add(new GenericPluginMirror(e));
- return null;
- }
- }
-
- private static class ScopeTypeVisitor extends ElementKindVisitor9<TypeElement, Types> {
- protected ScopeTypeVisitor(final TypeElement defaultValue) {
- super(defaultValue);
- }
-
- @Override
- public TypeElement visitType(final TypeElement e, final Types types) {
- for (final AnnotationMirror annotationMirror : e.getAnnotationMirrors()) {
- final DeclaredType annotationType = annotationMirror.getAnnotationType();
- if (annotationType.getAnnotation(ScopeType.class) != null) {
- return (TypeElement) annotationType.asElement();
- }
+ final var constructors = ElementFilter.constructorsIn(e.getEnclosedElements());
+ if (constructors.size() > 1) {
+ genericPlugins.add(new GenericPluginMirror(e));
+ } else {
+ implicitPlugins.add(new InjectionTargetMirror(e));
}
- return super.visitType(e, types);
- }
-
- @Override
- public TypeElement visitVariableAsField(final VariableElement e, final Types types) {
- return Stream.concat(e.getAnnotationMirrors().stream(), e.asType().getAnnotationMirrors().stream())
- .map(AnnotationMirror::getAnnotationType)
- .filter(type -> type.getAnnotation(ScopeType.class) != null)
- .findFirst()
- .map(type -> (TypeElement) type.asElement())
- .orElse(super.DEFAULT_VALUE);
- }
-
- @Override
- public TypeElement visitExecutableAsMethod(final ExecutableElement e, final Types types) {
- return Stream.concat(e.getAnnotationMirrors().stream(), e.getReturnType().getAnnotationMirrors().stream())
- .map(AnnotationMirror::getAnnotationType)
- .filter(type -> type.getAnnotation(ScopeType.class) != null)
- .findFirst()
- .map(type -> (TypeElement) type.asElement())
- .orElse(super.DEFAULT_VALUE);
+ return null;
}
}
+ /**
+ * Annotation scanning mirror of {@link org.apache.logging.log4j.plugins.di.model.PluginSource}.
+ */
interface PluginSourceMirror<E extends Element> {
E getElement();
@@ -364,6 +351,7 @@ public class BeanProcessor extends AbstractProcessor {
mirrors.addAll(producesAnnotationVisitor.producerFields);
mirrors.addAll(injectAnnotationVisitor.injectableClasses);
mirrors.addAll(disposesAnnotationVisitor.disposesParameters);
+ mirrors.addAll(pluginAnnotationVisitor.implicitPlugins);
mirrors.forEach(mirror -> {
declaringTypes.add(mirror.getDeclaringElement());
packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
@@ -377,7 +365,7 @@ public class BeanProcessor extends AbstractProcessor {
packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
});
- pluginAnnotationVisitor.plugins.stream()
+ pluginAnnotationVisitor.genericPlugins.stream()
.filter(mirror -> !declaringTypes.contains(mirror.getDeclaringElement()))
.forEach(mirror -> {
mirrors.add(mirror);
@@ -418,12 +406,14 @@ public class BeanProcessor extends AbstractProcessor {
out.println("import java.util.List;");
out.println("import java.util.Set;");
out.println();
- out.println("public class " + className + " extends PluginModule {");
+ out.println("public class " + className + " implements PluginModule {");
out.println();
- out.println(" private static final List<PluginSource> PLUGINS = List.of(" + javaListOfPlugins(mirrors) + ");");
+ out.println(" private final ClassLoader classLoader = getClass().getClassLoader();");
+ out.println(" private final List<PluginSource> pluginSources = List.of(" + javaListOfPlugins(mirrors) + ");");
out.println();
- out.println(" public " + className + "() {");
- out.println(" super(PLUGINS);");
+ out.println(" @Override");
+ out.println(" public List<PluginSource> getPluginSources() {");
+ out.println(" return pluginSources;");
out.println(" }");
out.println();
out.println("}");
@@ -433,39 +423,35 @@ public class BeanProcessor extends AbstractProcessor {
private String javaListOfPlugins(final List<PluginSourceMirror<?>> mirrors) {
final Elements elements = processingEnv.getElementUtils();
final Types types = processingEnv.getTypeUtils();
- final var scopeTypeVisitor = new ScopeTypeVisitor(elements.getTypeElement(DependentScoped.class.getCanonicalName()));
return mirrors.stream()
.sorted(Comparator.<PluginSourceMirror<?>, String>comparing(m -> m.getClass().getName())
.thenComparing(m -> elements.getBinaryName(m.getDeclaringElement()), CharSequence::compare))
.map(mirror -> {
final String declaringClassName = '"' + elements.getBinaryName(mirror.getDeclaringElement()).toString() + '"';
final String setOfImplementedInterfaces = javaSetOfImplementedInterfaces(mirror.getType());
- final String scopeTypeClassReference = mirror.getElement().accept(scopeTypeVisitor, types).getQualifiedName() + ".class";
if (mirror instanceof ProducerMethodMirror) {
- return "new ProducerMethod(" + declaringClassName + ", \"" +
+ return "new ProducerMethod(classLoader, " + declaringClassName + ", \"" +
mirror.getType().toString() + "\", \"" +
mirror.getElement().getSimpleName() + "\", " +
- setOfImplementedInterfaces + ", " +
- scopeTypeClassReference + ")";
+ setOfImplementedInterfaces + ")";
} else if (mirror instanceof ProducerFieldMirror) {
- return "new ProducerField(" + declaringClassName + ", \"" +
+ return "new ProducerField(classLoader, " + declaringClassName + ", \"" +
mirror.getElement().getSimpleName() + "\", " +
- setOfImplementedInterfaces + ", " +
- scopeTypeClassReference + ")";
+ setOfImplementedInterfaces + ")";
} else if (mirror instanceof InjectionTargetMirror) {
- return "new InjectionTarget(" + declaringClassName + ", " +
- setOfImplementedInterfaces + ", " +
- scopeTypeClassReference + ")";
+ return "new InjectionTarget(classLoader, " + declaringClassName + ", " +
+ setOfImplementedInterfaces + ")";
} else if (mirror instanceof DisposesMirror) {
- return "new DisposesMethod(" + declaringClassName + ", \"" +
+ return "new DisposesMethod(classLoader, " + declaringClassName + ", \"" +
elements.getBinaryName((TypeElement) types.asElement(mirror.getElement().asType())) + "\")";
} else if (mirror instanceof GenericPluginMirror) {
- return "new GenericPlugin(" + declaringClassName + ", " + setOfImplementedInterfaces + ")";
+ return "new GenericPlugin(classLoader, " + declaringClassName + ", " +
+ setOfImplementedInterfaces + ")";
} else {
throw new UnsupportedOperationException(mirror.getClass().getName());
}
})
- .collect(Collectors.joining(",\n", "\n", "\n"));
+ .collect(Collectors.joining(",\n ", "\n ", "\n "));
}
private String javaSetOfImplementedInterfaces(final TypeMirror base) {
diff --git a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/BeanProcessorTest.java b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/BeanProcessorTest.java
index 624c803..c448467 100644
--- a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/BeanProcessorTest.java
+++ b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugin/processor/BeanProcessorTest.java
@@ -18,11 +18,14 @@
package org.apache.logging.log4j.plugin.processor;
import org.apache.logging.log4j.plugins.di.model.DisposesMethod;
-import org.apache.logging.log4j.plugins.di.model.GenericPlugin;
import org.apache.logging.log4j.plugins.di.model.InjectionTarget;
import org.apache.logging.log4j.plugins.di.model.PluginModule;
import org.apache.logging.log4j.plugins.di.model.ProducerField;
+import org.apache.logging.log4j.plugins.test.validation.ExampleBean;
import org.apache.logging.log4j.plugins.test.validation.FakePlugin;
+import org.apache.logging.log4j.plugins.test.validation.ImplicitBean;
+import org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean;
+import org.apache.logging.log4j.plugins.test.validation.ProductionBean;
import org.apache.logging.log4j.plugins.test.validation.plugins.Log4jModule;
import org.junit.jupiter.api.Test;
@@ -34,18 +37,18 @@ class BeanProcessorTest {
final PluginModule module = new Log4jModule();
final var plugins = module.getPluginSources();
assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
- plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ExampleBean")));
+ plugin.getDeclaringClass().equals(ExampleBean.class)));
assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
- plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ImplicitBean")));
+ plugin.getDeclaringClass().equals(ImplicitBean.class)));
assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
- plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean")));
+ plugin.getDeclaringClass().equals(ImplicitMethodBean.class)));
assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
- plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder")));
+ plugin.getDeclaringClass().equals(ProductionBean.Builder.class)));
assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof ProducerField &&
- plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean")));
+ plugin.getDeclaringClass().equals(ProductionBean.class)));
assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof DisposesMethod &&
- plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean")));
- assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof GenericPlugin &&
- plugin.getDeclaringClassName().equals(FakePlugin.class.getName())));
+ plugin.getDeclaringClass().equals(ProductionBean.class)));
+ assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+ plugin.getDeclaringClass().equals(FakePlugin.class)));
}
}
diff --git a/log4j-plugins/src/main/java/module-info.java b/log4j-plugins/src/main/java/module-info.java
index 6e5d976..62fe441 100644
--- a/log4j-plugins/src/main/java/module-info.java
+++ b/log4j-plugins/src/main/java/module-info.java
@@ -34,5 +34,8 @@ module org.apache.logging.log4j.plugins {
uses org.apache.logging.log4j.plugins.processor.PluginService;
uses org.apache.logging.log4j.plugins.di.model.PluginModule;
+ uses org.apache.logging.log4j.plugins.spi.BeanManager;
+ provides org.apache.logging.log4j.plugins.spi.BeanManager with
+ org.apache.logging.log4j.plugins.spi.impl.DefaultBeanManager;
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistry.java
index 9bb2eb6..f3301ce 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterRegistry.java
@@ -17,20 +17,24 @@
package org.apache.logging.log4j.plugins.convert;
import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.plugins.util.PluginManager;
-import org.apache.logging.log4j.plugins.util.PluginType;
+import org.apache.logging.log4j.plugins.di.model.PluginSource;
+import org.apache.logging.log4j.plugins.spi.Bean;
+import org.apache.logging.log4j.plugins.spi.BeanManager;
+import org.apache.logging.log4j.plugins.util.PluginLoader;
import org.apache.logging.log4j.plugins.util.TypeUtil;
-import org.apache.logging.log4j.util.ReflectionUtil;
import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.UnknownFormatConversionException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
/**
* Registry for {@link TypeConverter} plugins.
@@ -40,6 +44,7 @@ import java.util.concurrent.ConcurrentMap;
public class TypeConverterRegistry {
private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String DEFAULT_CONVERTERS_PACKAGE = "org.apache.logging.log4j.core.config.plugins.convert";
private static volatile TypeConverterRegistry INSTANCE;
private static final Object INSTANCE_LOCK = new Object();
@@ -107,22 +112,37 @@ public class TypeConverterRegistry {
private TypeConverterRegistry() {
LOGGER.trace("TypeConverterRegistry initializing.");
- final PluginManager manager = new PluginManager(TypeConverters.CATEGORY);
- manager.collectPlugins();
- loadKnownTypeConverters(manager.getPlugins().values());
+ // TODO: this should eventually be refactored to load TypeConverter instances on demand via BeanManager
+ // (can be done after AbstractConfigurationBinder is removed; can then combine TypeConverters with rest of beans)
+ loadKnownTypeConverters();
registerPrimitiveTypes();
}
- private void loadKnownTypeConverters(final Collection<PluginType<?>> knownTypes) {
- for (final PluginType<?> knownType : knownTypes) {
- final Class<?> clazz = knownType.getPluginClass();
- if (TypeConverter.class.isAssignableFrom(clazz)) {
- @SuppressWarnings("rawtypes")
- final Class<? extends TypeConverter> pluginClass = clazz.asSubclass(TypeConverter.class);
- final Type conversionType = getTypeConverterSupportedType(pluginClass);
- final TypeConverter<?> converter = ReflectionUtil.instantiate(pluginClass);
- registerConverter(conversionType, converter);
- }
+ private void loadKnownTypeConverters() {
+ final BeanManager beanManager = BeanManager.getInstance();
+ final Set<PluginSource> typeConverterPlugins = PluginLoader.loadPluginSourcesFromMainClassLoader()
+ .stream()
+ .filter(pluginSource -> pluginSource.getImplementedInterfaces().contains(TypeConverter.class))
+ .collect(Collectors.toSet());
+ final Collection<Bean<?>> typeConverterBeans;
+ if (typeConverterPlugins.isEmpty()) {
+ // retry with a classpath scan
+ LOGGER.warn("Unable to load PluginModule metadata; falling back to scan of classpath");
+ typeConverterBeans = beanManager.scanAndLoadBeans(LoaderUtil.getClassLoader(), DEFAULT_CONVERTERS_PACKAGE)
+ .stream()
+ .filter(bean -> bean.hasMatchingType(TypeConverter.class))
+ .collect(Collectors.toSet());
+ } else {
+ typeConverterBeans = beanManager.loadBeansFromPluginSources(typeConverterPlugins);
+ }
+ beanManager.validateBeans(typeConverterBeans);
+ final var initializationContext = beanManager.createInitializationContext(null);
+ for (final Bean<?> typeConverterBean : typeConverterBeans) {
+ final Bean<TypeConverter<?>> bean = TypeUtil.cast(typeConverterBean);
+ final Class<? extends TypeConverter<?>> pluginClass = TypeUtil.cast(bean.getDeclaringClass());
+ final Type conversionType = getTypeConverterSupportedType(pluginClass);
+ final TypeConverter<?> converter = beanManager.getValue(bean, initializationContext);
+ registerConverter(conversionType, converter);
}
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java
index 02175cb..2dbfca1 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java
@@ -17,37 +17,32 @@
package org.apache.logging.log4j.plugins.di.model;
-import org.apache.logging.log4j.plugins.di.Disposes;
+import org.apache.logging.log4j.plugins.util.PluginUtil;
+import org.apache.logging.log4j.plugins.util.Value;
-import java.lang.annotation.Annotation;
import java.util.Set;
public class DisposesMethod implements PluginSource {
- private final String declaringClassName;
- private final String disposesTypeName;
+ private final Value<Class<?>> disposesType;
+ private final Value<Class<?>> declaringClass;
- public DisposesMethod(final String declaringClassName, final String disposesTypeName) {
- this.declaringClassName = declaringClassName;
- this.disposesTypeName = disposesTypeName;
+ public DisposesMethod(
+ final ClassLoader classLoader, final String declaringClassName, final String disposesTypeName) {
+ disposesType = PluginUtil.lazyLoadClass(classLoader, disposesTypeName);
+ declaringClass = PluginUtil.lazyLoadClass(classLoader, declaringClassName);
}
@Override
- public String getDeclaringClassName() {
- return declaringClassName;
+ public Class<?> getDeclaringClass() {
+ return declaringClass.get();
}
- public String getDisposesTypeName() {
- return disposesTypeName;
+ public Class<?> getDisposesType() {
+ return disposesType.get();
}
@Override
public Set<Class<?>> getImplementedInterfaces() {
return Set.of();
}
-
- @Override
- public Class<? extends Annotation> getScopeType() {
- // not a real scope
- return Disposes.class;
- }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java
index 3bff77c..27b95a0 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java
@@ -17,32 +17,28 @@
package org.apache.logging.log4j.plugins.di.model;
-import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.util.PluginUtil;
+import org.apache.logging.log4j.plugins.util.Value;
-import java.lang.annotation.Annotation;
import java.util.Set;
public class GenericPlugin implements PluginSource {
- private final String declaringClassName;
+ private final Value<Class<?>> declaringClass;
private final Set<Class<?>> implementedInterfaces;
- public GenericPlugin(final String declaringClassName, final Set<Class<?>> implementedInterfaces) {
- this.declaringClassName = declaringClassName;
+ public GenericPlugin(
+ final ClassLoader classLoader, final String declaringClassName, final Set<Class<?>> implementedInterfaces) {
+ declaringClass = PluginUtil.lazyLoadClass(classLoader, declaringClassName);
this.implementedInterfaces = implementedInterfaces;
}
@Override
- public String getDeclaringClassName() {
- return declaringClassName;
+ public Class<?> getDeclaringClass() {
+ return declaringClass.get();
}
@Override
public Set<Class<?>> getImplementedInterfaces() {
return implementedInterfaces;
}
-
- @Override
- public Class<? extends Annotation> getScopeType() {
- return Plugin.class;
- }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java
index a6676e5..20b76ba 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java
@@ -17,33 +17,28 @@
package org.apache.logging.log4j.plugins.di.model;
-import java.lang.annotation.Annotation;
+import org.apache.logging.log4j.plugins.util.PluginUtil;
+import org.apache.logging.log4j.plugins.util.Value;
+
import java.util.Set;
public class InjectionTarget implements PluginSource {
- private final String className;
+ private final Value<Class<?>> declaringClass;
private final Set<Class<?>> implementedInterfaces;
- private final Class<? extends Annotation> scopeType;
- public InjectionTarget(final String className, final Set<Class<?>> implementedInterfaces,
- final Class<? extends Annotation> scopeType) {
- this.className = className;
+ public InjectionTarget(
+ final ClassLoader classLoader, final String className, final Set<Class<?>> implementedInterfaces) {
+ declaringClass = PluginUtil.lazyLoadClass(classLoader, className);
this.implementedInterfaces = implementedInterfaces;
- this.scopeType = scopeType;
}
@Override
- public String getDeclaringClassName() {
- return className;
+ public Class<?> getDeclaringClass() {
+ return declaringClass.get();
}
@Override
public Set<Class<?>> getImplementedInterfaces() {
return implementedInterfaces;
}
-
- @Override
- public Class<? extends Annotation> getScopeType() {
- return scopeType;
- }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java
index 70b7093..acbce13 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java
@@ -19,14 +19,6 @@ package org.apache.logging.log4j.plugins.di.model;
import java.util.List;
-public abstract class PluginModule {
- private final List<PluginSource> pluginSources;
-
- protected PluginModule(final List<PluginSource> pluginSources) {
- this.pluginSources = pluginSources;
- }
-
- public List<PluginSource> getPluginSources() {
- return pluginSources;
- }
+public interface PluginModule {
+ List<PluginSource> getPluginSources();
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java
index c144f63..dd7325e 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java
@@ -17,13 +17,10 @@
package org.apache.logging.log4j.plugins.di.model;
-import java.lang.annotation.Annotation;
import java.util.Set;
public interface PluginSource {
- String getDeclaringClassName();
+ Class<?> getDeclaringClass();
Set<Class<?>> getImplementedInterfaces();
-
- Class<? extends Annotation> getScopeType();
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java
index acfc542..58f3d22 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java
@@ -17,26 +17,27 @@
package org.apache.logging.log4j.plugins.di.model;
-import java.lang.annotation.Annotation;
+import org.apache.logging.log4j.plugins.util.PluginUtil;
+import org.apache.logging.log4j.plugins.util.Value;
+
import java.util.Set;
public class ProducerField implements PluginSource {
- private final String declaringClassName;
+ private final Value<Class<?>> declaringClass;
private final String fieldName;
private final Set<Class<?>> implementedInterfaces;
- private final Class<? extends Annotation> scopeType;
- public ProducerField(final String declaringClassName, final String fieldName,
- final Set<Class<?>> implementedInterfaces, final Class<? extends Annotation> scopeType) {
- this.declaringClassName = declaringClassName;
+ public ProducerField(
+ final ClassLoader classLoader, final String declaringClassName, final String fieldName,
+ final Set<Class<?>> implementedInterfaces) {
+ declaringClass = PluginUtil.lazyLoadClass(classLoader, declaringClassName);
this.fieldName = fieldName;
this.implementedInterfaces = implementedInterfaces;
- this.scopeType = scopeType;
}
@Override
- public String getDeclaringClassName() {
- return declaringClassName;
+ public Class<?> getDeclaringClass() {
+ return declaringClass.get();
}
public String getFieldName() {
@@ -47,9 +48,4 @@ public class ProducerField implements PluginSource {
public Set<Class<?>> getImplementedInterfaces() {
return implementedInterfaces;
}
-
- @Override
- public Class<? extends Annotation> getScopeType() {
- return scopeType;
- }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
index 5c836b4..5d253f5 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
@@ -17,33 +17,33 @@
package org.apache.logging.log4j.plugins.di.model;
-import java.lang.annotation.Annotation;
+import org.apache.logging.log4j.plugins.util.PluginUtil;
+import org.apache.logging.log4j.plugins.util.Value;
+
import java.util.Set;
public class ProducerMethod implements PluginSource {
- private final String declaringClassName;
- private final String returnTypeClassName;
+ private final Value<Class<?>> declaringClass;
+ private final Value<Class<?>> returnType;
private final String methodName;
private final Set<Class<?>> implementedInterfaces;
- private final Class<? extends Annotation> scopeType;
- public ProducerMethod(final String declaringClassName, final String returnTypeClassName,
- final String methodName, final Set<Class<?>> implementedInterfaces,
- final Class<? extends Annotation> scopeType) {
- this.declaringClassName = declaringClassName;
- this.returnTypeClassName = returnTypeClassName;
+ public ProducerMethod(
+ final ClassLoader classLoader, final String declaringClassName, final String returnTypeClassName,
+ final String methodName, final Set<Class<?>> implementedInterfaces) {
+ declaringClass = PluginUtil.lazyLoadClass(classLoader, declaringClassName);
+ returnType = PluginUtil.lazyLoadClass(classLoader, returnTypeClassName);
this.methodName = methodName;
this.implementedInterfaces = implementedInterfaces;
- this.scopeType = scopeType;
}
@Override
- public String getDeclaringClassName() {
- return declaringClassName;
+ public Class<?> getDeclaringClass() {
+ return declaringClass.get();
}
- public String getReturnTypeClassName() {
- return returnTypeClassName;
+ public Class<?> getReturnType() {
+ return returnType.get();
}
public String getMethodName() {
@@ -54,9 +54,4 @@ public class ProducerMethod implements PluginSource {
public Set<Class<?>> getImplementedInterfaces() {
return implementedInterfaces;
}
-
- @Override
- public Class<? extends Annotation> getScopeType() {
- return scopeType;
- }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/BeanManager.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/BeanManager.java
index 05b8f13..6945403 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/BeanManager.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/BeanManager.java
@@ -20,6 +20,7 @@ package org.apache.logging.log4j.plugins.spi;
import org.apache.logging.log4j.plugins.di.Inject;
import org.apache.logging.log4j.plugins.di.Producer;
import org.apache.logging.log4j.plugins.di.Qualifier;
+import org.apache.logging.log4j.plugins.di.model.PluginSource;
import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
import org.apache.logging.log4j.plugins.util.AnnotationUtil;
import org.apache.logging.log4j.util.Strings;
@@ -35,14 +36,23 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
+import java.util.ServiceLoader;
/**
* Central SPI for injecting and managing beans and their instances.
*/
-// TODO: move this whole API to log4j-plugins
public interface BeanManager extends AutoCloseable {
/**
+ * Gets the configured BeanManager service for loading and unloading beans.
+ */
+ static BeanManager getInstance() {
+ return ServiceLoader.load(BeanManager.class, BeanManager.class.getClassLoader())
+ .findFirst()
+ .orElseThrow(() -> new InitializationException("No BeanManager service available"));
+ }
+
+ /**
* Loads beans from the given classes. This looks for injectable classes and producers in the provided classes,
* loads them into this manager, and returns the loaded beans.
*
@@ -52,6 +62,24 @@ public interface BeanManager extends AutoCloseable {
Collection<Bean<?>> loadBeans(final Collection<Class<?>> beanClasses);
/**
+ * Scans the classpath in a given package for beans and loads bean metadata from discovered classes.
+ *
+ * @param classLoader ClassLoader to use for loading classes from package or null to use the default context ClassLoader
+ * @param packageName package name to begin search for beans inside
+ * @return beans loaded from the given package
+ */
+ Collection<Bean<?>> scanAndLoadBeans(final ClassLoader classLoader, final String packageName);
+
+ /**
+ * Loads beans from the given {@link PluginSource} instances. PluginSources are generated by the plugins annotation
+ * processor to help avoid classpath scanning at runtime.
+ *
+ * @param pluginSources collection of PluginSource instances to load bean metadata from
+ * @return beans loaded from the given sources
+ */
+ Collection<Bean<?>> loadBeansFromPluginSources(final Collection<PluginSource> pluginSources);
+
+ /**
* Creates a bean for an injectable bean class.
*/
<T> Bean<T> createBean(final Class<T> beanClass);
@@ -255,6 +283,8 @@ public interface BeanManager extends AutoCloseable {
@Override
void close();
+ // TODO: API to register ScopeContexts
+ // TODO: Configuration APIs
// TODO: integrate with constraint validators
// TODO: integrate with TypeConverters
// TODO: need some sort of default value strategy to bridge over @PluginAttribute and optional injected values
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/InitializationException.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/InitializationException.java
index d547f8d..8c11613 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/InitializationException.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/InitializationException.java
@@ -18,6 +18,10 @@
package org.apache.logging.log4j.plugins.spi;
public class InitializationException extends InjectionException {
+ public InitializationException(final String message) {
+ super(message);
+ }
+
public InitializationException(final String message, final Throwable cause) {
super(message, cause);
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/impl/DefaultBeanManager.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/impl/DefaultBeanManager.java
index 78b9439..90b3e0d 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/impl/DefaultBeanManager.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/impl/DefaultBeanManager.java
@@ -17,6 +17,15 @@
package org.apache.logging.log4j.plugins.spi.impl;
+import org.apache.logging.log4j.plugins.di.DependentScoped;
+import org.apache.logging.log4j.plugins.di.Disposes;
+import org.apache.logging.log4j.plugins.di.Producer;
+import org.apache.logging.log4j.plugins.di.Provider;
+import org.apache.logging.log4j.plugins.di.ScopeType;
+import org.apache.logging.log4j.plugins.di.SingletonScoped;
+import org.apache.logging.log4j.plugins.di.model.PluginSource;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementAliasesProvider;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
import org.apache.logging.log4j.plugins.spi.AmbiguousBeanException;
import org.apache.logging.log4j.plugins.spi.Bean;
import org.apache.logging.log4j.plugins.spi.BeanManager;
@@ -30,17 +39,11 @@ import org.apache.logging.log4j.plugins.spi.ResolutionException;
import org.apache.logging.log4j.plugins.spi.ScopeContext;
import org.apache.logging.log4j.plugins.spi.UnsatisfiedBeanException;
import org.apache.logging.log4j.plugins.spi.ValidationException;
-import org.apache.logging.log4j.plugins.di.DependentScoped;
-import org.apache.logging.log4j.plugins.di.Disposes;
-import org.apache.logging.log4j.plugins.di.Producer;
-import org.apache.logging.log4j.plugins.di.Provider;
-import org.apache.logging.log4j.plugins.di.ScopeType;
-import org.apache.logging.log4j.plugins.di.SingletonScoped;
-import org.apache.logging.log4j.plugins.name.AnnotatedElementAliasesProvider;
-import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
import org.apache.logging.log4j.plugins.util.AnnotationUtil;
import org.apache.logging.log4j.plugins.util.LazyValue;
+import org.apache.logging.log4j.plugins.util.ResolverUtil;
import org.apache.logging.log4j.plugins.util.TypeUtil;
+import org.apache.logging.log4j.util.Strings;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
@@ -51,18 +54,20 @@ import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
+import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -70,20 +75,25 @@ public class DefaultBeanManager implements BeanManager {
private final Injector injector = new Injector(this);
- private final Collection<Bean<?>> enabledBeans = new HashSet<>();
+ private final Collection<Bean<?>> enabledBeans = ConcurrentHashMap.newKeySet();
private final Map<Type, Collection<Bean<?>>> beansByType = new ConcurrentHashMap<>();
private final Collection<DisposesMethod> disposesMethods = new ArrayList<>();
private final Map<Class<? extends Annotation>, ScopeContext> scopes = new LinkedHashMap<>();
public DefaultBeanManager() {
// TODO: need a better way to register scope contexts
+ // TODO: need ThreadLocalScopeContext for LoggerContext (~ContextSelector) and ConfigurationContext scopes
+ // (can potentially modify LoggerContext[Factory] to set these thread local values on construction et al.
scopes.put(DependentScoped.class, new DependentScopeContext());
scopes.put(SingletonScoped.class, new DefaultScopeContext(SingletonScoped.class));
}
@Override
public Collection<Bean<?>> loadBeans(final Collection<Class<?>> beanClasses) {
- final Collection<Bean<?>> loadedBeans = new HashSet<>();
+ if (beanClasses.isEmpty()) {
+ return Set.of();
+ }
+ final Collection<Bean<?>> loadedBeans = new LinkedHashSet<>();
for (final Class<?> beanClass : beanClasses) {
final Bean<?> bean = isInjectable(beanClass) ? createBean(beanClass) : null;
loadDisposerMethods(beanClass, bean);
@@ -111,6 +121,32 @@ public class DefaultBeanManager implements BeanManager {
}
@Override
+ public Collection<Bean<?>> scanAndLoadBeans(final ClassLoader classLoader, final String packageName) {
+ if (Strings.isBlank(packageName)) {
+ return Set.of();
+ }
+ final ResolverUtil resolver = new ResolverUtil();
+ if (classLoader != null) {
+ resolver.setClassLoader(classLoader);
+ }
+ resolver.findInPackage(new BeanTest(this::isInjectable), packageName);
+ return loadBeans(resolver.getClasses());
+ }
+
+ @Override
+ public Collection<Bean<?>> loadBeansFromPluginSources(final Collection<PluginSource> pluginSources) {
+ if (pluginSources.isEmpty()) {
+ return Set.of();
+ }
+ // TODO: enhance PluginSource metadata to support lazy loading of classes in respective beans for implemented interfaces
+ final Set<Class<?>> beanClasses = pluginSources.stream()
+ .map(PluginSource::getDeclaringClass)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ return loadBeans(beanClasses);
+ }
+
+ @Override
public <T> Bean<T> createBean(final Class<T> beanClass) {
final Collection<Type> types = TypeUtil.getTypeClosure(beanClass);
final String name = AnnotatedElementNameProvider.getName(beanClass);
@@ -172,12 +208,11 @@ public class DefaultBeanManager implements BeanManager {
}
private void loadDisposerMethods(final Class<?> beanClass, final Bean<?> bean) {
- for (final Method method : beanClass.getDeclaredMethods()) {
+ for (final Method method : beanClass.getMethods()) {
for (final Parameter parameter : method.getParameters()) {
if (parameter.isAnnotationPresent(Disposes.class)) {
final String name = AnnotatedElementNameProvider.getName(parameter);
final Collection<String> aliases = AnnotatedElementAliasesProvider.getAliases(parameter);
- method.setAccessible(true);
disposesMethods.add(new DisposesMethod(parameter.getParameterizedType(), name, aliases, bean, method));
}
}
@@ -423,6 +458,43 @@ public class DefaultBeanManager implements BeanManager {
}
}
+ private static class BeanTest implements ResolverUtil.Test {
+ private final Predicate<Class<?>> isBeanClass;
+
+ private BeanTest(final Predicate<Class<?>> isBeanClass) {
+ this.isBeanClass = isBeanClass;
+ }
+
+ @Override
+ public boolean matches(final Class<?> type) {
+ if (isBeanClass.test(type)) {
+ return true;
+ }
+ for (final Annotation annotation : type.getAnnotations()) {
+ final String name = annotation.annotationType().getName();
+ if (name.startsWith("org.apache.logging.log4j.") && name.endsWith(".plugins.Plugin")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean matches(final URI resource) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean doesMatchClass() {
+ return true;
+ }
+
+ @Override
+ public boolean doesMatchResource() {
+ return false;
+ }
+ }
+
private static class DisposesMethod {
private final Type type;
private final String name;
@@ -455,6 +527,22 @@ public class DefaultBeanManager implements BeanManager {
private boolean matchesName(final String name) {
return this.name.equalsIgnoreCase(name) || aliases.stream().anyMatch(name::equalsIgnoreCase);
}
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ final DisposesMethod that = (DisposesMethod) o;
+ return type.equals(that.type) && name.equals(that.name) && aliases.equals(that.aliases) && Objects.equals(
+ declaringBean, that.declaringBean) && disposesMethod.equals(that.disposesMethod);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, name, aliases, declaringBean, disposesMethod);
+ }
}
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginLoader.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginLoader.java
new file mode 100644
index 0000000..3815c26
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginLoader.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.util;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.di.model.PluginModule;
+import org.apache.logging.log4j.plugins.di.model.PluginSource;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ServiceLoader;
+
+public class PluginLoader {
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public static List<PluginSource> loadPluginSourcesFromMainClassLoader() {
+ final List<PluginSource> sources = new ArrayList<>();
+ for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
+ sources.addAll(loadPluginSources(classLoader));
+ }
+ return sources;
+ }
+
+ public static List<PluginSource> loadPluginSources(final ClassLoader classLoader) {
+ final long startTime = System.nanoTime();
+ final ServiceLoader<PluginModule> serviceLoader = ServiceLoader.load(PluginModule.class, classLoader);
+ final List<PluginSource> sources = new ArrayList<>();
+ for (final PluginModule module : serviceLoader) {
+ sources.addAll(module.getPluginSources());
+ }
+ final int numPlugins = sources.size();
+ LOGGER.debug(() -> {
+ final long endTime = System.nanoTime();
+ final DecimalFormat numFormat = new DecimalFormat("#0.000000");
+ return "Took " + numFormat.format((endTime - startTime) * 1e-9) +
+ " seconds to load " + numPlugins +
+ " plugins from " + classLoader;
+ });
+ return sources;
+ }
+
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginUtil.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginUtil.java
index feb7deb..1bcb952 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginUtil.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginUtil.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.plugins.util;
import org.apache.logging.log4j.plugins.PluginFactory;
+import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -93,4 +94,18 @@ public final class PluginUtil {
throw new IllegalStateException("no factory method found for class " + pluginClass);
}
+ public static Value<Class<?>> lazyLoadClass(final ClassLoader classLoader, final String className) {
+ final var classLoaderRef = new WeakReference<>(classLoader);
+ return WeakLazyValue.forSupplier(() -> {
+ final ClassLoader loader = classLoaderRef.get();
+ if (loader != null) {
+ try {
+ return loader.loadClass(className);
+ } catch (ClassNotFoundException ignored) {
+ }
+ }
+ return null;
+ });
+ }
+
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
index adde765..685f882 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
@@ -16,7 +16,6 @@
*/
package org.apache.logging.log4j.plugins.util;
-import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
@@ -29,9 +28,11 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* Utility class for working with Java {@link Type}s and derivatives. This class is adapted heavily from the
@@ -535,4 +536,12 @@ public final class TypeUtil {
return new ParameterizedTypeImpl(null, rawType, typeArguments);
}
+ public static Set<Class<?>> getImplementedInterfaces(final Class<?> type) {
+ final Set<Class<?>> interfaces = new LinkedHashSet<>(List.of(type.getInterfaces()));
+ for (Class<?> superclass = type.getSuperclass(); superclass != null; superclass = superclass.getSuperclass()) {
+ interfaces.addAll(List.of(superclass.getInterfaces()));
+ }
+ return interfaces;
+ }
+
}
diff --git a/log4j-plugins/src/main/resources/META-INF/services/org.apache.logging.log4j.plugins.spi.BeanManager b/log4j-plugins/src/main/resources/META-INF/services/org.apache.logging.log4j.plugins.spi.BeanManager
new file mode 100644
index 0000000..98c32ea
--- /dev/null
+++ b/log4j-plugins/src/main/resources/META-INF/services/org.apache.logging.log4j.plugins.spi.BeanManager
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache license, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the license for the specific language governing permissions and
+# limitations under the license.
+#
+
+org.apache.logging.log4j.plugins.spi.impl.DefaultBeanManager